import numpy as np

from backend.app.models.activity import Activity, ActivityMetrics, DataPoint


def calculate_metrics(
    data_points: list[DataPoint],
    activity: Activity,
    ftp: float | None = None,
) -> ActivityMetrics | None:
    """Calculate all power/HR-based metrics for an activity."""
    if not data_points:
        return None

    powers = np.array([dp.power for dp in data_points if dp.power is not None], dtype=float)
    hrs = np.array([dp.heart_rate for dp in data_points if dp.heart_rate is not None], dtype=float)
    cadences = np.array([dp.cadence for dp in data_points if dp.cadence is not None], dtype=float)
    speeds = np.array([dp.speed for dp in data_points if dp.speed is not None], dtype=float)

    has_power = len(powers) > 0
    has_hr = len(hrs) > 0

    avg_power = float(np.mean(powers)) if has_power else None
    max_power = int(np.max(powers)) if has_power else None
    np_value = _normalized_power(powers) if len(powers) >= 30 else avg_power

    avg_hr = int(np.mean(hrs)) if has_hr else None
    max_hr = int(np.max(hrs)) if has_hr else None
    avg_cadence = int(np.mean(cadences)) if len(cadences) > 0 else None
    avg_speed = float(np.mean(speeds)) if len(speeds) > 0 else None

    # Variability Index
    variability_index = None
    if np_value and avg_power and avg_power > 0:
        variability_index = round(np_value / avg_power, 2)

    # FTP-dependent metrics
    intensity_factor = None
    tss = None
    if np_value and ftp and ftp > 0:
        intensity_factor = round(np_value / ftp, 2)
        tss = round(
            (activity.duration * np_value * (np_value / ftp))
            / (ftp * 3600)
            * 100,
            1,
        )

    # Efficiency Factor: NP / avg HR (aerobic decoupling indicator)
    calories = None
    if has_power:
        # Rough estimate: 1 kJ ≈ 1 kcal, power in watts * seconds / 1000
        calories = int(np.sum(powers) / 1000)

    return ActivityMetrics(
        activity_id=activity.id,
        tss=tss,
        normalized_power=round(np_value, 1) if np_value else None,
        intensity_factor=intensity_factor,
        variability_index=variability_index,
        avg_power=round(avg_power, 1) if avg_power else None,
        max_power=max_power,
        avg_hr=avg_hr,
        max_hr=max_hr,
        avg_cadence=avg_cadence,
        avg_speed=round(avg_speed, 2) if avg_speed else None,
        calories=calories,
    )


def _normalized_power(powers: np.ndarray) -> float:
    """NP = 4th root of mean of (30s rolling average)^4."""
    if len(powers) < 30:
        return float(np.mean(powers))
    rolling = np.convolve(powers, np.ones(30) / 30, mode="valid")
    return float(np.power(np.mean(np.power(rolling, 4)), 0.25))
