from __future__ import annotations

from typing import cast

import numpy as np
import numpy.typing as npt
from typing_extensions import TypeAlias

FloatArray: TypeAlias = npt.NDArray[np.float64]


def gauss(x: FloatArray, mu: float, sigma: float) -> FloatArray:
    y = np.exp(-0.5 * ((x - mu) / sigma) ** 2)
    return cast(FloatArray, np.asarray(y, dtype=np.float64))


def ecg_pqrst(
    pct: FloatArray,
    p_mu: float = 0.18,
    q_mu: float = 0.38,
    r_mu: float = 0.40,
    s_mu: float = 0.42,
    t_mu: float = 0.70,
    p_sig: float = 0.030,
    q_sig: float = 0.010,
    r_sig: float = 0.008,
    s_sig: float = 0.010,
    t_sig: float = 0.050,
    p_amp: float = 0.15,
    q_amp: float = -0.12,
    r_amp: float = 1.00,
    s_amp: float = -0.25,
    t_amp: float = 0.35,
) -> FloatArray:
    p = p_amp * gauss(pct, p_mu, p_sig)
    q = q_amp * gauss(pct, q_mu, q_sig)
    r = r_amp * gauss(pct, r_mu, r_sig)
    s = s_amp * gauss(pct, s_mu, s_sig)
    t = t_amp * gauss(pct, t_mu, t_sig)
    return cast(FloatArray, np.asarray(p + q + r + s + t, dtype=np.float64))


def normalize_01(x: FloatArray) -> FloatArray:
    xmin = float(np.min(x))
    z = cast(FloatArray, np.asarray(x - xmin, dtype=np.float64))
    xmax = float(np.max(z))
    if xmax <= 0.0:
        return cast(FloatArray, np.zeros_like(z))
    return cast(FloatArray, np.asarray(z / xmax, dtype=np.float64))
