"""Parity tests — verify LangChain path produces valid output."""
from __future__ import annotations

from unittest.mock import MagicMock

from claim_validator.constants import Severity
from claim_validator.models.deidentified import (
    DeidentifiedClaim,
    DeidentifiedLineData,
)


def _make_deidentified_claim() -> DeidentifiedClaim:
    """Create a minimal de-identified claim for testing."""
    return DeidentifiedClaim(
        patient_age=45,
        patient_gender="M",
        diagnosis_codes=[{"code": "J06.9", "pointer": 1}],
        lines=[
            DeidentifiedLineData(
                procedure_code="99213",
                modifiers=[],
                charge_amount=150.00,
                units=1.0,
                place_of_service="11",
                rendering_provider_npi=None,
                service_year=None,
            )
        ],
        payer_id="60054",
        payer_name="Aetna",
        place_of_service="11",
        total_charge=150.00,
    )


class TestCodeValidationParity:
    """Verify chain path produces valid ValidatorOutput."""

    def test_chain_path_returns_validator_output(self) -> None:
        """The chain path should return a ValidatorOutput with findings."""
        from claim_validator.chains.output_schemas.code_validation import (
            CodeValidationFinding,
            CodeValidationOutput,
        )
        from claim_validator.validators.ai.code_validation import CodeValidationAI

        # Mock the chain to return a structured CodeValidationOutput
        mock_chain = MagicMock()
        mock_chain.invoke.return_value = CodeValidationOutput(
            findings=[
                CodeValidationFinding(
                    code="AI_CLINICAL_IMPLAUSIBILITY",
                    message="Test finding",
                    severity=Severity.WARNING,
                    field_name="procedure_code",
                    line_number=1,
                    suggestion="Check coding",
                )
            ],
            confidence=0.85,
            reasoning="Test reasoning",
        )

        validator = CodeValidationAI(chain=mock_chain, use_langchain=True)
        claim = _make_deidentified_claim()
        result = validator.validate_deidentified(claim)

        assert result.validator_name == "code_validation_ai"
        assert len(result.findings) == 1
        assert result.findings[0].code == "AI_CLINICAL_IMPLAUSIBILITY"
        assert result.findings[0].severity == Severity.WARNING
        assert result.findings[0].context["confidence"] == 0.85

    def test_chain_path_empty_findings(self) -> None:
        """Clean claim should produce empty findings via chain."""
        from claim_validator.chains.output_schemas.code_validation import (
            CodeValidationOutput,
        )
        from claim_validator.validators.ai.code_validation import CodeValidationAI

        mock_chain = MagicMock()
        mock_chain.invoke.return_value = CodeValidationOutput(
            findings=[],
            confidence=0.95,
            reasoning="All plausible",
        )

        validator = CodeValidationAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        assert result.validator_name == "code_validation_ai"
        assert len(result.findings) == 0

    def test_chain_fallback_on_error(self) -> None:
        """Chain failure should return empty findings (graceful degradation)."""
        from claim_validator.validators.ai.code_validation import CodeValidationAI

        mock_chain = MagicMock()
        mock_chain.invoke.side_effect = Exception("LLM unavailable")

        validator = CodeValidationAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        # Should degrade gracefully — empty findings, not crash
        assert result.validator_name == "code_validation_ai"
        assert len(result.findings) == 0


class TestPriorAuthParity:
    """Verify prior auth chain path produces valid ValidatorOutput."""

    def test_chain_path_returns_findings(self) -> None:
        from claim_validator.chains.output_schemas.prior_auth import (
            PriorAuthOutput,
            PriorAuthRequirement,
        )
        from claim_validator.validators.ai.prior_auth import PriorAuthAI

        mock_chain = MagicMock()
        mock_chain.invoke.return_value = PriorAuthOutput(
            requirements=[
                PriorAuthRequirement(
                    procedure_code="70553",
                    requires_auth=True,
                    reason="MRI requires PA",
                    urgency="routine",
                )
            ],
            overall_determination="required",
            confidence=0.9,
            reasoning="High-cost imaging",
        )

        validator = PriorAuthAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        assert result.validator_name == "prior_auth_ai"
        assert len(result.findings) == 1
        assert result.findings[0].code == "AI_PRIOR_AUTH_LIKELY"

    def test_chain_path_no_auth_required(self) -> None:
        """Procedure not requiring auth should produce no findings."""
        from claim_validator.chains.output_schemas.prior_auth import (
            PriorAuthOutput,
            PriorAuthRequirement,
        )
        from claim_validator.validators.ai.prior_auth import PriorAuthAI

        mock_chain = MagicMock()
        mock_chain.invoke.return_value = PriorAuthOutput(
            requirements=[
                PriorAuthRequirement(
                    procedure_code="99213",
                    requires_auth=False,
                    reason="Routine office visit",
                    urgency="routine",
                )
            ],
            overall_determination="not_required",
            confidence=0.95,
            reasoning="No high-cost services",
        )

        validator = PriorAuthAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        assert result.validator_name == "prior_auth_ai"
        # requires_auth=False entries are filtered out by to_validator_output
        assert len(result.findings) == 0


class TestCoverageCheckParity:
    """Verify coverage check chain path produces valid ValidatorOutput."""

    def test_chain_path_returns_findings(self) -> None:
        from claim_validator.chains.output_schemas.coverage_check import (
            CoverageCheckOutput,
            CoverageConcern,
        )
        from claim_validator.validators.ai.coverage_check import CoverageCheckAI

        mock_chain = MagicMock()
        mock_chain.invoke.return_value = CoverageCheckOutput(
            concerns=[
                CoverageConcern(
                    code="AI_COVERAGE_CONCERN",
                    message="Frequency limit",
                    severity=Severity.WARNING,
                    medical_necessity_score=0.6,
                )
            ],
            overall_medical_necessity="questionable",
            confidence=0.8,
            reasoning="Too many visits",
        )

        validator = CoverageCheckAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        assert result.validator_name == "coverage_check_ai"
        assert len(result.findings) == 1
        assert result.findings[0].code == "AI_COVERAGE_CONCERN"

    def test_chain_path_no_concerns(self) -> None:
        """Clean claim should produce empty findings via coverage chain."""
        from claim_validator.chains.output_schemas.coverage_check import (
            CoverageCheckOutput,
        )
        from claim_validator.validators.ai.coverage_check import CoverageCheckAI

        mock_chain = MagicMock()
        mock_chain.invoke.return_value = CoverageCheckOutput(
            concerns=[],
            overall_medical_necessity="supported",
            confidence=0.95,
            reasoning="All services medically necessary",
        )

        validator = CoverageCheckAI(chain=mock_chain, use_langchain=True)
        result = validator.validate_deidentified(_make_deidentified_claim())

        assert result.validator_name == "coverage_check_ai"
        assert len(result.findings) == 0
