"""Tests for the 835 ERA parser orchestrator."""
from datetime import date
from pathlib import Path

import pytest

from cyclone.parsers.exceptions import CycloneParseError
from cyclone.parsers.parse_835 import parse
from cyclone.parsers.payer import PayerConfig835

FIXTURE = Path(__file__).parent / "fixtures" / "minimal_835.txt"


def test_parse_minimal_fixture_returns_one_claim():
    text = FIXTURE.read_text()
    result = parse(text, PayerConfig835.co_medicaid_835())
    assert len(result.claims) == 1
    c = result.claims[0]
    assert c.payer_claim_control_number == "CLM001"
    assert c.status_code == "1"
    assert c.status_label == "Processed as Primary"
    assert c.total_charge == 100.00
    assert c.total_paid == 85.00
    assert c.patient_responsibility == 15.00
    assert c.claim_filing_indicator == "MC"
    assert c.facility_type == "12"
    assert c.frequency_code == "1"


def test_parse_envelope_captured():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    assert result.envelope is not None
    assert result.envelope.control_number == "991102984"
    assert result.envelope.transaction_date == date(2026, 6, 17)
    assert result.envelope.sender_id == "COMEDASSISTPROG"
    assert result.envelope.implementation_guide == "005010X221A1"


def test_parse_financial_info():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    f = result.financial_info
    assert f.handling_code == "C"
    assert f.paid_amount == 85.00
    assert f.credit_debit_flag == "C"
    assert f.payment_method == "ACH"
    assert f.payer_tax_id == "81-1725341"
    assert f.payment_date == date(2026, 6, 17)


def test_parse_trace():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    t = result.trace
    assert t.trace_type_code == "1"
    # X12 standard: TRN02 = Reference Identification (trace number),
    # TRN03 = Originating Company Identifier. The CO guide labels
    # TRN03 as the Payer Tax ID, so for CO Medicaid 835s the
    # originating_company_id is "81-1725341" and the trace number is
    # the provider's TPID.
    assert t.trace_number == "TPID001"
    assert t.originating_company_id == "81-1725341"
    assert t.payer_id is None  # TRN04 is not present in this fixture


def test_parse_payer_and_payee():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    p = result.payer
    assert p.name == "CO_TXIX"
    assert p.id == "7912900843"
    assert p.contact_url == "hcpf.colorado.gov"
    assert p.address is not None
    assert p.address.city == "DENVER"
    pe = result.payee
    assert pe.name == "TOC, Inc."
    assert pe.npi == "1881068062"
    assert pe.address is not None
    assert pe.address.zip == "814014063"


def test_parse_claim_has_two_service_lines():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    c = result.claims[0]
    assert len(c.service_payments) == 2
    s1, s2 = c.service_payments
    assert s1.line_number == 1
    assert s1.procedure_code == "S5130"
    assert s1.procedure_qualifier == "HC"
    assert s1.modifiers == ["U2"]
    assert s1.payment == 50.00
    assert s1.service_date == date(2026, 6, 2)
    assert s2.line_number == 2
    assert s2.payment == 35.00
    assert s2.service_date == date(2026, 6, 3)


def test_parse_summary_counts():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    assert result.summary.total_claims == 1
    assert result.summary.input_file == ""
    # The orchestrator sets passed/failed from the validation report.
    # Without running validation it remains 0 — the CLI / API does that.
    assert result.summary.passed == 0


def test_parse_preserves_raw_segments():
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    c = result.claims[0]
    assert any(s[0] == "CLP" for s in c.raw_segments)
    assert any(s[0] == "SVC" for s in c.raw_segments)


def test_parse_uses_generic_config():
    """Generic config shouldn't suppress parser errors but accepts any payer."""
    result = parse(FIXTURE.read_text(), PayerConfig835.generic_835())
    assert len(result.claims) == 1


def test_parse_no_bpr_raises():
    text = (
        "ISA*00*          *00*          *ZZ*COMEDASSISTPROG*ZZ*TPID001        *260617*1937*^*00501*991102990*1*P*:~"
        "ST*835*991102990*005010X221A1~"
        "SE*1*991102990~"
        "GE*1*991102990~"
        "IEA*1*991102990~"
    )
    with pytest.raises(CycloneParseError, match="No BPR"):
        parse(text, PayerConfig835.co_medicaid_835())


def test_parse_service_payment_units_and_unit_type():
    """X12 005010X221A1: SVC04 = unit-of-measure, SVC05 = service unit count.

    Bug history: the parser previously read SVC04 as ``units`` and SVC05
    as ``unit_type`` (with the comment stating the opposite of the spec).
    On real-world 835s — and the canonical minimal fixture — this left
    ``units=None`` because SVC04 carries the string "UN" (which fails
    ``Decimal`` parsing) and SVC05 (a count like "7.73") got assigned to
    ``unit_type``.

    This test pins down the correct mapping so SP7's line-level matcher
    can compare units on the SVC side against the claim side.
    """
    from decimal import Decimal
    result = parse(FIXTURE.read_text(), PayerConfig835.co_medicaid_835())
    c = result.claims[0]
    s1, s2 = c.service_payments
    # minimal_835.txt fixture: SVC*HC:S5130:U2*60.00*50.00*UN*7.73*date*
    assert s1.units == Decimal("7.73"), s1.units
    assert s1.unit_type == "UN"
    assert s2.units == Decimal("7.73"), s2.units
    assert s2.unit_type == "UN"


def test_parse_service_payment_units_default_unit_type_to_un():
    """If SVC04 is empty but SVC05 carries a count, default unit_type to UN.

    Some payers omit the unit-of-measure code when the units are obvious
    (e.g. always "UN"). The parser should still parse the units and
    surface a reasonable default for ``unit_type`` so downstream
    consumers (validation, SP7 line matcher) have a non-None value.
    """
    from decimal import Decimal
    base = FIXTURE.read_text()
    text = (
        base
        .replace(
            "SVC*HC:S5130:U2*60.00*50.00*UN*7.73*20260602~",
            "SVC*HC:S5130:U2*60.00*50.00**3*20260602~",
        )
        .replace(
            "SVC*HC:S5130:U2*40.00*35.00*UN*7.73*20260603~",
            "SVC*HC:S5130:U2*40.00*35.00**4*20260603~",
        )
    )
    result = parse(text, PayerConfig835.co_medicaid_835())
    s1, s2 = result.claims[0].service_payments
    assert s1.units == Decimal("3"), s1.units
    assert s2.units == Decimal("4"), s2.units
    # Default to UN when SVC04 missing but units present.
    assert s1.unit_type == "UN", s1.unit_type
    assert s2.unit_type == "UN", s2.unit_type
