"""
Tests for Admin Annotation Routes.
"""

import pytest
from datetime import datetime
from unittest.mock import MagicMock, patch
from uuid import UUID

from fastapi import HTTPException

from backend.data.admin_models import AdminAnnotation, AdminDocument
from shared.fields import FIELD_CLASSES
from backend.web.api.v1.admin.annotations import _validate_uuid, create_annotation_router
from backend.web.schemas.admin import (
    AnnotationCreate,
    AnnotationUpdate,
    AutoLabelRequest,
    BoundingBox,
)


# Test UUIDs
TEST_DOC_UUID = "550e8400-e29b-41d4-a716-446655440000"
TEST_ANN_UUID = "660e8400-e29b-41d4-a716-446655440001"
TEST_TOKEN = "test-admin-token-12345"


class TestAnnotationRouterCreation:
    """Tests for annotation router creation."""

    def test_creates_router_with_endpoints(self):
        """Test router is created with expected endpoints."""
        router = create_annotation_router()

        # Get route paths (includes prefix)
        paths = [route.path for route in router.routes]

        # Paths include the /admin/documents prefix
        assert any("{document_id}/annotations" in p for p in paths)
        assert any("{annotation_id}" in p for p in paths)
        assert any("auto-label" in p for p in paths)
        assert any("images" in p for p in paths)


class TestAnnotationCreateSchema:
    """Tests for AnnotationCreate schema."""

    def test_valid_annotation(self):
        """Test valid annotation creation."""
        ann = AnnotationCreate(
            page_number=1,
            class_id=0,
            bbox=BoundingBox(x=100, y=100, width=200, height=50),
            text_value="12345",
        )

        assert ann.page_number == 1
        assert ann.class_id == 0
        assert ann.bbox.x == 100
        assert ann.text_value == "12345"

    def test_class_id_range(self):
        """Test class_id must be 0-9."""
        # Valid class IDs
        for class_id in range(10):
            ann = AnnotationCreate(
                page_number=1,
                class_id=class_id,
                bbox=BoundingBox(x=0, y=0, width=100, height=50),
            )
            assert ann.class_id == class_id

    def test_bbox_validation(self):
        """Test bounding box validation."""
        bbox = BoundingBox(x=0, y=0, width=100, height=50)
        assert bbox.width >= 1
        assert bbox.height >= 1


class TestAnnotationUpdateSchema:
    """Tests for AnnotationUpdate schema."""

    def test_partial_update(self):
        """Test partial update with only some fields."""
        update = AnnotationUpdate(
            text_value="new value",
        )

        assert update.text_value == "new value"
        assert update.class_id is None
        assert update.bbox is None

    def test_bbox_update(self):
        """Test bounding box update."""
        update = AnnotationUpdate(
            bbox=BoundingBox(x=50, y=50, width=150, height=75),
        )

        assert update.bbox.x == 50
        assert update.bbox.width == 150


class TestAutoLabelRequestSchema:
    """Tests for AutoLabelRequest schema."""

    def test_valid_request(self):
        """Test valid auto-label request."""
        request = AutoLabelRequest(
            field_values={
                "InvoiceNumber": "12345",
                "Amount": "1000.00",
            },
            replace_existing=True,
        )

        assert len(request.field_values) == 2
        assert request.field_values["InvoiceNumber"] == "12345"
        assert request.replace_existing is True

    def test_requires_field_values(self):
        """Test that field_values is required."""
        with pytest.raises(Exception):
            AutoLabelRequest(replace_existing=True)


class TestFieldClasses:
    """Tests for field class mapping."""

    def test_all_classes_defined(self):
        """Test all 10 field classes are defined."""
        assert len(FIELD_CLASSES) == 10

    def test_class_ids_sequential(self):
        """Test class IDs are 0-9."""
        assert set(FIELD_CLASSES.keys()) == set(range(10))

    def test_known_field_names(self):
        """Test known field names are present."""
        names = list(FIELD_CLASSES.values())

        assert "invoice_number" in names
        assert "invoice_date" in names
        assert "amount" in names
        assert "bankgiro" in names
        assert "ocr_number" in names


class TestAnnotationModel:
    """Tests for AdminAnnotation model."""

    def test_annotation_creation(self):
        """Test annotation model creation."""
        ann = AdminAnnotation(
            document_id=UUID(TEST_DOC_UUID),
            page_number=1,
            class_id=0,
            class_name="invoice_number",
            x_center=0.5,
            y_center=0.5,
            width=0.2,
            height=0.05,
            bbox_x=100,
            bbox_y=100,
            bbox_width=200,
            bbox_height=50,
            text_value="12345",
            confidence=0.95,
            source="manual",
        )

        assert str(ann.document_id) == TEST_DOC_UUID
        assert ann.class_id == 0
        assert ann.x_center == 0.5
        assert ann.source == "manual"

    def test_normalized_coordinates(self):
        """Test normalized coordinates are 0-1 range."""
        # Valid normalized coords
        ann = AdminAnnotation(
            document_id=UUID(TEST_DOC_UUID),
            page_number=1,
            class_id=0,
            class_name="test",
            x_center=0.5,
            y_center=0.5,
            width=0.2,
            height=0.05,
            bbox_x=0,
            bbox_y=0,
            bbox_width=100,
            bbox_height=50,
        )

        assert 0 <= ann.x_center <= 1
        assert 0 <= ann.y_center <= 1
        assert 0 <= ann.width <= 1
        assert 0 <= ann.height <= 1


class TestAutoLabelFilePathResolution:
    """Tests for auto-label file path resolution.

    The auto-label endpoint needs to resolve the storage path (e.g., "raw_pdfs/uuid.pdf")
    to an actual filesystem path via the storage helper.
    """

    def test_extracts_filename_from_storage_path(self):
        """Test that filename is extracted from storage path correctly."""
        # Storage paths are like "raw_pdfs/uuid.pdf"
        storage_path = "raw_pdfs/550e8400-e29b-41d4-a716-446655440000.pdf"

        # The annotation endpoint extracts filename
        filename = storage_path.split("/")[-1] if "/" in storage_path else storage_path

        assert filename == "550e8400-e29b-41d4-a716-446655440000.pdf"

    def test_handles_path_without_prefix(self):
        """Test that paths without prefix are handled."""
        storage_path = "550e8400-e29b-41d4-a716-446655440000.pdf"

        filename = storage_path.split("/")[-1] if "/" in storage_path else storage_path

        assert filename == "550e8400-e29b-41d4-a716-446655440000.pdf"

    def test_storage_helper_resolves_path(self):
        """Test that storage helper can resolve the path."""
        from pathlib import Path
        from unittest.mock import MagicMock, patch

        # Mock storage helper
        mock_storage = MagicMock()
        mock_path = Path("/storage/raw_pdfs/test.pdf")
        mock_storage.get_raw_pdf_local_path.return_value = mock_path

        with patch(
            "backend.web.services.storage_helpers.get_storage_helper",
            return_value=mock_storage,
        ):
            from backend.web.services.storage_helpers import get_storage_helper

            storage = get_storage_helper()
            result = storage.get_raw_pdf_local_path("test.pdf")

            assert result == mock_path
            mock_storage.get_raw_pdf_local_path.assert_called_once_with("test.pdf")

    def test_auto_label_request_validation(self):
        """Test AutoLabelRequest validates field_values."""
        # Valid request
        request = AutoLabelRequest(
            field_values={"InvoiceNumber": "12345"},
            replace_existing=False,
        )
        assert request.field_values == {"InvoiceNumber": "12345"}

        # Empty field_values should be valid at schema level
        # (endpoint validates non-empty)
        request_empty = AutoLabelRequest(
            field_values={},
            replace_existing=False,
        )
        assert request_empty.field_values == {}


class TestMatchClassAttributes:
    """Tests for Match class attributes used in auto-labeling.

    The autolabel service uses Match objects from FieldMatcher.
    Verifies the correct attribute names are used.
    """

    def test_match_has_matched_text_attribute(self):
        """Test that Match class has matched_text attribute (not matched_value)."""
        from shared.matcher.models import Match

        # Create a Match object
        match = Match(
            field="invoice_number",
            value="12345",
            bbox=(100, 100, 200, 150),
            page_no=0,
            score=0.95,
            matched_text="INV-12345",
            context_keywords=["faktura", "nummer"],
        )

        # Verify matched_text exists (this is what autolabel.py should use)
        assert hasattr(match, "matched_text")
        assert match.matched_text == "INV-12345"

        # Verify matched_value does NOT exist
        # This was the bug - autolabel.py was using matched_value instead of matched_text
        assert not hasattr(match, "matched_value")

    def test_match_attributes_for_annotation_creation(self):
        """Test that Match has all attributes needed for annotation creation."""
        from shared.matcher.models import Match

        match = Match(
            field="amount",
            value="1000.00",
            bbox=(50, 200, 150, 230),
            page_no=0,
            score=0.88,
            matched_text="1 000,00",
            context_keywords=["att betala", "summa"],
        )

        # These are all the attributes used in autolabel._create_annotations_from_matches
        assert hasattr(match, "bbox")
        assert hasattr(match, "matched_text")  # NOT matched_value
        assert hasattr(match, "score")

        # Verify bbox format
        assert len(match.bbox) == 4  # (x0, y0, x1, y1)
