"""Tests for `load_sleep_stages` — percentage invariants + edge cases."""

from __future__ import annotations

import numpy as np
import pandas as pd
import pytest

from openrun.model import load_sleep_stages


def _seed(conn, date: str, deep: float, light: float, rem: float,
          awake: float = 0.0, score: float | None = None) -> None:
    conn.execute(
        """INSERT INTO daily_sleep
           (calendar_date, deep_s, light_s, rem_s, awake_s, sleep_score, raw, fetched_at)
           VALUES (?, ?, ?, ?, ?, ?, '{}', 'now')""",
        (date, deep, light, rem, awake, score),
    )
    conn.commit()


def test_sleep_stages_percentages_sum_to_one(tmp_conn) -> None:
    """deep + light + rem percentages always sum to 1.0 (the load-bearing invariant)."""
    _seed(tmp_conn, "2026-05-01", deep=3600, light=10800, rem=5400)   # 1h / 3h / 1.5h
    _seed(tmp_conn, "2026-05-02", deep=1800, light=9000,  rem=3600)   # 0.5h / 2.5h / 1h

    df = load_sleep_stages(tmp_conn)
    total_pct = df["deep_pct"] + df["light_pct"] + df["rem_pct"]
    np.testing.assert_allclose(total_pct.values, [1.0, 1.0], rtol=1e-12)


def test_sleep_stages_percentages_match_seconds(tmp_conn) -> None:
    _seed(tmp_conn, "2026-05-01", deep=3600, light=10800, rem=5400)
    row = load_sleep_stages(tmp_conn).iloc[0]
    total = 3600 + 10800 + 5400
    assert row["deep_pct"] == pytest.approx(3600 / total)
    assert row["light_pct"] == pytest.approx(10800 / total)
    assert row["rem_pct"] == pytest.approx(5400 / total)


def test_sleep_stages_awake_pct_uses_time_in_bed(tmp_conn) -> None:
    """awake_pct = awake / (asleep + awake), not /total nor /asleep alone."""
    # 4h asleep + 1h awake → awake_pct = 1/5 = 0.2
    _seed(tmp_conn, "2026-05-01", deep=3600, light=7200, rem=3600, awake=3600)
    row = load_sleep_stages(tmp_conn).iloc[0]
    assert row["awake_pct"] == pytest.approx(0.2)


def test_sleep_stages_sleep_hours_excludes_awake(tmp_conn) -> None:
    _seed(tmp_conn, "2026-05-01", deep=3600, light=7200, rem=3600, awake=999)
    row = load_sleep_stages(tmp_conn).iloc[0]
    # 1 + 2 + 1 = 4 hours.
    assert row["sleep_hours"] == pytest.approx(4.0)


def test_sleep_stages_zero_sleep_yields_nan_pct(tmp_conn) -> None:
    """Recorded night with no asleep time (data glitch) → NaN percentages, no DivisionByZero."""
    _seed(tmp_conn, "2026-05-01", deep=0, light=0, rem=0, awake=1800)
    row = load_sleep_stages(tmp_conn).iloc[0]
    assert np.isnan(row["deep_pct"])
    assert np.isnan(row["light_pct"])
    assert np.isnan(row["rem_pct"])


def test_sleep_stages_empty_returns_empty_frame(tmp_conn) -> None:
    df = load_sleep_stages(tmp_conn)
    assert df.empty
