"""Pipeline A: denied/partial → frequency-7 replacement 837Ps.

For each visit the previous adjudication said was DENIED or PARTIAL,
emit a fresh 837P with CLM05-3 = '7' (replacement claim) and the
original claim_submit_id preserved as CLM01. The 837P carries the
original DOS and the same procedure / member.

The CARC-aware filter is the gate — only REBILL / REVIEW decisions are
emitted; EXCLUDED visits never make it to a file.
"""
from dataclasses import dataclass
from datetime import date
from decimal import Decimal
from pathlib import Path

from cyclone.rebill.carc_filter import CarcDecision
from cyclone.rebill.reconcile import ReconcileOutcome, OutcomeCategory


@dataclass(frozen=True)
class RebillClaim:
    """Pre-emission 837P shape for Pipeline A. The orchestrator's serializer
    adapter converts this to a ClaimOutput for serialize_837.
    """

    claim_id: str           # reuses original claim_submit_id
    member_id: str
    procedure: str
    svc_date: date
    charge: Decimal
    frequency_code: str     # always "7" for Pipeline A
    needs_review: bool      # True for CARC REVIEW decisions
    original_carc_reasons: tuple[str, ...]


def build_pipeline_a_claims(
    original_claim_id: str,
    visit_outcomes: list[ReconcileOutcome],
    carc_decisions: list[CarcDecision],
    cas_reasons_per_visit: list[tuple[str, ...]],
) -> list[RebillClaim]:
    """Zip three parallel per-visit lists by index; drop PAID/NOT_IN_835 outcomes and EXCLUDED CARC decisions; emit one frequency-7 RebillClaim per survivor."""
    out: list[RebillClaim] = []
    # strict=True: all three lists must be the same length; a mismatch is a contract bug, fail loud.
    for vo, carc, reasons in zip(visit_outcomes, carc_decisions, cas_reasons_per_visit, strict=True):
        if vo.category not in (OutcomeCategory.DENIED, OutcomeCategory.PARTIAL):
            continue
        if carc == CarcDecision.EXCLUDED:
            continue
        out.append(RebillClaim(
            claim_id=original_claim_id,
            member_id=vo.visit.member_id,
            procedure=vo.visit.procedure,
            svc_date=vo.visit.date,
            charge=vo.visit.billed,
            frequency_code="7",
            needs_review=(carc == CarcDecision.REVIEW),
            original_carc_reasons=reasons,
        ))
    return out


def write_pipeline_a_files(
    claims: list[RebillClaim],
    out_dir: Path,
    serialize_837_fn,  # injected: callable[[RebillClaim], str] (orchestrator-supplied adapter)
    tpid: str,
) -> list[Path]:
    """Write one 837P per claim, using HCPF-spec filenames via build_outbound_filename.

    `serialize_837_fn(claim)` returns the X12 string. The 837P envelope is
    pure ASCII so we write it as text. The filename is
    `cyclone.edi.filenames.build_outbound_filename(tpid, '837P')`.
    """
    # Lazy: keep cyclone.rebill.pipeline_a importable without zoneinfo from filenames.
    from cyclone.edi.filenames import build_outbound_filename

    out_dir.mkdir(parents=True, exist_ok=True)
    paths: list[Path] = []
    for c in claims:
        body = serialize_837_fn(c)
        fname = build_outbound_filename(tpid, "837P")
        path = out_dir / fname
        path.write_text(body, encoding="ascii")
        paths.append(path)
    return paths
