"""CTL / ATL / TSB (Fitness / Fatigue / Form) calculation service."""

from datetime import date, timedelta

from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from backend.app.models.activity import Activity, ActivityMetrics
from backend.app.models.fitness import FitnessHistory

CTL_DAYS = 42  # Chronic Training Load time constant
ATL_DAYS = 7   # Acute Training Load time constant


async def rebuild_fitness_history(
    rider_id,
    session: AsyncSession,
    days_back: int = 365,
) -> list[FitnessHistory]:
    """Rebuild CTL/ATL/TSB for a rider from scratch using exponential moving averages."""

    end_date = date.today()
    start_date = end_date - timedelta(days=days_back)

    # Get all activities with TSS in the date range
    query = (
        select(
            func.date(Activity.date).label("activity_date"),
            func.sum(ActivityMetrics.tss).label("daily_tss"),
        )
        .join(ActivityMetrics, ActivityMetrics.activity_id == Activity.id)
        .where(Activity.rider_id == rider_id)
        .where(Activity.date >= start_date)
        .where(ActivityMetrics.tss.isnot(None))
        .group_by(func.date(Activity.date))
        .order_by(func.date(Activity.date))
    )

    result = await session.execute(query)
    tss_by_date: dict[date, float] = {}
    for row in result:
        tss_by_date[row.activity_date] = float(row.daily_tss)

    # Delete existing history for this rider
    existing = await session.execute(
        select(FitnessHistory).where(FitnessHistory.rider_id == rider_id)
    )
    for entry in existing.scalars().all():
        await session.delete(entry)

    # Calculate EMA-based CTL/ATL/TSB
    ctl = 0.0
    atl = 0.0
    prev_ctl = 0.0
    entries: list[FitnessHistory] = []

    current = start_date
    while current <= end_date:
        daily_tss = tss_by_date.get(current, 0.0)

        ctl = ctl + (daily_tss - ctl) / CTL_DAYS
        atl = atl + (daily_tss - atl) / ATL_DAYS
        tsb = ctl - atl
        ramp_rate = ctl - prev_ctl

        entry = FitnessHistory(
            rider_id=rider_id,
            date=current,
            ctl=round(ctl, 1),
            atl=round(atl, 1),
            tsb=round(tsb, 1),
            ramp_rate=round(ramp_rate, 2),
        )
        entries.append(entry)

        prev_ctl = ctl
        current += timedelta(days=1)

    session.add_all(entries)
    await session.flush()
    return entries


async def get_fitness_history(
    rider_id,
    session: AsyncSession,
    days: int = 90,
) -> list[FitnessHistory]:
    """Get fitness history for a rider, rebuilding if needed."""

    cutoff = date.today() - timedelta(days=days)

    query = (
        select(FitnessHistory)
        .where(FitnessHistory.rider_id == rider_id)
        .where(FitnessHistory.date >= cutoff)
        .order_by(FitnessHistory.date)
    )
    result = await session.execute(query)
    entries = list(result.scalars().all())

    # If no data or stale, rebuild
    if not entries or entries[-1].date < date.today() - timedelta(days=1):
        all_entries = await rebuild_fitness_history(rider_id, session)
        entries = [e for e in all_entries if e.date >= cutoff]

    return entries
