"""Pydantic v2 models for parsed 270 (Health Care Eligibility Benefit Inquiry) files.

Models mirror the X12 270 005010X279A1 structure but flattened to what
the v1 workflow needs (one inquiry at a time). Per spec section 3.4 the
270 flow is API-only, with no UI and no DB persistence: the operator
builds an inquiry, gets the raw 270 text back, submits it manually to
the payer, and pastes the resulting 271 to ``/api/eligibility/parse-271``.

- ``Envelope`` (ISA/GS/ST) — reused from :mod:`cyclone.parsers.models`
- ``InformationSource270`` (HL*20 / NM1*PR) — payer
- ``InformationReceiver270`` (HL*21 / NM1*1P) — receiver/provider
- ``Subscriber270`` (HL*22 / NM1*IL) — the member
- ``Patient270`` (HL*23 / NM1*QC) — optional, when the patient differs from the subscriber
- ``EligibilityBenefitInquiry`` (EQ)
- ``ParseResult270`` (batch)

We copy the ``_Base`` model from :mod:`cyclone.parsers.models` so the
``date`` -> ``str`` JSON serializer is shared across transactions. That
keeps FastAPI responses consistent across transaction sets.
"""

from __future__ import annotations

from datetime import date

from pydantic import BaseModel, ConfigDict, Field, model_serializer

from cyclone.parsers.models import BatchSummary, Envelope, ValidationIssue, ValidationReport


# --------------------------------------------------------------------------- #
# Shared base
# --------------------------------------------------------------------------- #


class _Base(BaseModel):
    """Shared Pydantic base; matches the 837P / 835 / 999 models for JSON consistency."""

    model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)

    @model_serializer(mode="wrap")
    def _serialize(self, handler):  # type: ignore[no-untyped-def]
        data = handler(self)
        for key, value in data.items():
            if isinstance(value, date):
                data[key] = value.isoformat()
        return data


# --------------------------------------------------------------------------- #
# 270 header / body shapes
# --------------------------------------------------------------------------- #


class InformationSource270(_Base):
    """HL*20 + NM1*PR — the payer (information source)."""

    name: str
    id: str | None = None


class InformationReceiver270(_Base):
    """HL*21 + NM1*1P — the information receiver (typically the provider)."""

    name: str
    id: str | None = None
    npi: str | None = None


class Subscriber270(_Base):
    """HL*22 + NM1*IL + DMG — the subscriber / insured party."""

    member_id: str
    first_name: str | None = None
    last_name: str | None = None
    dob: date | None = None


class Patient270(_Base):
    """HL*23 + NM1*QC — the patient (optional; only present when the patient
    differs from the subscriber, e.g. a dependent).
    """

    first_name: str | None = None
    last_name: str | None = None
    member_id: str | None = None
    dob: date | None = None
    relationship: str | None = None


class EligibilityBenefitInquiry(_Base):
    """EQ — one eligibility/benefit inquiry.

    EQ01 carries the service-type code (e.g. "1" for Medical Care,
    "30" for Health Plan Coverage). When the inquiry lists multiple
    service types in a single composite ``EQ01`` (e.g. ``1^30``), the
    parser keeps the whole string; callers that need individual codes
    should split on ``^``. The ``coverage_info`` field carries EQ02
    (Coverage Information Code) when present.
    """

    service_type_code: str
    coverage_info: str | None = None


# --------------------------------------------------------------------------- #
# Top-level result
# --------------------------------------------------------------------------- #


class ParseResult270(_Base):
    """Top-level parsed 270 eligibility inquiry document.

    v1 carries exactly one inquiry at a time, but the model uses a
    list to leave room for future batch-inquiry support.
    """

    envelope: Envelope
    information_source: InformationSource270
    information_receiver: InformationReceiver270
    subscriber: Subscriber270
    patient: Patient270 | None = None
    inquiries: list[EligibilityBenefitInquiry] = Field(default_factory=list)
    summary: BatchSummary


__all__ = [
    "EligibilityBenefitInquiry",
    "InformationReceiver270",
    "InformationSource270",
    "ParseResult270",
    "Patient270",
    "Subscriber270",
]


# Silence the "imported but unused" complaint on names that exist for
# parity with the other model modules' public surface but are not
# referenced in this file's implementation.
_ = (BaseModel, ValidationIssue, ValidationReport)
