"""Unit tests for evidence status classification."""

# FILE: tests/unit/coverage/test_status_classifier.py

from datetime import datetime

import pytest

from libs.coverage.evaluator import CoverageEvaluator
from libs.schemas import (
    CompiledCoveragePolicy,
    CoveragePolicy,
    Defaults,
    FoundEvidence,
    Status,
    StatusClassifier,
    StatusClassifierConfig,
    TaxYearBoundary,
)
from libs.schemas.coverage.core import ConflictRules, Privacy, QuestionTemplates

# pylint: disable=wrong-import-position,import-error,too-few-public-methods,global-statement
# pylint: disable=raise-missing-from,unused-argument,too-many-arguments,too-many-positional-arguments
# pylint: disable=too-many-locals,import-outside-toplevel
# mypy: disable-error-code=union-attr
# mypy: disable-error-code=no-untyped-def


class TestStatusClassifier:
    """Test evidence status classification logic"""

    @pytest.fixture
    def mock_policy(self):
        """Create mock compiled policy for testing"""
        policy = CoveragePolicy(
            version="1.0",
            jurisdiction="UK",
            tax_year="2024-25",
            tax_year_boundary=TaxYearBoundary(start="2024-04-06", end="2025-04-05"),
            defaults=Defaults(
                confidence_thresholds={"ocr": 0.82, "extract": 0.85},
                date_tolerance_days=30,
            ),
            document_kinds=["P60"],
            status_classifier=StatusClassifierConfig(
                present_verified=StatusClassifier(
                    min_ocr=0.82,
                    min_extract=0.85,
                    date_in_year=True,
                ),
                present_unverified=StatusClassifier(
                    min_ocr=0.60,
                    min_extract=0.70,
                    date_in_year_or_tolerance=True,
                ),
                conflicting=StatusClassifier(
                    conflict_rules=["Same doc kind, different totals"]
                ),
                missing=StatusClassifier(),
            ),
            conflict_resolution=ConflictRules(precedence=["P60"]),
            question_templates=QuestionTemplates(
                default={"text": "test", "why": "test"}
            ),
            privacy=Privacy(vector_pii_free=True, redact_patterns=[]),
        )

        return CompiledCoveragePolicy(
            policy=policy,
            compiled_predicates={},
            compiled_at=datetime.utcnow(),
            hash="test-hash",
            source_files=["test.yaml"],
        )

    @pytest.fixture
    def evaluator(self):
        """Create coverage evaluator for testing"""
        return CoverageEvaluator()

    def test_classify_missing_evidence(self, evaluator, mock_policy):
        """Test classification when no evidence found"""
        found = []
        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.MISSING

    def test_classify_verified_evidence(self, evaluator, mock_policy):
        """Test classification of verified evidence"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2024-05-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED

    def test_classify_unverified_evidence(self, evaluator, mock_policy):
        """Test classification of unverified evidence"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.70,  # Below verified threshold
                extract_confidence=0.75,  # Below verified threshold
                date="2024-05-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.PRESENT_UNVERIFIED

    def test_classify_low_confidence_evidence(self, evaluator, mock_policy):
        """Test classification of very low confidence evidence"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.50,  # Below unverified threshold
                extract_confidence=0.55,  # Below unverified threshold
                date="2024-05-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.MISSING

    def test_classify_conflicting_evidence(self, evaluator, mock_policy):
        """Test classification when multiple conflicting documents found"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2024-05-15T10:00:00Z",
            ),
            FoundEvidence(
                doc_id="DOC-002",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2024-05-20T10:00:00Z",
            ),
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.CONFLICTING

    def test_classify_evidence_outside_tax_year(self, evaluator, mock_policy):
        """Test classification of evidence outside tax year"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2023-03-15T10:00:00Z",  # Outside tax year
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        # Evidence outside tax year should be unverified even with high confidence
        # This is correct business logic - date validation is part of verification
        assert status == Status.PRESENT_UNVERIFIED

    def test_classify_evidence_no_date(self, evaluator, mock_policy):
        """Test classification of evidence without date"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date=None,
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        # Evidence without date cannot be fully verified, even with high confidence
        # This is correct business logic - date validation is required for verification
        assert status == Status.PRESENT_UNVERIFIED

    def test_parse_tax_year_bounds(self, evaluator):
        """Test parsing of tax year boundary strings"""
        start_str = "2024-04-06"
        end_str = "2025-04-05"

        start, end = evaluator._parse_tax_year_bounds(start_str, end_str)

        assert isinstance(start, datetime)
        assert isinstance(end, datetime)
        assert start.year == 2024
        assert start.month == 4
        assert start.day == 6
        assert end.year == 2025
        assert end.month == 4
        assert end.day == 5

    def test_evidence_within_tax_year(self, evaluator, mock_policy):
        """Test evidence date validation within tax year"""
        # Evidence within tax year
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2024-06-15T10:00:00Z",  # Within 2024-25 tax year
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED

    def test_evidence_boundary_dates(self, evaluator, mock_policy):
        """Test evidence on tax year boundary dates"""
        # Test start boundary
        found_start = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2024-04-06T00:00:00Z",  # Exact start date
            )
        ]

        status = evaluator.classify_status(found_start, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED

        # Test end boundary
        found_end = [
            FoundEvidence(
                doc_id="DOC-002",
                kind="P60",
                ocr_confidence=0.85,
                extract_confidence=0.90,
                date="2025-04-05T23:59:59Z",  # Exact end date
            )
        ]

        status = evaluator.classify_status(found_end, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED

    def test_threshold_edge_cases(self, evaluator, mock_policy):
        """Test classification at threshold boundaries"""
        # Exactly at verified threshold
        found_exact = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.82,  # Exactly at threshold
                extract_confidence=0.85,  # Exactly at threshold
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found_exact, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED

        # Just below verified threshold
        found_below = [
            FoundEvidence(
                doc_id="DOC-002",
                kind="P60",
                ocr_confidence=0.81,  # Just below threshold
                extract_confidence=0.84,  # Just below threshold
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found_below, mock_policy, "2024-25")
        assert status == Status.PRESENT_UNVERIFIED

    def test_mixed_confidence_levels(self, evaluator, mock_policy):
        """Test classification with mixed OCR and extract confidence"""
        # High OCR, low extract
        found_mixed1 = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.90,  # High
                extract_confidence=0.70,  # Low
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found_mixed1, mock_policy, "2024-25")
        assert status == Status.PRESENT_UNVERIFIED  # Both must meet threshold

        # Low OCR, high extract
        found_mixed2 = [
            FoundEvidence(
                doc_id="DOC-002",
                kind="P60",
                ocr_confidence=0.70,  # Low
                extract_confidence=0.90,  # High
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found_mixed2, mock_policy, "2024-25")
        assert status == Status.PRESENT_UNVERIFIED  # Both must meet threshold

    def test_zero_confidence_evidence(self, evaluator, mock_policy):
        """Test classification of zero confidence evidence"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=0.0,
                extract_confidence=0.0,
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.MISSING

    def test_perfect_confidence_evidence(self, evaluator, mock_policy):
        """Test classification of perfect confidence evidence"""
        found = [
            FoundEvidence(
                doc_id="DOC-001",
                kind="P60",
                ocr_confidence=1.0,
                extract_confidence=1.0,
                date="2024-06-15T10:00:00Z",
            )
        ]

        status = evaluator.classify_status(found, mock_policy, "2024-25")
        assert status == Status.PRESENT_VERIFIED
