"""Pydantic models for providers, payers, payer configs, and clearhouse config.

SP9 — multi-payer, multi-NPI, SFTP stub. See spec:
docs/superpowers/specs/2026-06-20-cyclone-multi-payer-npi-sftp-design.md

These models are the in-memory representation of the rows in:
- providers
- payers
- payer_configs
- clearhouse

The DB ORM classes live in `cyclone.db`. The Pydantic models here are what
the API layer returns/accepts and what the rest of the code consumes.
"""

from __future__ import annotations

from datetime import datetime
from typing import Literal

from pydantic import BaseModel, ConfigDict, Field


# ---------------------------------------------------------------------------
# Provider (one per NPI)
# ---------------------------------------------------------------------------


class Provider(BaseModel):
    model_config = ConfigDict(extra="ignore")

    npi: str = Field(min_length=10, max_length=10, pattern=r"^\d{10}$")
    label: str
    legal_name: str
    tax_id: str
    taxonomy_code: str
    address_line1: str
    address_line2: str | None = None
    city: str
    state: str = Field(min_length=2, max_length=2)
    zip: str
    is_active: bool = True
    created_at: datetime
    updated_at: datetime


# ---------------------------------------------------------------------------
# Payer (per-payer identity)
# ---------------------------------------------------------------------------


class Payer(BaseModel):
    model_config = ConfigDict(extra="ignore")

    payer_id: str
    name: str
    receiver_name: str
    receiver_id: str
    is_active: bool = True
    created_at: datetime
    updated_at: datetime


# ---------------------------------------------------------------------------
# Payer config (per-payer, per-transaction-type)
# ---------------------------------------------------------------------------


class PayerConfig837(BaseModel):
    """Per-payer, per-837-transaction-type configuration block."""

    model_config = ConfigDict(extra="ignore")

    submitter_name: str
    submitter_contact_name: str
    submitter_contact_email: str
    receiver_name: str
    receiver_id_qualifier: str = "46"
    receiver_id: str
    bht06_allowed: list[str]
    bht06_default: str
    sbr09_default: str
    sbr09_allowed: list[str]
    payer_id_qualifier: str = "PI"
    payer_id: str
    pwk_supported: bool = False
    cas_2320_group_allowed: bool = False
    claim_type_codes: dict[str, str] = Field(default_factory=dict)


class PayerConfig835(BaseModel):
    """Per-payer, per-835-transaction-type configuration block."""

    model_config = ConfigDict(extra="ignore")

    expected_payer_tax_ids: list[str] = Field(default_factory=list)
    expected_payer_health_plan_id: str = ""
    payer_name_pattern: str = ""


class PayerConfig277CA(BaseModel):
    """Per-payer, per-277CA-transaction-type configuration block.

    Carries the status-code classification sets the parser and Inbox
    lane logic consult. HCPF publishes the canonical STC category codes
    (A1-A9 for adjudication states, P1-P5 for paid states); this block
    lets the operator override the classification per-payer without
    touching code.
    """

    model_config = ConfigDict(extra="ignore")

    rejected_status_codes: list[str] = Field(default_factory=lambda: ["A4", "A6", "A7"])
    pended_status_codes: list[str] = Field(default_factory=lambda: ["A8", "A9"])
    accepted_status_codes: list[str] = Field(default_factory=lambda: ["A1", "A2", "A3"])
    paid_status_codes: list[str] = Field(default_factory=lambda: ["P1", "P2", "P3", "P4", "P5"])
    # HCPF uses ST*277CA but the X12 spec also allows ST*277. Both accepted.
    transaction_set_ids_allowed: list[str] = Field(default_factory=lambda: ["277", "277CA"])
    implementation_guide: str = "005010X214"


class PayerConfigRow(BaseModel):
    """One row in the payer_configs table.

    Wraps the per-tx config with metadata.
    """

    model_config = ConfigDict(extra="ignore")

    payer_id: str
    transaction_type: Literal["837P", "835", "277CA", "999", "TA1", "270", "271"]
    config_json: dict
    updated_at: datetime


# ---------------------------------------------------------------------------
# Clearhouse (singleton)
# ---------------------------------------------------------------------------


class FilenameBlock(BaseModel):
    model_config = ConfigDict(extra="ignore")

    tz: str = "America/Denver"
    outbound_template: str
    inbound_template: str


class SftpBlock(BaseModel):
    model_config = ConfigDict(extra="ignore")

    host: str
    port: int = 22
    username: str
    paths: dict[str, str]
    stub: bool = True
    staging_dir: str = "./var/sftp/staging"
    poll_seconds: int = 300
    auth: dict[str, str] = Field(default_factory=dict)


class Clearhouse(BaseModel):
    model_config = ConfigDict(extra="ignore")

    id: Literal[1] = 1
    name: str
    tpid: str
    submitter_name: str
    submitter_id_qual: str = "46"
    submitter_contact_name: str
    submitter_contact_email: str
    filename_block: FilenameBlock
    sftp_block: SftpBlock
    updated_at: datetime


# ---------------------------------------------------------------------------
# Inbound filename parsing result
# ---------------------------------------------------------------------------


class InboundFilename(BaseModel):
    """Parsed HCPF inbound filename per the X12 File Naming Standards Quick Guide.

    Example: TP11525703-837P_M019048402-20260520231513488-1of1_999.x12
    → tpid='11525703', orig_tx='837P', tracking='M019048402',
      ts='20260520231513488', file_type='999'
    """

    model_config = ConfigDict(extra="ignore")

    tpid: str
    orig_tx: str
    tracking: str
    ts: str
    file_type: str
    ext: str
