"""End-to-end test for coverage to compute flow integration."""

# FILE: tests/e2e/test_coverage_to_compute_flow.py

import os
import sys
from unittest.mock import patch

import pytest
from fastapi.testclient import TestClient

sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.append(
    os.path.join(os.path.dirname(__file__), "..", "..", "apps", "svc-coverage")
)

from libs.schemas import OverallStatus, Role, Status


def create_test_app():
    """Create a test FastAPI app without problematic startup events"""
    from fastapi import FastAPI

    from libs.config import BaseAppSettings
    from libs.security import TrustedProxyMiddleware

    # Create minimal settings
    class TestSettings(BaseAppSettings):
        service_name: str = "test-coverage"
        internal_cidrs: list[str] = ["127.0.0.1/32"]

    settings = TestSettings()

    # Create test app
    test_app = FastAPI(
        title="Test Coverage Service",
        description="Test coverage service",
        version="1.0.0",
    )

    # Add middleware
    test_app.add_middleware(
        TrustedProxyMiddleware, internal_cidrs=settings.internal_cidrs
    )

    # Import and add routes from main app
    from main import app as main_app

    test_app.router = main_app.router

    return test_app


class TestCoverageToComputeFlow:
    """Test end-to-end flow from coverage checking to compute triggering"""

    @pytest.fixture
    def client(self):
        """Create test client"""
        test_app = create_test_app()
        return TestClient(test_app)

    @pytest.fixture
    def mock_dependencies(self):
        """Mock all external dependencies"""
        with (
            patch("sys.modules") as mock_modules,
            patch("libs.policy.loader.PolicyLoader") as mock_loader,
            patch("libs.neo.client.Neo4jClient") as mock_kg,
            patch("libs.rag.retriever.RAGRetriever") as mock_rag,
            patch("sqlalchemy.orm.Session") as mock_db,
            patch("libs.config.create_neo4j_client") as mock_create_neo4j,
            patch("libs.config.create_event_bus") as mock_create_event_bus,
            patch("libs.policy.get_policy_loader") as mock_get_policy_loader,
        ):

            # Mock policy loader
            from unittest.mock import Mock

            mock_policy = Mock()
            mock_policy.policy.version = "1.0"
            mock_policy.policy.jurisdiction = "UK"
            mock_policy.policy.tax_year = "2024-25"
            mock_loader.return_value.load_policy.return_value = mock_policy
            mock_loader.return_value.compile_predicates.return_value = mock_policy

            # Mock KG client
            mock_kg_client = Mock()
            mock_kg.return_value = mock_kg_client

            # Mock RAG client
            mock_rag_client = Mock()
            mock_rag.return_value = mock_rag_client

            # Mock database session
            mock_session = Mock()
            mock_db.return_value = mock_session

            # Mock the factory functions that are called during startup
            mock_create_neo4j.return_value = Mock()
            mock_create_event_bus.return_value = Mock()
            mock_get_policy_loader.return_value = mock_loader.return_value

            yield {
                "policy_loader": mock_loader.return_value,
                "kg_client": mock_kg_client,
                "rag_client": mock_rag_client,
                "db_session": mock_session,
                "policy": mock_policy,
            }

    def test_complete_coverage_flow(self, client, mock_dependencies):
        """Test complete flow when all evidence is present"""
        # Mock coverage evaluator to return complete coverage
        with patch("apps.svc_coverage.main.CoverageEvaluator") as mock_evaluator_class:
            from unittest.mock import Mock

            mock_evaluator = Mock()
            mock_evaluator_class.return_value = mock_evaluator

            # Mock complete coverage report
            mock_report = Mock()
            mock_report.overall_status = OverallStatus.OK
            mock_report.schedules_required = ["SA102"]
            mock_report.blocking_items = []

            # Mock coverage details
            mock_evidence = Mock()
            mock_evidence.id = "P60"
            mock_evidence.status = Status.PRESENT_VERIFIED
            mock_evidence.role = Role.REQUIRED
            mock_evidence.found = [
                Mock(
                    doc_id="DOC-P60-001",
                    kind="P60",
                    ocr_confidence=0.95,
                    extract_confidence=0.92,
                )
            ]

            mock_schedule = Mock()
            mock_schedule.schedule_id = "SA102"
            mock_schedule.status = OverallStatus.OK
            mock_schedule.evidence = [mock_evidence]

            mock_report.coverage = [mock_schedule]
            mock_evaluator.check_document_coverage.return_value = mock_report

            # Call coverage check endpoint
            response = client.post(
                "/v1/coverage/check",
                json={
                    "taxpayer_id": "T-001",
                    "tax_year": "2024-25",
                    "jurisdiction": "UK",
                },
            )

            assert response.status_code == 200
            data = response.json()

            # Verify response structure
            assert data["overall_status"] == "OK"
            assert len(data["schedules_required"]) == 1
            assert "SA102" in data["schedules_required"]
            assert len(data["coverage"]) == 1
            assert len(data["blocking_items"]) == 0

            # Verify coverage details
            sa102_coverage = data["coverage"][0]
            assert sa102_coverage["schedule_id"] == "SA102"
            assert sa102_coverage["status"] == "OK"
            assert len(sa102_coverage["evidence"]) == 1

            p60_evidence = sa102_coverage["evidence"][0]
            assert p60_evidence["id"] == "P60"
            assert p60_evidence["status"] == "PRESENT_VERIFIED"
            assert p60_evidence["role"] == "REQUIRED"

    def test_incomplete_coverage_flow(self, client, mock_dependencies):
        """Test flow when evidence is missing"""
        with patch("apps.svc_coverage.main.CoverageEvaluator") as mock_evaluator_class:
            from unittest.mock import Mock

            mock_evaluator = Mock()
            mock_evaluator_class.return_value = mock_evaluator

            # Mock incomplete coverage report
            mock_report = Mock()
            mock_report.overall_status = OverallStatus.BLOCKING
            mock_report.schedules_required = ["SA102"]

            # Mock evidence
            mock_evidence = Mock()
            mock_evidence.id = "P60"
            mock_evidence.status = Status.MISSING
            mock_evidence.role = Role.REQUIRED
            mock_evidence.found = []
            mock_evidence.acceptable_alternatives = ["P45", "FinalPayslipYTD"]

            mock_schedule = Mock()
            mock_schedule.schedule_id = "SA102"
            mock_schedule.status = OverallStatus.BLOCKING
            mock_schedule.evidence = [mock_evidence]

            # Mock blocking item (without acceptable_alternatives field per schema)
            mock_blocking_item = Mock()
            mock_blocking_item.schedule_id = "SA102"
            mock_blocking_item.evidence_id = "P60"

            mock_report.coverage = [mock_schedule]
            mock_report.blocking_items = [mock_blocking_item]
            mock_evaluator.check_document_coverage.return_value = mock_report

            # Call coverage check endpoint
            response = client.post(
                "/v1/coverage/check",
                json={
                    "taxpayer_id": "T-001",
                    "tax_year": "2024-25",
                    "jurisdiction": "UK",
                },
            )

            assert response.status_code == 200
            data = response.json()

            # Verify incomplete status
            assert data["overall_status"] == "INCOMPLETE"
            assert len(data["blocking_items"]) == 1

            # Verify blocking item details
            blocking_item = data["blocking_items"][0]
            assert blocking_item["evidence_id"] == "P60"
            assert blocking_item["schedule_id"] == "SA102"

            # Verify alternatives are in the evidence item, not blocking item
            sa102_coverage = data["coverage"][0]
            p60_evidence = sa102_coverage["evidence"][0]
            assert len(p60_evidence["acceptable_alternatives"]) == 2

    def test_clarification_flow(self, client, mock_dependencies):
        """Test clarification question generation flow"""
        with patch("apps.svc_coverage.main.CoverageEvaluator") as mock_evaluator_class:
            mock_evaluator = AsyncMock()
            mock_evaluator_class.return_value = mock_evaluator

            # Mock clarification response
            mock_evaluator.generate_clarifying_question.return_value = AsyncMock(
                question="To complete the SA102 for 2024-25, we need P60. These documents support boxes SA102_b1, SA102_b2.",
                why="P60 provides year-end pay and PAYE tax figures required for employment income reporting.",
                blocking=True,
                boxes_affected=["SA102_b1", "SA102_b2"],
                upload_options=[
                    AsyncMock(
                        label="Upload P60 (PDF/CSV)",
                        accepted_formats=["pdf", "csv"],
                        upload_endpoint="/v1/ingest/upload?tag=P60",
                    ),
                    AsyncMock(
                        label="Upload P45 (PDF/CSV)",
                        accepted_formats=["pdf", "csv"],
                        upload_endpoint="/v1/ingest/upload?tag=P45",
                    ),
                ],
                citations=[
                    AsyncMock(
                        rule_id="UK.SA102.P60.Required",
                        doc_id="SA102-Notes-2025",
                        locator="p.3 §1.1",
                        url="https://docs.local/SA102-Notes-2025#p3s1.1",
                    )
                ],
            )

            # Call clarification endpoint
            response = client.post(
                "/v1/coverage/clarify",
                json={
                    "taxpayer_id": "T-001",
                    "tax_year": "2024-25",
                    "jurisdiction": "UK",
                    "schedule_id": "SA102",
                    "evidence_id": "P60",
                },
            )

            assert response.status_code == 200
            data = response.json()

            # Verify clarification response
            assert "question" in data
            assert "why" in data
            assert data["blocking"] is True
            assert len(data["boxes_affected"]) == 2
            assert len(data["upload_options"]) == 2
            assert len(data["citations"]) == 1

            # Verify upload options
            upload_option = data["upload_options"][0]
            assert "Upload P60" in upload_option["label"]
            assert "pdf" in upload_option["accepted_formats"]
            assert "/v1/ingest/upload" in upload_option["upload_endpoint"]

    def test_policy_validation_flow(self, client, mock_dependencies):
        """Test policy validation endpoint"""
        # Mock policy validation
        mock_dependencies["policy_loader"].validate_policy.return_value = AsyncMock(
            ok=True,
            errors=[],
        )

        # Call validation endpoint
        response = client.post(
            "/v1/coverage/validate",
            json={
                "version": "1.0",
                "jurisdiction": "UK",
                "tax_year": "2024-25",
                "tax_year_boundary": {"start": "2024-04-06", "end": "2025-04-05"},
                "defaults": {"confidence_thresholds": {"ocr": 0.82, "extract": 0.85}},
                "document_kinds": ["P60"],
                "triggers": {
                    "SA102": {"any_of": ["exists(IncomeItem[type='Employment'])"]}
                },
                "schedules": {
                    "SA102": {
                        "evidence": [
                            {"id": "P60", "role": "REQUIRED", "boxes": ["SA102_b1"]}
                        ]
                    }
                },
                "status_classifier": {
                    "present_verified": {"min_ocr": 0.82},
                    "present_unverified": {"min_ocr": 0.60},
                    "conflicting": {"conflict_rules": []},
                    "missing": {"default": True},
                },
                "conflict_resolution": {"precedence": ["P60"]},
                "question_templates": {"default": {"text": "test", "why": "test"}},
            },
        )

        assert response.status_code == 200
        data = response.json()

        assert data["valid"] is True
        assert len(data["errors"]) == 0

    def test_policy_reload_flow(self, client, mock_dependencies):
        """Test policy hot reload flow"""
        # Mock admin user check
        with patch("apps.svc_coverage.main.check_admin_permission") as mock_admin:
            mock_admin.return_value = True

            # Call reload endpoint
            response = client.post(
                "/admin/coverage/reload",
                headers={"Authorization": "Bearer admin-token"},
            )

            assert response.status_code == 200
            data = response.json()

            assert data["reloaded"] is True
            assert "timestamp" in data
            assert "version" in data

    def test_policy_info_flow(self, client, mock_dependencies):
        """Test policy information endpoint"""
        # Call policy info endpoint
        response = client.get("/v1/coverage/policy")

        assert response.status_code == 200
        data = response.json()

        assert data["version"] == "1.0"
        assert data["jurisdiction"] == "UK"
        assert data["tax_year"] == "2024-25"

    def test_health_check_flow(self, client, mock_dependencies):
        """Test health check endpoint"""
        response = client.get("/health")

        assert response.status_code == 200
        data = response.json()

        assert data["status"] == "healthy"
        assert "timestamp" in data

    def test_error_handling_flow(self, client, mock_dependencies):
        """Test error handling in coverage flow"""
        with patch("apps.svc_coverage.main.CoverageEvaluator") as mock_evaluator_class:
            mock_evaluator = AsyncMock()
            mock_evaluator_class.return_value = mock_evaluator

            # Mock evaluator to raise exception
            mock_evaluator.check_document_coverage.side_effect = Exception(
                "KG connection failed"
            )

            # Call coverage check endpoint
            response = client.post(
                "/v1/coverage/check",
                json={
                    "taxpayer_id": "T-001",
                    "tax_year": "2024-25",
                    "jurisdiction": "UK",
                },
            )

            assert response.status_code == 500
            data = response.json()

            assert "error" in data["detail"]

    def test_invalid_request_flow(self, client, mock_dependencies):
        """Test validation of invalid requests"""
        # Missing required fields
        response = client.post(
            "/v1/coverage/check",
            json={
                "taxpayer_id": "T-001",
                # Missing tax_year and jurisdiction
            },
        )

        assert response.status_code == 422  # Validation error

    def test_unauthorized_admin_flow(self, client, mock_dependencies):
        """Test unauthorized access to admin endpoints"""
        with patch("apps.svc_coverage.main.check_admin_permission") as mock_admin:
            mock_admin.return_value = False

            response = client.post(
                "/admin/coverage/reload", headers={"Authorization": "Bearer user-token"}
            )

            assert response.status_code == 403

    def test_concurrent_requests_flow(self, client, mock_dependencies):
        """Test handling of concurrent requests"""
        with patch("apps.svc_coverage.main.CoverageEvaluator") as mock_evaluator_class:
            mock_evaluator = AsyncMock()
            mock_evaluator_class.return_value = mock_evaluator

            # Mock successful response
            mock_evaluator.check_document_coverage.return_value = AsyncMock(
                overall_status=OverallStatus.OK,
                schedules_required=[],
                coverage=[],
                blocking_items=[],
            )

            # Make multiple concurrent requests
            responses = []
            for i in range(5):
                response = client.post(
                    "/v1/coverage/check",
                    json={
                        "taxpayer_id": f"T-{i:03d}",
                        "tax_year": "2024-25",
                        "jurisdiction": "UK",
                    },
                )
                responses.append(response)

            # All should succeed
            for response in responses:
                assert response.status_code == 200
                data = response.json()
                assert data["overall_status"] == "OK"
