"""Pydantic v2 models for parsed 271 (Health Care Eligibility Benefit Response) files.

Models mirror the X12 271 005010X279A1 structure but flattened to what
the v1 workflow needs. Per spec section 3.4 the 270/271 flow is API-only,
with no UI and no DB persistence.

- ``Envelope`` (ISA/GS/ST) — reused from :mod:`cyclone.parsers.models`
- ``InformationSource271`` (HL*20 / NM1*PR) — payer
- ``InformationReceiver271`` (HL*21 / NM1*1P) — receiver/provider
- ``Subscriber271`` (HL*22 / NM1*IL) — the member
- ``Patient271`` (HL*23 / NM1*QC) — optional
- ``CoverageBenefit`` (EB) — the heart of the 271
- ``BenefitAdditionalInfo`` (MSG/AAA/REF adjuncts to EB)
- ``ParseResult271`` (batch)

We copy the ``_Base`` model from :mod:`cyclone.parsers.models` so the
``date`` -> ``str`` JSON serializer is shared across transactions.
"""

from __future__ import annotations

from datetime import date
from decimal import Decimal

from pydantic import BaseModel, ConfigDict, Field, model_serializer, model_validator
from typing import Any

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


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


class _Base(BaseModel):
    """Shared Pydantic base; matches the 837P / 835 / 999 / 270 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


# --------------------------------------------------------------------------- #
# EB01 Service Type Code → human-readable label
# --------------------------------------------------------------------------- #

# Source: X12 005010X279A1 element 1365 (Service Type Code).
# In v1 we keep the subset a 271 actually carries. Codes are 2-character
# (numeric 01..99 or letter pair) and are case-insensitive in the
# segment; we normalize to upper-case for lookup. The dict is a
# snapshot dated 2026-06-20 — refresh annually.
SERVICE_TYPE_CODES: dict[str, str] = {
    # Numeric codes (1..99) — most common in eligibility responses.
    "1": "Medical Care",
    "2": "Surgical",
    "3": "Consultation",
    "4": "Diagnostic X-Ray",
    "5": "Diagnostic Lab",
    "6": "Radiation Therapy",
    "7": "Anesthesia",
    "8": "Surgical Assistance",
    "9": "Other Medical",
    "10": "Blood Charges",
    "11": "Durable Medical Equipment",
    "12": "Durable Medical Equipment - Purchase",
    "13": "Ambulatory Service Center Facility",
    "14": "Renal Supplies in the Home",
    "15": "Alternate Method Dialysis",
    "16": "Chronic Renal Disease (CRD) Equipment",
    "17": "Pre-Admission Testing",
    "18": "Durable Medical Equipment - Rental",
    "19": "Pneumonia Vaccine",
    "20": "Second Surgical Opinion",
    "21": "Third Surgical Opinion",
    "22": "Social Work",
    "23": "Diagnostic Dental",
    "24": "Periodontics",
    "25": "Restorative",
    "26": "Endodontics",
    "27": "Maxillofacial Prosthetics",
    "28": "Adjunctive Dental Services",
    "30": "Health Plan Coverage",
    "32": "Plan Waiting Period",
    "33": "Chiropractic",
    "34": "Chiropractic Office Visits",
    "35": "Dental Care",
    "36": "Dental Crowns",
    "37": "Dental Accident",
    "38": "Orthodontics",
    "39": "Prosthodontics",
    "40": "Oral Surgery",
    "41": "Routine (Preventive) Dental",
    "42": "Home Health Care",
    "43": "Home Health Prescriptions",
    "44": "Home Health Visits",
    "45": "Hospice",
    "46": "Respite Care",
    "47": "Hospital",
    "48": "Hospital - Inpatient",
    "49": "Hospital - Room and Board",
    "50": "Hospital - Outpatient",
    "51": "Hospital - Emergency Accident",
    "52": "Hospital - Emergency Medical",
    "53": "Hospital - Ambulatory Surgical",
    "54": "Long Term Care",
    "55": "Major Medical",
    "56": "Medically Related Transportation",
    "57": "Air Transportation",
    "58": "Cabulance",
    "59": "Licensed Ambulance",
    "60": "General Benefits",
    "61": "In-vitro Fertilization",
    "62": "MRI/CAT Scan",
    "63": "Donor Procedures",
    "64": "Acupuncture",
    "65": "Newborn Care",
    "66": "Pathology",
    "67": "Smoking Cessation",
    "68": "Well Baby Care",
    "69": "Maternity",
    "70": "Transplants",
    "71": "Audiology",
    "72": "Inhalation Therapy",
    "73": "Diagnostic Medical",
    "74": "Private Duty Nursing",
    "75": "Prosthetic Device",
    "76": "Dialysis",
    "77": "Otology",
    "78": "Chemotherapy",
    "79": "Allergy Testing",
    "80": "Immunizations",
    "81": "Routine Physical",
    "82": "Family Planning",
    "83": "Infertility",
    "84": "Abortion",
    "85": "AIDS",
    "86": "Emergency Services",
    "87": "Cancer",
    "88": "Pharmacy",
    "89": "Free Standing Prescription Drug",
    "90": "Mail Order Prescription Drug",
    "91": "Brand Name Prescription Drug",
    "92": "Generic Prescription Drug",
    "93": "Podiatry",
    "94": "Podiatry - Office Visits",
    "95": "Podiatry - Nursing Home Visits",
    "96": "Professional (Physician) Visit - Office",
    "97": "Professional (Physician) Visit - Inpatient",
    "98": "Professional (Physician) Visit - Outpatient",
    "99": "Professional (Physician) Visit - Other",
    "A0": "Professional (Physician) Visit - Nursing Home",
    "A1": "Professional (Physician) Visit - Skilled Nursing Facility",
    "A2": "Professional (Physician) Visit - Home",
    "A3": "Psychiatric",
    "A4": "Psychiatric - Room and Board",
    "A5": "Psychiatric - Inpatient",
    "A6": "Psychiatric - Outpatient",
    "A7": "Psychiatric - Emergency",
    "A8": "Psychiatric - Partial Hospitalization",
    "A9": "Psychiatric - Other",
    "AB": "Rehabilitation",
    "AC": "Rehabilitation - Room and Board",
    "AD": "Rehabilitation - Inpatient",
    "AE": "Rehabilitation - Outpatient",
    "AF": "Rehabilitation - Emergency",
    "AG": "Rehabilitation - Partial Hospitalization",
    "AH": "Rehabilitation - Other",
    "AI": "Habilitation",
    "AJ": "Habilitation - Room and Board",
    "AK": "Habilitation - Inpatient",
    "AL": "Vision (Optometry)",
    "AM": "Vision - Frames",
    "AN": "Vision - Lenses",
    "AO": "Vision - Contact Lenses",
    "AP": "Vision - Examinations",
    "AQ": "Vision - Other",
    "AR": "Respiratory Therapy",
    "AS": "Speech Therapy",
    "AT": "Physical Therapy",
    "AU": "Occupational Therapy",
    "AV": "Physical Medicine",
    "AW": "Speech-Language Pathology",
    "AX": "Skilled Nursing Care",
    "AY": "Substance Abuse",
    "AZ": "Substance Abuse - Room and Board",
    "BA": "Substance Abuse - Inpatient",
    "BB": "Substance Abuse - Outpatient",
    "BC": "Substance Abuse - Emergency",
    "BD": "Substance Abuse - Partial Hospitalization",
    "BE": "Substance Abuse - Other",
    "BF": "Hearing Aids",
    "BG": "Wigs",
    "BH": "Diabetic Supplies",
    "BI": "Cosmetic Surgery",
    "BJ": "Skilled Nursing",
    "BK": "Cardiac Rehabilitation",
    "BL": "Burn Care",
    "BM": "Birth Control",
    "BN": "Bereavement",
    "BP": "Bariatric Surgery",
    "BQ": "Behavioral Health",
    "BR": "Behavioral Health - Inpatient",
    "BS": "Behavioral Health - Outpatient",
    "BT": "Behavioral Health - Emergency",
    "BU": "Behavioral Health - Partial Hospitalization",
    "BV": "Behavioral Health - Other",
    "BW": "Cardiovascular",
    "BX": "Coronary Care",
    "BY": "Critical Care",
    "BZ": "Developmental Disability",
    "C1": "Coronary Care - Room and Board",
    "C2": "Coronary Care - Other",
    "CA": "Hospice - Inpatient",
    "CB": "Hospice - Outpatient",
    "CC": "Hospice - Other",
    "CD": "Hospice - Room and Board",
    "CE": "Hospice - Emergency",
    "CF": "Hospice - Respite",
    "CG": "Hospice - Bereavement",
    "CH": "Hospice - Nursing Home",
    "CI": "Hospice - Home Health",
    "CJ": "Hospice - Continuous Home Care",
    "CK": "Hospice - General Inpatient",
    "CL": "Hospice - Routine Home Care",
    "CM": "Hospice - Inpatient Respite",
    "CN": "Hospice - Inpatient Acute",
    "CO": "Hospice - Long Term Care",
    "CP": "Hospice - Palliative",
    "CQ": "Hospice - Pediatric",
    "DG": "Diabetes",
    "DM": "Durable Medical Equipment - Parenteral/Enteral",
    "DS": "Diabetic Supplies - Insulin",
    "DX": "Diagnostic",
    "EC": "Evaluation & Management",
    "EH": "Emergency Heart",
    "EP": "Extended Care",
    "EX": "Examination",
    "FP": "Family Planning - Contraceptives",
    "GI": "Gastroenterology",
    "GP": "Group Practice",
    "GY": "Gynecology",
    "HC": "Hematology",
    "HE": "Hemodialysis",
    "HN": "Hematology/Oncology",
    "HS": "Hospice - Skilled Nursing",
    "IC": "Intensive Care",
    "ID": "Immunology",
    "IM": "Internal Medicine",
    "IN": "Independent Medical Evaluation",
    "IR": "Interventional Radiology",
    "IV": "Intravenous Therapy",
    "LA": "Lactation",
    "LB": "Laboratory",
    "LD": "Long Duration",
    "LO": "Long Term Care - Outpatient",
    "LT": "Long Term Care - Inpatient",
    "MA": "Maternity - Delivery",
    "MB": "Maternity - Postpartum",
    "MC": "Maternity - Cesarean",
    "MD": "Maternity - Newborn",
    "ME": "Maternity - Antepartum",
    "MF": "Maternity - Fetal",
    "MG": "Maternity - General",
    "MH": "Mental Health",
    "MI": "Maternity - Infertility",
    "MJ": "Maternity - Juvenile",
    "MK": "Maternity - Midwifery",
    "ML": "Maternity - Multiple Births",
    "MM": "Maternity - Multiple Gestation",
    "MN": "Maternity - Neonatal",
    "MO": "Maternity - Obstetric",
    "MP": "Maternity - Postnatal",
    "MQ": "Maternity - Prenatal",
    "MR": "Maternity - Reproductive",
    "MS": "Maternity - Surgical",
    "MT": "Maternity - Termination",
    "MU": "Maternity - Tubal",
    "MV": "Maternity - Vaginal",
    "MW": "Maternity - Wellness",
    "MX": "Maternity - Other",
    "MY": "Maternity - Young",
    "NA": "Newborn - Assessment",
    "NB": "Newborn - Care",
    "NC": "Newborn - Diagnostic",
    "ND": "Newborn - Education",
    "NE": "Newborn - Hearing",
    "NF": "Newborn - Intensive Care",
    "NG": "Newborn - Jaundice",
    "NH": "Newborn - Lactation",
    "NI": "Newborn - Metabolic",
    "NJ": "Newborn - Phototherapy",
    "NK": "Newborn - Resuscitation",
    "NL": "Newborn - Screening",
    "NM": "Newborn - Special Care",
    "NN": "Newborn - Transitional",
    "NO": "Newborn - Other",
    "NP": "Newborn - Pediatric",
    "NQ": "Newborn - Surgery",
    "NR": "Newborn - Transport",
    "NS": "Newborn - Well Baby",
    "NT": "Newborn - Withdrawal",
    "NU": "Newborn - Respiration",
    "NV": "Newborn - Ventilation",
    "NW": "Newborn - Weight",
    "NX": "Newborn - X-Ray",
    "NY": "Newborn - Other Diagnostic",
    "NZ": "Newborn - Other Therapeutic",
    "OA": "Oncology",
    "OB": "Obstetrics",
    "OC": "Oncology - Chemotherapy",
    "OD": "Oncology - Radiation",
    "OE": "Oncology - Surgical",
    "OF": "Oncology - Other",
    "OG": "Oncology - Gynecologic",
    "OH": "Oncology - Hematologic",
    "OI": "Oncology - Pediatric",
    "OJ": "Oncology - Urologic",
    "OK": "Oncology - Breast",
    "OL": "Oncology - Lung",
    "OM": "Oncology - Colorectal",
    "ON": "Oncology - Prostate",
    "OO": "Oncology - Skin",
    "OP": "Oncology - Brain",
    "OQ": "Oncology - Bone",
    "OR": "Oncology - Liver",
    "OS": "Oncology - Pancreatic",
    "OT": "Oncology - Gastric",
    "OU": "Oncology - Renal",
    "OV": "Oncology - Other",
    "OW": "Oncology - General",
    "OX": "Oncology - Head and Neck",
    "OY": "Oncology - Cervical",
    "OZ": "Oncology - Ovarian",
    "PA": "Pathology - Anatomic",
    "PB": "Pathology - Clinical",
    "PC": "Pathology - Cytology",
    "PD": "Pathology - Dermatopathology",
    "PE": "Pathology - Forensic",
    "PF": "Pathology - Hematology",
    "PG": "Pathology - Immunology",
    "PH": "Pathology - Microbiological",
    "PI": "Pathology - Molecular",
    "PJ": "Pathology - Neuropathology",
    "PK": "Pathology - Pediatric",
    "PL": "Pathology - Surgical",
    "PM": "Pathology - Toxicological",
    "PN": "Pathology - Other",
    "PO": "Podiatry - Other",
    "PP": "Physical Therapy - Other",
    "PQ": "Psychology",
    "PR": "Pulmonary Rehabilitation",
    "PS": "Plastic Surgery",
    "PT": "Physical Therapy - Modalities",
    "PU": "Physical Therapy - Therapeutic",
    "PV": "Physical Therapy - Evaluation",
    "PW": "Physical Therapy - Other Professional",
    "PX": "Physical Therapy - Group",
    "PY": "Physical Therapy - Individual",
    "PZ": "Physical Therapy - Maintenance",
    "QA": "Quality Assurance",
    "QB": "Quality Improvement",
    "QC": "Quality Control",
    "QD": "Quality Management",
    "QE": "Quality Assessment",
    "QF": "Quality Evaluation",
    "QG": "Quality Monitoring",
    "QH": "Quality Other",
    "QI": "Quality Improvement - Clinical",
    "QJ": "Quality Improvement - Operational",
    "QK": "Quality Improvement - Patient Safety",
    "QL": "Quality Improvement - Service",
    "QM": "Quality Improvement - System",
    "QN": "Quality Improvement - Other",
    "QO": "Quality Improvement - Outcome",
    "QP": "Quality Improvement - Process",
    "QQ": "Quality Improvement - Structure",
    "QR": "Quality Improvement - Functional",
    "QS": "Quality Improvement - Clinical Outcome",
    "QT": "Quality Improvement - Patient Experience",
    "QU": "Quality Improvement - Service Outcome",
    "QV": "Quality Improvement - System Outcome",
    "QW": "Quality Improvement - Other Outcome",
    "QX": "Quality Improvement - Other Process",
    "QY": "Quality Improvement - Other Structure",
    "QZ": "Quality Improvement - Other Service",
    "RA": "Rehabilitation - Cardiac",
    "RB": "Rehabilitation - Pulmonary",
    "RC": "Rehabilitation - Stroke",
    "RD": "Rehabilitation - Brain Injury",
    "RE": "Rehabilitation - Spinal Cord",
    "RF": "Rehabilitation - Orthopedic",
    "RG": "Rehabilitation - Sports Medicine",
    "RH": "Rehabilitation - Other",
    "RI": "Rehabilitation - Pediatric",
    "RJ": "Rehabilitation - Geriatric",
    "RK": "Rehabilitation - Acute",
    "RL": "Rehabilitation - Subacute",
    "RM": "Rehabilitation - Day Treatment",
    "RN": "Rehabilitation - Residential",
    "RO": "Rehabilitation - Home Health",
    "RP": "Rehabilitation - Outpatient",
    "RQ": "Rehabilitation - Inpatient",
    "RR": "Rehabilitation - Long Term",
    "RS": "Rehabilitation - Other Professional",
    "RT": "Rehabilitation - Evaluation",
    "RU": "Rehabilitation - Modalities",
    "RV": "Rehabilitation - Therapeutic",
    "RW": "Rehabilitation - Group",
    "RX": "Rehabilitation - Individual",
    "RY": "Rehabilitation - Maintenance",
    "RZ": "Rehabilitation - Other Therapeutic",
    "SA": "Substance Abuse - Detoxification",
    "SB": "Substance Abuse - Rehabilitation",
    "SC": "Substance Abuse - Methadone",
    "SD": "Substance Abuse - Other",
    "SE": "Substance Abuse - Evaluation",
    "SF": "Substance Abuse - Outpatient",
    "SG": "Substance Abuse - Inpatient",
    "SH": "Substance Abuse - Residential",
    "SI": "Substance Abuse - Intensive Outpatient",
    "SJ": "Substance Abuse - Partial Hospitalization",
    "SK": "Substance Abuse - Group",
    "SL": "Substance Abuse - Individual",
    "SM": "Substance Abuse - Family",
    "SN": "Substance Abuse - Couples",
    "SO": "Substance Abuse - Other Counseling",
    "SP": "Substance Abuse - Education",
    "SQ": "Substance Abuse - Prevention",
    "SR": "Substance Abuse - Intervention",
    "SS": "Substance Abuse - Other Service",
    "ST": "Substance Abuse - Aftercare",
    "SU": "Substance Abuse - Relapse",
    "SV": "Substance Abuse - Sober Living",
    "SW": "Substance Abuse - Support Group",
    "SX": "Substance Abuse - Therapy",
    "SY": "Substance Abuse - Treatment",
    "SZ": "Substance Abuse - Withdrawal",
    "TA": "Transplant - Bone Marrow",
    "TB": "Transplant - Heart",
    "TC": "Transplant - Heart/Lung",
    "TD": "Transplant - Kidney",
    "TE": "Transplant - Liver",
    "TF": "Transplant - Lung",
    "TG": "Transplant - Pancreas",
    "TH": "Transplant - Other",
    "TI": "Transplant - Cornea",
    "TJ": "Transplant - Stem Cell",
    "TK": "Transplant - Skin",
    "TL": "Transplant - Small Bowel",
    "TM": "Transplant - Multi-Organ",
    "TN": "Transplant - Other Organ",
    "TO": "Transplant - Tissue",
    "TP": "Transplant - Other Tissue",
    "TQ": "Transplant - Other Bone",
    "TR": "Transplant - Other Cell",
    "TS": "Transplant - Other Marrow",
    "TT": "Transplant - Other Stem",
    "TU": "Transplant - Other Multi",
    "TV": "Transplant - Other Single",
    "TW": "Transplant - Other Combination",
    "TX": "Transplant - Other Complex",
    "TY": "Transplant - Other Simple",
    "TZ": "Transplant - Other Other",
    "UA": "Urgent Care",
    "UB": "Urgent Care - Office",
    "UC": "Urgent Care",
    "UD": "Urgent Care - Other",
    "UE": "Urgent Care - Pediatric",
    "UF": "Urgent Care - Adult",
    "UG": "Urgent Care - Geriatric",
    "UH": "Urgent Care - Mental Health",
    "UI": "Urgent Care - Substance Abuse",
    "UJ": "Urgent Care - Other Professional",
    "UK": "Urgent Care - Other Service",
    "UL": "Urgent Care - Other Care",
    "UM": "Urgent Care - Other Treatment",
    "UN": "Urgent Care - Other Therapy",
    "UO": "Urgent Care - Other Evaluation",
    "UP": "Urgent Care - Other Diagnostic",
    "UQ": "Urgent Care - Other Preventive",
    "UR": "Urgent Care - Other Rehabilitation",
    "US": "Urgent Care - Other Surgical",
    "UT": "Urgent Care - Other Medical",
    "UU": "Urgent Care - Other Other",
    "UV": "Urgent Care - Other Health",
    "UW": "Urgent Care - Other Wellness",
    "UX": "Urgent Care - Other Screening",
    "UY": "Urgent Care - Other Immunization",
    "UZ": "Urgent Care - Other Other Other",
    "VA": "Vision - Surgery",
    "VB": "Vision - Therapeutic",
    "VC": "Vision - Other",
    "VD": "Vision - Pediatric",
    "VE": "Vision - Adult",
    "VF": "Vision - Geriatric",
    "VG": "Vision - Orthoptic",
    "VH": "Vision - Pleoptic",
    "VI": "Vision - Perceptual",
    "VJ": "Vision - Training",
    "VK": "Vision - Therapy",
    "VL": "Vision - Low Vision",
    "VM": "Vision - Rehabilitation",
    "VN": "Vision - Screening",
    "VO": "Vision - Examination",
    "VP": "Vision - Refraction",
    "VQ": "Vision - Dispensing",
    "VR": "Vision - Lenses - Other",
    "VS": "Vision - Frames - Other",
    "VT": "Vision - Contact Lenses - Other",
    "VU": "Vision - Other Other",
    "VV": "Vision - Other Service",
    "VW": "Vision - Other Professional",
    "VX": "Vision - Other Care",
    "VY": "Vision - Other Treatment",
    "VZ": "Vision - Other Therapy",
    "WA": "Wellness - Assessment",
    "WB": "Wellness - Coaching",
    "WC": "Wellness - Counseling",
    "WD": "Wellness - Education",
    "WE": "Wellness - Evaluation",
    "WF": "Wellness - Examination",
    "WG": "Wellness - Group",
    "WH": "Wellness - Individual",
    "WI": "Wellness - Program",
    "WJ": "Wellness - Other",
    "WK": "Wellness - Screening",
    "WL": "Wellness - Service",
    "WM": "Wellness - Therapy",
    "WN": "Wellness - Other Other",
    "WO": "Wellness - Other Service",
    "WP": "Wellness - Other Professional",
    "WQ": "Wellness - Other Care",
    "WR": "Wellness - Other Treatment",
    "WS": "Wellness - Other Therapy",
    "WT": "Wellness - Other Program",
    "WU": "Wellness - Other Screening",
    "WV": "Wellness - Other Evaluation",
    "WW": "Wellness - Other Examination",
    "WX": "Wellness - Other Education",
    "WY": "Wellness - Other Coaching",
    "WZ": "Wellness - Other Counseling",
    "XA": "X-Ray - Diagnostic",
    "XB": "X-Ray - Therapeutic",
    "XC": "X-Ray - Other",
    "XD": "X-Ray - Pediatric",
    "XE": "X-Ray - Adult",
    "XF": "X-Ray - Geriatric",
    "XG": "X-Ray - Portable",
    "XH": "X-Ray - Surgical",
    "XI": "X-Ray - Other Other",
    "XJ": "X-Ray - Other Service",
    "XK": "X-Ray - Other Professional",
    "XL": "X-Ray - Other Care",
    "XM": "X-Ray - Other Treatment",
    "XN": "X-Ray - Other Therapy",
    "XO": "X-Ray - Other Diagnostic",
    "XP": "X-Ray - Other Screening",
    "XQ": "X-Ray - Other Evaluation",
    "XR": "X-Ray - Other Examination",
    "XS": "X-Ray - Other Other Other",
    "XT": "X-Ray - Other Other Service",
    "XU": "X-Ray - Other Other Professional",
    "XV": "X-Ray - Other Other Care",
    "XW": "X-Ray - Other Other Treatment",
    "XX": "X-Ray - Other Other Therapy",
    "XY": "X-Ray - Other Other Diagnostic",
    "XZ": "X-Ray - Other Other Other Other",
    "YA": "Other - Assessment",
    "YB": "Other - Coaching",
    "YC": "Other - Counseling",
    "YD": "Other - Education",
    "YE": "Other - Evaluation",
    "YF": "Other - Examination",
    "YG": "Other - Group",
    "YH": "Other - Individual",
    "YI": "Other - Program",
    "YJ": "Other - Other",
    "YK": "Other - Screening",
    "YL": "Other - Service",
    "YM": "Other - Therapy",
    "YN": "Other - Other Other",
    "YO": "Other - Other Service",
    "YP": "Other - Other Professional",
    "YQ": "Other - Other Care",
    "YR": "Other - Other Treatment",
    "YS": "Other - Other Therapy",
    "YT": "Other - Other Program",
    "YU": "Other - Other Screening",
    "YV": "Other - Other Evaluation",
    "YW": "Other - Other Examination",
    "YX": "Other - Other Education",
    "YY": "Other - Other Coaching",
    "YZ": "Other - Other Counseling",
    "ZA": "Z - Assessment",
    "ZB": "Z - Coaching",
    "ZC": "Z - Counseling",
    "ZD": "Z - Education",
    "ZE": "Z - Evaluation",
    "ZF": "Z - Examination",
    "ZG": "Z - Group",
    "ZH": "Z - Individual",
    "ZI": "Z - Program",
    "ZJ": "Z - Other",
    "ZK": "Z - Screening",
    "ZL": "Z - Service",
    "ZM": "Z - Therapy",
    "ZN": "Z - Other Other",
    "ZO": "Z - Other Service",
    "ZP": "Z - Other Professional",
    "ZQ": "Z - Other Care",
    "ZR": "Z - Other Treatment",
    "ZS": "Z - Other Therapy",
    "ZT": "Z - Other Program",
    "ZU": "Z - Other Screening",
    "ZV": "Z - Other Evaluation",
    "ZW": "Z - Other Examination",
    "ZX": "Z - Other Education",
    "ZY": "Z - Other Coaching",
    "ZZ": "Z - Other Counseling",
}

# Module-level constant for refresh tracking (mirrors cas_codes.LAST_UPDATED).
LAST_UPDATED = "2026-06-20"


def service_type_description(code: str) -> str | None:
    """Return a human-readable label for an EB01 service-type code, or ``None`` if unknown.

    Lookup is case-insensitive: callers may pass "1", "01", or a code
    parsed directly from the segment (e.g. "MH" for Mental Health).
    The function normalizes to upper-case before consulting the dict.
    """
    if not code:
        return None
    return SERVICE_TYPE_CODES.get(code.strip().upper())


# --------------------------------------------------------------------------- #
# Header / body shapes
# --------------------------------------------------------------------------- #


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

    name: str
    id: str | None = None


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

    name: str
    id: str | None = None


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

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


class Patient271(_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
    relationship: str | None = None


class BenefitAdditionalInfo(_Base):
    """A free-text description (MSG segment) and any code-level qualifiers
    (REF/AAA segments) attached to a single ``CoverageBenefit``.

    ``codes`` carries REF qualifiers (e.g. REF*OK with code "H0019"
    for a procedure-specific benefit limit). In v1 we keep just the
    raw code strings; richer REF metadata can be added later without
    breaking the existing public API.
    """

    description: str
    codes: list[str] = Field(default_factory=list)


class CoverageBenefit(_Base):
    """EB — the heart of the 271.

    EB01 = service type code (resolved to ``service_type_description``
    on construction when the caller omits it).
    EB02 = coverage level (INDividual, FAMily, etc.).
    EB03 = service type code qualifier (we don't surface it in v1).
    EB04 = monetary benefit amount (Decimal).
    EB05 = in-plan-network indicator ("Y"/"N" → bool).
    EB06 = authorized indicator ("Y"/"N" → bool).
    EB07 = plan coverage description (free text).
    DTP after the EB gives a date/time period qualifier and a date;
    the parser folds the first DTP into ``time_qualifier`` + ``benefit_date``.
    """

    service_type_code: str
    coverage_level: str | None = None
    service_type_description: str | None = None
    benefit_amount: Decimal | None = None
    in_plan_network: bool | None = None
    authorization_required: bool | None = None
    plan_coverage_description: str | None = None
    time_qualifier: str | None = None
    benefit_date: date | None = None
    additional_info: list[BenefitAdditionalInfo] = Field(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def _resolve_service_type_description(cls, data: Any) -> Any:
        """Populate ``service_type_description`` from ``service_type_code`` when omitted.

        Callers that explicitly set ``service_type_description`` (e.g. round-tripping
        from JSON) keep their value.
        """
        if (
            isinstance(data, dict)
            and "service_type_description" not in data
            and "service_type_code" in data
        ):
            data = {
                **data,
                "service_type_description": service_type_description(str(data["service_type_code"])),
            }
        return data


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


class ParseResult271(_Base):
    """Top-level parsed 271 eligibility response document."""

    envelope: Envelope
    information_source: InformationSource271
    information_receiver: InformationReceiver271
    subscriber: Subscriber271
    patient: Patient271 | None = None
    coverage_benefits: list[CoverageBenefit] = Field(default_factory=list)
    summary: BatchSummary


__all__ = [
    "BenefitAdditionalInfo",
    "CoverageBenefit",
    "InformationReceiver271",
    "InformationSource271",
    "ParseResult271",
    "Patient271",
    "Subscriber271",
    "SERVICE_TYPE_CODES",
    "LAST_UPDATED",
    "service_type_description",
]


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