"""
Dashboard Service Integration Tests

Tests DashboardStatsService and DashboardActivityService with real database operations.
"""

from datetime import datetime, timezone
from uuid import uuid4

import pytest

from backend.data.admin_models import (
    AdminAnnotation,
    AdminDocument,
    AnnotationHistory,
    ModelVersion,
    TrainingDataset,
    TrainingTask,
)
from backend.web.services.dashboard_service import (
    DashboardStatsService,
    DashboardActivityService,
    is_annotation_complete,
    IDENTIFIER_CLASS_IDS,
    PAYMENT_CLASS_IDS,
)


class TestIsAnnotationComplete:
    """Tests for is_annotation_complete function."""

    def test_complete_with_invoice_number_and_bankgiro(self):
        """Test complete with invoice_number (0) and bankgiro (4)."""
        annotations = [
            {"class_id": 0},  # invoice_number
            {"class_id": 4},  # bankgiro
        ]
        assert is_annotation_complete(annotations) is True

    def test_complete_with_ocr_number_and_plusgiro(self):
        """Test complete with ocr_number (3) and plusgiro (5)."""
        annotations = [
            {"class_id": 3},  # ocr_number
            {"class_id": 5},  # plusgiro
        ]
        assert is_annotation_complete(annotations) is True

    def test_incomplete_missing_identifier(self):
        """Test incomplete when missing identifier."""
        annotations = [
            {"class_id": 4},  # bankgiro only
        ]
        assert is_annotation_complete(annotations) is False

    def test_incomplete_missing_payment(self):
        """Test incomplete when missing payment."""
        annotations = [
            {"class_id": 0},  # invoice_number only
        ]
        assert is_annotation_complete(annotations) is False

    def test_incomplete_empty_annotations(self):
        """Test incomplete with empty annotations."""
        assert is_annotation_complete([]) is False

    def test_complete_with_multiple_fields(self):
        """Test complete with multiple fields."""
        annotations = [
            {"class_id": 0},  # invoice_number
            {"class_id": 1},  # invoice_date
            {"class_id": 3},  # ocr_number
            {"class_id": 4},  # bankgiro
            {"class_id": 5},  # plusgiro
            {"class_id": 6},  # amount
        ]
        assert is_annotation_complete(annotations) is True


class TestDashboardStatsService:
    """Tests for DashboardStatsService."""

    def test_get_stats_empty_database(self, patched_session):
        """Test stats with empty database."""
        service = DashboardStatsService()

        stats = service.get_stats()

        assert stats["total_documents"] == 0
        assert stats["annotation_complete"] == 0
        assert stats["annotation_incomplete"] == 0
        assert stats["pending"] == 0
        assert stats["completeness_rate"] == 0.0

    def test_get_stats_with_documents(self, patched_session, admin_token):
        """Test stats with various document states."""
        service = DashboardStatsService()
        session = patched_session

        # Create documents with different statuses
        docs = []
        for i, status in enumerate(["pending", "auto_labeling", "labeled", "labeled", "exported"]):
            doc = AdminDocument(
                document_id=uuid4(),
                admin_token=admin_token.token,
                filename=f"doc_{i}.pdf",
                file_size=1024,
                content_type="application/pdf",
                file_path=f"/uploads/doc_{i}.pdf",
                page_count=1,
                status=status,
                upload_source="ui",
                category="invoice",
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )
            session.add(doc)
            docs.append(doc)
        session.commit()

        stats = service.get_stats()

        assert stats["total_documents"] == 5
        assert stats["pending"] == 2  # pending + auto_labeling

    def test_get_stats_complete_annotations(self, patched_session, admin_token):
        """Test completeness calculation with proper annotations."""
        service = DashboardStatsService()
        session = patched_session

        # Create a labeled document with complete annotations
        doc = AdminDocument(
            document_id=uuid4(),
            admin_token=admin_token.token,
            filename="complete_doc.pdf",
            file_size=1024,
            content_type="application/pdf",
            file_path="/uploads/complete_doc.pdf",
            page_count=1,
            status="labeled",
            upload_source="ui",
            category="invoice",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(doc)
        session.commit()

        # Add identifier annotation (invoice_number = class_id 0)
        ann1 = AdminAnnotation(
            annotation_id=uuid4(),
            document_id=doc.document_id,
            page_number=1,
            class_id=0,
            class_name="invoice_number",
            x_center=0.5,
            y_center=0.1,
            width=0.2,
            height=0.05,
            bbox_x=400,
            bbox_y=80,
            bbox_width=160,
            bbox_height=40,
            text_value="INV-001",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(ann1)

        # Add payment annotation (bankgiro = class_id 4)
        ann2 = AdminAnnotation(
            annotation_id=uuid4(),
            document_id=doc.document_id,
            page_number=1,
            class_id=4,
            class_name="bankgiro",
            x_center=0.5,
            y_center=0.2,
            width=0.2,
            height=0.05,
            bbox_x=400,
            bbox_y=160,
            bbox_width=160,
            bbox_height=40,
            text_value="123-4567",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(ann2)
        session.commit()

        stats = service.get_stats()

        assert stats["annotation_complete"] == 1
        assert stats["annotation_incomplete"] == 0
        assert stats["completeness_rate"] == 100.0

    def test_get_stats_incomplete_annotations(self, patched_session, admin_token):
        """Test completeness with incomplete annotations."""
        service = DashboardStatsService()
        session = patched_session

        # Create a labeled document missing payment annotation
        doc = AdminDocument(
            document_id=uuid4(),
            admin_token=admin_token.token,
            filename="incomplete_doc.pdf",
            file_size=1024,
            content_type="application/pdf",
            file_path="/uploads/incomplete_doc.pdf",
            page_count=1,
            status="labeled",
            upload_source="ui",
            category="invoice",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(doc)
        session.commit()

        # Add only identifier annotation (missing payment)
        ann = AdminAnnotation(
            annotation_id=uuid4(),
            document_id=doc.document_id,
            page_number=1,
            class_id=0,
            class_name="invoice_number",
            x_center=0.5,
            y_center=0.1,
            width=0.2,
            height=0.05,
            bbox_x=400,
            bbox_y=80,
            bbox_width=160,
            bbox_height=40,
            text_value="INV-001",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(ann)
        session.commit()

        stats = service.get_stats()

        assert stats["annotation_complete"] == 0
        assert stats["annotation_incomplete"] == 1
        assert stats["completeness_rate"] == 0.0

    def test_get_stats_mixed_completeness(self, patched_session, admin_token):
        """Test stats with mix of complete and incomplete documents."""
        service = DashboardStatsService()
        session = patched_session

        # Create 2 labeled documents
        docs = []
        for i in range(2):
            doc = AdminDocument(
                document_id=uuid4(),
                admin_token=admin_token.token,
                filename=f"mixed_doc_{i}.pdf",
                file_size=1024,
                content_type="application/pdf",
                file_path=f"/uploads/mixed_doc_{i}.pdf",
                page_count=1,
                status="labeled",
                upload_source="ui",
                category="invoice",
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )
            session.add(doc)
            docs.append(doc)
        session.commit()

        # First document: complete (has identifier + payment)
        session.add(AdminAnnotation(
            annotation_id=uuid4(),
            document_id=docs[0].document_id,
            page_number=1,
            class_id=0,  # invoice_number
            class_name="invoice_number",
            x_center=0.5, y_center=0.1, width=0.2, height=0.05,
            bbox_x=400, bbox_y=80, bbox_width=160, bbox_height=40,
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        ))
        session.add(AdminAnnotation(
            annotation_id=uuid4(),
            document_id=docs[0].document_id,
            page_number=1,
            class_id=4,  # bankgiro
            class_name="bankgiro",
            x_center=0.5, y_center=0.2, width=0.2, height=0.05,
            bbox_x=400, bbox_y=160, bbox_width=160, bbox_height=40,
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        ))

        # Second document: incomplete (missing payment)
        session.add(AdminAnnotation(
            annotation_id=uuid4(),
            document_id=docs[1].document_id,
            page_number=1,
            class_id=0,  # invoice_number only
            class_name="invoice_number",
            x_center=0.5, y_center=0.1, width=0.2, height=0.05,
            bbox_x=400, bbox_y=80, bbox_width=160, bbox_height=40,
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        ))
        session.commit()

        stats = service.get_stats()

        assert stats["annotation_complete"] == 1
        assert stats["annotation_incomplete"] == 1
        assert stats["completeness_rate"] == 50.0


class TestDashboardActivityService:
    """Tests for DashboardActivityService."""

    def test_get_recent_activities_empty(self, patched_session):
        """Test activities with empty database."""
        service = DashboardActivityService()

        activities = service.get_recent_activities()

        assert activities == []

    def test_get_recent_activities_document_uploads(self, patched_session, admin_token):
        """Test activities include document uploads."""
        service = DashboardActivityService()
        session = patched_session

        # Create documents
        for i in range(3):
            doc = AdminDocument(
                document_id=uuid4(),
                admin_token=admin_token.token,
                filename=f"activity_doc_{i}.pdf",
                file_size=1024,
                content_type="application/pdf",
                file_path=f"/uploads/activity_doc_{i}.pdf",
                page_count=1,
                status="pending",
                upload_source="ui",
                category="invoice",
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )
            session.add(doc)
        session.commit()

        activities = service.get_recent_activities()

        upload_activities = [a for a in activities if a["type"] == "document_uploaded"]
        assert len(upload_activities) == 3

    def test_get_recent_activities_annotation_overrides(self, patched_session, sample_document, sample_annotation):
        """Test activities include annotation overrides."""
        service = DashboardActivityService()
        session = patched_session

        # Create annotation history with override
        history = AnnotationHistory(
            history_id=uuid4(),
            annotation_id=sample_annotation.annotation_id,
            document_id=sample_document.document_id,
            action="override",
            previous_value={"text_value": "OLD-001"},
            new_value={"text_value": "NEW-001", "class_name": "invoice_number"},
            changed_by="test-admin",
            created_at=datetime.now(timezone.utc),
        )
        session.add(history)
        session.commit()

        activities = service.get_recent_activities()

        override_activities = [a for a in activities if a["type"] == "annotation_modified"]
        assert len(override_activities) >= 1

    def test_get_recent_activities_training_completed(self, patched_session, sample_training_task):
        """Test activities include training completions."""
        service = DashboardActivityService()
        session = patched_session

        # Update training task to completed
        sample_training_task.status = "completed"
        sample_training_task.metrics_mAP = 0.85
        sample_training_task.updated_at = datetime.now(timezone.utc)
        session.add(sample_training_task)
        session.commit()

        activities = service.get_recent_activities()

        training_activities = [a for a in activities if a["type"] == "training_completed"]
        assert len(training_activities) >= 1
        assert "mAP" in training_activities[0]["metadata"]

    def test_get_recent_activities_training_failed(self, patched_session, sample_training_task):
        """Test activities include training failures."""
        service = DashboardActivityService()
        session = patched_session

        # Update training task to failed
        sample_training_task.status = "failed"
        sample_training_task.error_message = "CUDA out of memory"
        sample_training_task.updated_at = datetime.now(timezone.utc)
        session.add(sample_training_task)
        session.commit()

        activities = service.get_recent_activities()

        failed_activities = [a for a in activities if a["type"] == "training_failed"]
        assert len(failed_activities) >= 1
        assert failed_activities[0]["metadata"]["error"] == "CUDA out of memory"

    def test_get_recent_activities_model_activated(self, patched_session, sample_model_version):
        """Test activities include model activations."""
        service = DashboardActivityService()
        session = patched_session

        # Activate model
        sample_model_version.is_active = True
        sample_model_version.activated_at = datetime.now(timezone.utc)
        session.add(sample_model_version)
        session.commit()

        activities = service.get_recent_activities()

        activation_activities = [a for a in activities if a["type"] == "model_activated"]
        assert len(activation_activities) >= 1
        assert activation_activities[0]["metadata"]["version"] == sample_model_version.version

    def test_get_recent_activities_limit(self, patched_session, admin_token):
        """Test activity limit parameter."""
        service = DashboardActivityService()
        session = patched_session

        # Create many documents
        for i in range(20):
            doc = AdminDocument(
                document_id=uuid4(),
                admin_token=admin_token.token,
                filename=f"limit_doc_{i}.pdf",
                file_size=1024,
                content_type="application/pdf",
                file_path=f"/uploads/limit_doc_{i}.pdf",
                page_count=1,
                status="pending",
                upload_source="ui",
                category="invoice",
                created_at=datetime.now(timezone.utc),
                updated_at=datetime.now(timezone.utc),
            )
            session.add(doc)
        session.commit()

        activities = service.get_recent_activities(limit=5)

        assert len(activities) <= 5

    def test_get_recent_activities_sorted_by_timestamp(self, patched_session, admin_token, sample_training_task):
        """Test activities are sorted by timestamp descending."""
        service = DashboardActivityService()
        session = patched_session

        # Create document
        doc = AdminDocument(
            document_id=uuid4(),
            admin_token=admin_token.token,
            filename="sorted_doc.pdf",
            file_size=1024,
            content_type="application/pdf",
            file_path="/uploads/sorted_doc.pdf",
            page_count=1,
            status="pending",
            upload_source="ui",
            category="invoice",
            created_at=datetime.now(timezone.utc),
            updated_at=datetime.now(timezone.utc),
        )
        session.add(doc)

        # Complete training task
        sample_training_task.status = "completed"
        sample_training_task.metrics_mAP = 0.90
        sample_training_task.updated_at = datetime.now(timezone.utc)
        session.add(sample_training_task)
        session.commit()

        activities = service.get_recent_activities()

        # Verify sorted by timestamp DESC
        timestamps = [a["timestamp"] for a in activities]
        assert timestamps == sorted(timestamps, reverse=True)
