"""Pydantic models for the interpret service.

The canonical source is contracts/dist/schema.json (TypeScript Zod). We
DON'T regenerate Pydantic models from it at runtime — instead, a developer
runs `scripts/regenerate.sh` (or the datamodel-codegen invocation in the
README) after schema changes. For Phase 1 we work with a thin Pydantic
hand-written model that mirrors the Zod schema enough to validate output.

Keeping this hand-written means we own the Python shape; if the codegen
ever drifts or fails we still have a working service.
"""

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field, ConfigDict


class _Strict(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=False)


Confidence = Literal["low", "medium", "high"]
ImageQuality = Literal["good", "marginal", "poor"]
Lead = Literal[
    "I", "II", "III", "aVR", "aVL", "aVF",
    "V1", "V2", "V3", "V4", "V5", "V6",
    "rhythm_strip",
]


class Measurement(_Strict):
    value: float
    unit: str
    normal_range: tuple[float, float] | None = None
    interpretation: Literal["normal", "borderline", "abnormal"]


class _Section(_Strict):
    finding: str = Field(min_length=1, max_length=500)
    reasoning: str = Field(min_length=1, max_length=2000)
    confidence: Confidence
    leads_to_examine: list[Lead]
    teaching_point: str = Field(min_length=1, max_length=1000)


class RateSection(_Section):
    measurement: Measurement
    atrial_rate: Measurement | None = None


class RhythmSection(_Section):
    diagnosis: str
    regular: bool | None


AxisCategory = Literal["normal", "left_deviation", "right_deviation", "extreme", "indeterminate"]


class AxisSection(_Section):
    measurement: Measurement
    category: AxisCategory


class IntervalSection(_Section):
    measurement: Measurement


CorrectionFormula = Literal["bazett", "fridericia", "framingham"]


class QTcSection(_Section):
    measurement: Measurement
    correction_formula: CorrectionFormula


class IntervalsSection(_Strict):
    pr: IntervalSection
    qrs: IntervalSection
    qt: IntervalSection
    qtc: QTcSection


class PWaveSection(_Section):
    abnormalities: list[str]


BundleBranchBlock = Literal[
    "none", "rbbb", "lbbb", "incomplete_rbbb", "incomplete_lbbb", "nonspecific_ivcd"
]
FascicularBlock = Literal["none", "lafb", "lpfb", "bifascicular"]
InfarctTerritory = Literal[
    "anterior", "inferior", "lateral", "septal", "posterior", "none"
]


class PathologicQWaves(_Strict):
    present: bool
    leads: list[Lead]
    territory: InfarctTerritory | None


class QRSMorphologySection(_Section):
    bundle_branch_block: BundleBranchBlock
    fascicular_block: FascicularBlock
    pathologic_q_waves: PathologicQWaves


STMorphology = Literal["concave", "convex", "horizontal"]
STDepressionMorphology = Literal["horizontal", "downsloping", "upsloping"]


class STElevation(_Strict):
    present: bool
    leads: list[Lead]
    magnitude_mm: float | None = None
    morphology: STMorphology | None = None
    territory: str | None = None


class STDepression(_Strict):
    present: bool
    leads: list[Lead]
    morphology: STDepressionMorphology | None = None


class TWaveChanges(_Strict):
    inverted_leads: list[Lead]
    peaked: bool
    flat: bool


class STTSection(_Section):
    st_elevation: STElevation
    st_depression: STDepression
    t_wave_changes: TWaveChanges


class ChamberSection(_Section):
    lvh: bool
    rvh: bool
    la_enlargement: bool
    ra_enlargement: bool
    criteria_met: list[str]


AVBlock = Literal[
    "none", "first_degree", "mobitz_i", "mobitz_ii", "high_grade", "complete"
]


class ConductionSection(_Section):
    av_block: AVBlock
    preexcitation: bool
    paced: bool


class FinalSynthesis(_Strict):
    one_line_summary: str = Field(min_length=1, max_length=200)
    primary_diagnosis: str
    secondary_findings: list[str]
    differential: list[str]
    clinical_significance_education: str
    overall_confidence: Confidence


class CannotInterpret(_Strict):
    reason: str


class Interpretation(_Strict):
    schema_version: Literal["1.0"]
    image_quality: ImageQuality
    image_quality_notes: str | None = None
    rate: RateSection
    rhythm: RhythmSection
    axis: AxisSection
    intervals: IntervalsSection
    p_wave_morphology: PWaveSection
    qrs_morphology: QRSMorphologySection
    st_t_changes: STTSection
    chamber_enlargement: ChamberSection
    conduction_abnormalities: ConductionSection
    final_synthesis: FinalSynthesis
    teaching_pearls: list[str] = Field(min_length=1, max_length=5)
    difficulty_estimate: int = Field(ge=1, le=5)
    concepts_illustrated: list[str]
    cannot_interpret: CannotInterpret | None = None
