"""Cardiovascular response model for hemodialysis simulation.

Models blood pressure, heart rate, and plasma refilling dynamics during
ultrafiltration. Includes simplified baroreceptor reflex response.

References:
    Ursino M, Innocenti M. Artif Organs. 1997;21(8):891-902.
    Flythe JE, et al. Kidney Int. 2011;79(2):250-257.
    Azar AT. Modelling and Control of Dialysis Systems. Springer; 2013.
"""

import numpy as np


class CardiovascularModel:
    """Cardiovascular model for intradialytic hemodynamic simulation.

    SIMPLIFICATION: Uses a lumped-parameter cardiovascular model with
    algebraic baroreceptor response rather than the full Ursino pulsatile
    model with cardiac contractility, venous tone, and arterial compliance.
    The Ursino model uses 15+ ODEs; this model captures the dominant
    volume-pressure relationship with 3 state variables. Acceptable for
    benchmark simulation of ultrafiltration-induced hemodynamic changes.
    """

    def __init__(
        self,
        baseline_sbp: float = 140.0,
        baseline_dbp: float = 80.0,
        baseline_hr: float = 75.0,
        blood_volume: float = 5000.0,
        plasma_refill_coeff: float = 0.004,
        is_diabetic: bool = False,
    ):
        """Initialize cardiovascular model.

        Args:
            baseline_sbp: Pre-dialysis systolic blood pressure (mmHg).
            baseline_dbp: Pre-dialysis diastolic blood pressure (mmHg).
            baseline_hr: Pre-dialysis heart rate (bpm).
            blood_volume: Total blood volume (mL).
            plasma_refill_coeff: Plasma refilling rate coefficient (fraction/min).
            is_diabetic: Whether patient has diabetic autonomic neuropathy.
        """
        self.baseline_sbp = baseline_sbp
        self.baseline_dbp = baseline_dbp
        self.baseline_hr = baseline_hr
        self.blood_volume_initial = blood_volume
        self.plasma_refill_coeff = plasma_refill_coeff
        self.is_diabetic = is_diabetic

        # SIMPLIFICATION: Baroreceptor gain reduced for diabetic patients
        # to simulate autonomic neuropathy. Clinical presentation involves
        # complex changes in sympathetic/parasympathetic balance.
        self.baroreceptor_gain = 0.3 if is_diabetic else 0.6

        # State
        self.blood_volume = blood_volume
        self.sbp = baseline_sbp
        self.dbp = baseline_dbp
        self.hr = baseline_hr
        self.total_uf_volume = 0.0

    def reset(self) -> dict:
        """Reset cardiovascular state to baseline."""
        self.blood_volume = self.blood_volume_initial
        self.sbp = self.baseline_sbp
        self.dbp = self.baseline_dbp
        self.hr = self.baseline_hr
        self.total_uf_volume = 0.0
        return self.get_state()

    def get_state(self) -> dict:
        """Get current cardiovascular state."""
        return {
            "systolic_bp": float(self.sbp),
            "diastolic_bp": float(self.dbp),
            "heart_rate": float(self.hr),
            "blood_volume": float(self.blood_volume),
            "total_uf_volume": float(self.total_uf_volume),
        }

    def plasma_refill_rate(self) -> float:
        """Compute plasma refilling rate from interstitial space.

        SIMPLIFICATION: Uses linear refilling proportional to volume deficit.
        Real refilling depends on Starling forces, oncotic pressure, and
        capillary hydraulic conductivity. Acceptable approximation for
        the first 2-3 liters of fluid removal.

        Returns:
            Refill rate in mL/min.
        """
        volume_deficit = self.blood_volume_initial - self.blood_volume
        return self.plasma_refill_coeff * volume_deficit

    def step(self, uf_rate_ml_per_hr: float, dt: float = 1.0) -> dict:
        """Advance cardiovascular model by dt minutes.

        Args:
            uf_rate_ml_per_hr: Current ultrafiltration rate in mL/hr.
            dt: Time step in minutes.

        Returns:
            Dict with updated hemodynamic state.
        """
        uf_rate_ml_per_min = uf_rate_ml_per_hr / 60.0
        refill_rate = self.plasma_refill_rate()

        # Net volume change
        net_removal = (uf_rate_ml_per_min - refill_rate) * dt
        self.blood_volume = max(
            self.blood_volume_initial * 0.7,  # minimum 70% of initial
            self.blood_volume - net_removal,
        )
        self.total_uf_volume += uf_rate_ml_per_min * dt

        # Blood volume fraction change
        bv_fraction = self.blood_volume / self.blood_volume_initial

        # SIMPLIFICATION: Blood pressure modeled as proportional to blood
        # volume fraction with baroreceptor compensation. The Ursino model
        # uses cardiac output, peripheral resistance, and venous return.
        raw_sbp = self.baseline_sbp * bv_fraction
        raw_dbp = self.baseline_dbp * bv_fraction

        # Baroreceptor reflex compensation
        sbp_deficit = self.baseline_sbp - raw_sbp
        compensation = self.baroreceptor_gain * sbp_deficit

        self.sbp = float(np.clip(raw_sbp + compensation, 50, 200))
        self.dbp = float(np.clip(raw_dbp + compensation * 0.6, 30, 120))

        # Heart rate increases to compensate for volume loss
        hr_increase = (1.0 - bv_fraction) * 30.0 * (1.0 - self.baroreceptor_gain * 0.3)
        self.hr = float(np.clip(self.baseline_hr + hr_increase, 50, 150))

        return self.get_state()

    @property
    def is_hypotensive(self) -> bool:
        """Check if patient is currently hypotensive (SBP < 90 mmHg)."""
        return self.sbp < 90.0
