"""Patient profile model for hemodialysis simulation.

Provides realistic patient parameter generation based on published
anthropometric and clinical data for hemodialysis populations.
"""

from dataclasses import dataclass, field

import numpy as np


# Literature-backed parameter ranges
PARAMETER_RANGES = {
    "urea_generation_rate": {
        "min": 6.0, "max": 10.0, "unit": "mg/min",
        "default": 8.0, "source": "Gotch and Sargent, 1985",
    },
    "blood_flow_rate": {
        "min": 200, "max": 500, "unit": "mL/min",
        "default": 350, "source": "Clinical standard",
    },
    "dialysate_flow_rate": {
        "min": 300, "max": 800, "unit": "mL/min",
        "default": 500, "source": "Clinical standard",
    },
    "koa_urea": {
        "min": 500, "max": 1200, "unit": "mL/min",
        "default": 800, "source": "Dialyzer manufacturer specifications",
    },
    "urea_distribution_volume_fraction": {
        "min": 0.55, "max": 0.65, "unit": "fraction of body_weight(kg) -> L",
        "default": 0.58, "source": "Watson formula",
    },
    "intercompartment_transfer_coeff": {
        "min": 500, "max": 1000, "unit": "mL/min",
        "default": 800, "source": "Smye et al., 1997",
    },
    "serum_phosphate_normal_min": {
        "min": 3.5, "max": 3.5, "unit": "mg/dL",
        "default": 3.5, "source": "Locatelli et al., 2002",
    },
    "serum_phosphate_normal_max": {
        "min": 5.5, "max": 5.5, "unit": "mg/dL",
        "default": 5.5, "source": "Locatelli et al., 2002",
    },
    "target_kt_v": {
        "min": 1.2, "max": 2.0, "unit": "dimensionless",
        "default": 1.4, "source": "KDOQI guidelines",
    },
    "max_uf_rate_per_kg": {
        "min": 10, "max": 13, "unit": "mL/kg/hr",
        "default": 10, "source": "Flythe et al., 2011",
    },
    "hypotension_threshold": {
        "min": 85, "max": 95, "unit": "mmHg systolic",
        "default": 90, "source": "Clinical standard",
    },
}


def validate_parameter(name: str, value: float) -> None:
    """Validate a parameter against its literature-backed range."""
    if name not in PARAMETER_RANGES:
        return
    p = PARAMETER_RANGES[name]
    if value < p["min"] or value > p["max"]:
        raise ValueError(
            f"Parameter '{name}' = {value} outside range "
            f"[{p['min']}, {p['max']}] {p['unit']} (source: {p['source']})"
        )


@dataclass
class PatientProfile:
    """Patient profile with clinical parameters for hemodialysis simulation.

    All parameters are validated against published literature ranges.
    """

    body_weight: float = 75.0  # kg
    age: int = 60
    is_diabetic: bool = False

    # Urea kinetics
    urea_generation_rate: float = 8.0  # mg/min
    pre_dialysis_bun: float = 70.0  # mg/dL
    urea_distribution_volume_fraction: float = 0.58

    # Dialyzer
    koa_urea: float = 800.0  # mL/min
    intercompartment_transfer_coeff: float = 800.0  # mL/min

    # Cardiovascular
    baseline_systolic_bp: float = 140.0  # mmHg
    baseline_diastolic_bp: float = 80.0  # mmHg
    baseline_heart_rate: float = 75.0  # bpm
    plasma_refill_coefficient: float = 0.004  # fraction/min

    # Fluid
    target_fluid_removal: float = 3.0  # L
    dry_weight: float = 72.0  # kg

    # Phosphate
    pre_dialysis_phosphate: float = 5.5  # mg/dL
    dietary_phosphate: float = 1200.0  # mg/day

    # Derived properties
    noise_std: float = 0.0  # observation noise (0 = deterministic)

    _validated: bool = field(default=False, repr=False)

    def __post_init__(self):
        if not self._validated:
            self._validate()
            self._validated = True

    def _validate(self):
        validate_parameter("urea_generation_rate", self.urea_generation_rate)
        validate_parameter("koa_urea", self.koa_urea)
        validate_parameter(
            "urea_distribution_volume_fraction",
            self.urea_distribution_volume_fraction,
        )
        validate_parameter(
            "intercompartment_transfer_coeff",
            self.intercompartment_transfer_coeff,
        )

    @property
    def total_body_water(self) -> float:
        """Total body water in liters (Watson formula approximation)."""
        return self.body_weight * self.urea_distribution_volume_fraction

    @property
    def ecf_volume(self) -> float:
        """Extracellular fluid volume (1/3 of TBW)."""
        return self.total_body_water / 3.0

    @property
    def icf_volume(self) -> float:
        """Intracellular fluid volume (2/3 of TBW)."""
        return 2.0 * self.total_body_water / 3.0

    @property
    def max_uf_rate(self) -> float:
        """Maximum safe ultrafiltration rate in mL/hr."""
        return PARAMETER_RANGES["max_uf_rate_per_kg"]["default"] * self.body_weight


def create_patient(
    difficulty: str = "easy",
    rng: np.random.Generator | None = None,
) -> PatientProfile:
    """Create a patient profile for the given difficulty tier.

    Args:
        difficulty: One of 'easy', 'medium', 'hard'.
        rng: Random number generator for stochastic patient variation.

    Returns:
        PatientProfile with parameters appropriate for difficulty.
    """
    if rng is None:
        rng = np.random.default_rng()

    if difficulty == "easy":
        return PatientProfile(
            body_weight=75.0,
            age=50,
            is_diabetic=False,
            baseline_systolic_bp=140.0,
            plasma_refill_coefficient=0.005,
            target_fluid_removal=2.5,
            noise_std=0.0,
        )
    elif difficulty == "medium":
        weight = rng.uniform(60, 95)
        return PatientProfile(
            body_weight=weight,
            age=rng.integers(55, 75),
            is_diabetic=False,
            baseline_systolic_bp=rng.uniform(130, 155),
            plasma_refill_coefficient=0.004,
            target_fluid_removal=rng.uniform(2.0, 3.5),
            dry_weight=weight - rng.uniform(2.0, 3.5),
            noise_std=0.02,
        )
    elif difficulty == "hard":
        weight = rng.uniform(55, 100)
        return PatientProfile(
            body_weight=weight,
            age=rng.integers(65, 85),
            is_diabetic=True,
            baseline_systolic_bp=rng.uniform(120, 160),
            # SIMPLIFICATION: Diabetic autonomic neuropathy modeled as reduced
            # plasma refill rate. Clinical presentation is more complex,
            # involving impaired baroreceptor sensitivity.
            plasma_refill_coefficient=0.002,
            target_fluid_removal=rng.uniform(2.5, 4.5),
            dry_weight=weight - rng.uniform(2.5, 4.5),
            noise_std=0.05,
        )
    else:
        raise ValueError(f"Unknown difficulty: {difficulty}. Use 'easy', 'medium', or 'hard'.")
