"""Tests for Pydantic field/model validators on claim models."""
from __future__ import annotations
import pytest
from pydantic import ValidationError


class TestDiagnosisCodeValidators:
    def test_valid_code(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        dx = DiagnosisCode(code="J06.9", pointer=1)
        assert dx.code == "J06.9"

    def test_code_stripped_and_uppercased(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        dx = DiagnosisCode(code="  j06.9  ", pointer=1)
        assert dx.code == "J06.9"

    def test_alphanumeric_code(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        dx = DiagnosisCode(code="V00.01XA", pointer=1)
        assert dx.code == "V00.01XA"

    def test_invalid_code_format(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        with pytest.raises(ValidationError, match="ICD-10"):
            DiagnosisCode(code="INVALID", pointer=1)

    def test_pointer_out_of_range(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        with pytest.raises(ValidationError, match="pointer"):
            DiagnosisCode(code="J06.9", pointer=0)

    def test_invalid_type(self) -> None:
        from claim_validator.models.claim import DiagnosisCode
        with pytest.raises(ValidationError, match="diagnosis type"):
            DiagnosisCode(code="J06.9", type="invalid")


class TestClaimLineDataValidators:
    def test_valid_cpt(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        line = ClaimLineData(procedure_code="99213", charge_amount=150.0)
        assert line.procedure_code == "99213"

    def test_valid_hcpcs(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        line = ClaimLineData(procedure_code="J0129", charge_amount=50.0)
        assert line.procedure_code == "J0129"

    def test_invalid_procedure_code(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        with pytest.raises(ValidationError, match="CPT/HCPCS"):
            ClaimLineData(procedure_code="INVALID", charge_amount=100.0)

    def test_negative_charge(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        with pytest.raises(ValidationError, match="positive"):
            ClaimLineData(procedure_code="99213", charge_amount=-10.0)

    def test_charge_rounded(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        line = ClaimLineData(procedure_code="99213", charge_amount=150.555)
        assert line.charge_amount == 150.56

    def test_invalid_modifier(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        with pytest.raises(ValidationError, match="modifier"):
            ClaimLineData(procedure_code="99213", charge_amount=100.0, modifiers=["ABC"])

    def test_npi_luhn_check(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        with pytest.raises(ValidationError, match="NPI"):
            ClaimLineData(procedure_code="99213", charge_amount=100.0, rendering_provider_npi="1234567890")

    def test_valid_npi(self) -> None:
        from claim_validator.models.claim import ClaimLineData
        line = ClaimLineData(procedure_code="99213", charge_amount=100.0, rendering_provider_npi="1234567893")
        assert line.rendering_provider_npi == "1234567893"


class TestClaimDataValidators:
    def test_valid_claim(self) -> None:
        from claim_validator.models.claim import ClaimData, ClaimLineData, DiagnosisCode
        claim = ClaimData(
            billing_provider_npi="1234567893",
            diagnosis_codes=[DiagnosisCode(code="J06.9")],
            lines=[ClaimLineData(procedure_code="99213", charge_amount=150.0)],
        )
        assert claim.billing_provider_npi == "1234567893"

    def test_invalid_npi_checksum(self) -> None:
        from claim_validator.models.claim import ClaimData, ClaimLineData, DiagnosisCode
        with pytest.raises(ValidationError, match="NPI"):
            ClaimData(
                billing_provider_npi="1234567890",
                diagnosis_codes=[DiagnosisCode(code="J06.9")],
                lines=[ClaimLineData(procedure_code="99213", charge_amount=150.0)],
            )

    def test_invalid_gender(self) -> None:
        from claim_validator.models.claim import ClaimData, ClaimLineData, DiagnosisCode
        with pytest.raises(ValidationError, match="Gender"):
            ClaimData(
                patient_gender="X",
                diagnosis_codes=[DiagnosisCode(code="J06.9")],
                lines=[ClaimLineData(procedure_code="99213", charge_amount=150.0)],
            )

    def test_no_diagnosis_codes(self) -> None:
        from claim_validator.models.claim import ClaimData, ClaimLineData
        with pytest.raises(ValidationError, match="diagnosis code"):
            ClaimData(diagnosis_codes=[], lines=[ClaimLineData(procedure_code="99213", charge_amount=150.0)])

    def test_no_lines(self) -> None:
        from claim_validator.models.claim import ClaimData, DiagnosisCode
        with pytest.raises(ValidationError, match="claim line"):
            ClaimData(diagnosis_codes=[DiagnosisCode(code="J06.9")], lines=[])

    def test_gender_normalized(self) -> None:
        from claim_validator.models.claim import ClaimData, ClaimLineData, DiagnosisCode
        claim = ClaimData(
            patient_gender="m",
            diagnosis_codes=[DiagnosisCode(code="J06.9")],
            lines=[ClaimLineData(procedure_code="99213", charge_amount=150.0)],
        )
        assert claim.patient_gender == "M"
