from __future__ import annotations

import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch

import numpy as np

from omr_service.form_routing import (
    QR_NOT_READABLE,
    UNKNOWN_FORM_ID,
    identify_form,
)


class FormRoutingTests(unittest.TestCase):
    def make_root(self, form_id: str = "BF000001") -> tuple[tempfile.TemporaryDirectory, Path]:
        temp = tempfile.TemporaryDirectory()
        root = Path(temp.name)
        (root / "profiles").mkdir()
        (root / "profiles" / "form_registry.json").write_text(
            json.dumps(
                {
                    form_id: {
                        "name": "Test form",
                        "template": "template.json",
                        "config": "config.json",
                        "marker": "marker.jpg",
                    }
                }
            )
        )
        return temp, root

    @patch("omr_service.form_routing.cv2.imread")
    @patch("omr_service.form_routing._decode_qr")
    def test_fast_path_routes_registered_bf_id(self, decode, imread):
        temp, root = self.make_root()
        self.addCleanup(temp.cleanup)
        imread.return_value = np.zeros((100, 200), dtype=np.uint8)
        decode.return_value = ("BF000001", None)

        route, issue = identify_form(Path("page.png"), root)

        self.assertIsNone(issue)
        self.assertEqual("BF000001", route.form_id)
        self.assertEqual(0, route.rotation_clockwise)
        self.assertEqual("Test form", route.profile_name)

    @patch("omr_service.form_routing.cv2.imread")
    @patch("omr_service.form_routing._decode_qr")
    def test_invalid_qr_is_rejected(self, decode, imread):
        temp, root = self.make_root()
        self.addCleanup(temp.cleanup)
        imread.return_value = np.zeros((100, 200), dtype=np.uint8)
        decode.side_effect = [("", None), ("MJ-RC5", None)]

        route, issue = identify_form(Path("page.png"), root)

        self.assertIsNone(route)
        self.assertEqual(QR_NOT_READABLE, issue.code)

    @patch("omr_service.form_routing.cv2.imread")
    @patch("omr_service.form_routing._decode_qr")
    def test_unknown_valid_form_id_is_not_guessed(self, decode, imread):
        temp, root = self.make_root()
        self.addCleanup(temp.cleanup)
        imread.return_value = np.zeros((100, 200), dtype=np.uint8)
        decode.return_value = ("BF999999", None)

        route, issue = identify_form(Path("page.png"), root)

        self.assertIsNone(route)
        self.assertEqual(UNKNOWN_FORM_ID, issue.code)

    @patch("omr_service.form_routing.rotate_image_file")
    @patch("omr_service.form_routing.cv2.imread")
    @patch("omr_service.form_routing._decode_qr")
    def test_full_page_qr_at_right_normalizes_orientation(self, decode, imread, rotate):
        temp, root = self.make_root()
        self.addCleanup(temp.cleanup)
        image = np.zeros((100, 200), dtype=np.uint8)
        imread.return_value = image
        points = np.array([[[160, 40], [180, 40], [180, 60], [160, 60]]], dtype=np.float32)
        decode.side_effect = [("", None), ("BF000001", points)]

        route, issue = identify_form(Path("page.png"), root)

        self.assertIsNone(issue)
        self.assertEqual(270, route.rotation_clockwise)
        rotate.assert_called_once_with(Path("page.png"), 270)


if __name__ == "__main__":
    unittest.main()
