"""Tests for CoverageCheckAI medical necessity validator (chain-based)."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

from claim_validator.constants import Severity
from claim_validator.models.deidentified import (
    DeidentifiedClaim,
    DeidentifiedLineData,
)
from claim_validator.models.results import Finding, ValidatorOutput
from claim_validator.validators.ai.coverage_check import (
    CoverageCheckAI,
)

# --- Helpers ---


def _make_claim(
    **overrides: object,
) -> DeidentifiedClaim:
    """Create a de-identified claim for testing."""
    defaults: dict = {
        "billing_provider_npi": "1234567893",
        "patient_age": 45,
        "patient_gender": "M",
        "payer_id": "BCBS01",
        "payer_name": "BCBS",
        "diagnosis_codes": [
            {"code": "J06.9", "pointer": 1},
        ],
        "lines": [
            DeidentifiedLineData(
                procedure_code="99213",
                diagnosis_pointers=[1],
                charge_amount=150.0,
            ),
        ],
    }
    defaults.update(overrides)
    return DeidentifiedClaim(**defaults)


def _make_mock_chain_result(findings=None):
    """Create a mock chain result."""
    result = MagicMock()
    if findings is None:
        findings = []
    result.to_validator_output.return_value = ValidatorOutput(
        validator_name="CoverageCheckAI",
        findings=findings,
    )
    return result


# --- Tests ---


class TestCoverageCheckAICleanClaim:
    """Test zero findings when no coverage concerns."""

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_no_concerns_returns_empty(self, mock_invoke) -> None:
        mock_invoke.return_value = _make_mock_chain_result()
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        assert isinstance(result, ValidatorOutput)
        assert len(result.findings) == 0

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_validator_name(self, mock_invoke) -> None:
        mock_invoke.return_value = _make_mock_chain_result()
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        assert result.validator_name == "CoverageCheckAI"


class TestCoverageCheckAIWithFindings:
    """Test coverage concerns produce findings."""

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_coverage_concern_produces_finding(self, mock_invoke) -> None:
        finding = Finding(
            code="AI_COVERAGE_CONCERN",
            message="Procedure may lack medical necessity documentation",
            severity=Severity.WARNING,
            field_name="procedure_code",
            suggestion="Provide clinical notes supporting medical necessity",
        )
        mock_invoke.return_value = _make_mock_chain_result(findings=[finding])
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        assert len(result.findings) == 1
        f = result.findings[0]
        assert f.code == "AI_COVERAGE_CONCERN"
        assert f.severity == Severity.WARNING

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_multiple_findings(self, mock_invoke) -> None:
        findings = [
            Finding(
                code="AI_COVERAGE_CONCERN",
                message="Concern A",
                severity=Severity.WARNING,
                field_name="procedure_code",
            ),
            Finding(
                code="AI_COVERAGE_CONCERN",
                message="Concern B",
                severity=Severity.WARNING,
                field_name="diagnosis_codes",
            ),
        ]
        mock_invoke.return_value = _make_mock_chain_result(findings=findings)
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        assert len(result.findings) == 2


class TestCoverageCheckAIChainFallback:
    """Test chain failure produces empty results."""

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_chain_returns_none_fallback(self, mock_invoke) -> None:
        mock_invoke.return_value = None
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        assert len(result.findings) == 0


class TestCoverageCheckAINoPHI:
    """Test no PHI in findings."""

    @patch("claim_validator.chains.base.invoke_chain_safe")
    def test_no_phi_in_findings(self, mock_invoke) -> None:
        finding = Finding(
            code="AI_COVERAGE_CONCERN",
            message="Coverage concern",
            severity=Severity.WARNING,
            field_name="procedure_code",
        )
        mock_invoke.return_value = _make_mock_chain_result(findings=[finding])
        mock_chain = MagicMock()
        validator = CoverageCheckAI(chain=mock_chain)
        result = validator.validate_deidentified(_make_claim())
        for f in result.findings:
            assert "John" not in f.message
            assert "SSN" not in f.message.upper()
