"""Tests on Lab 14's recorded week (04_alerts.py, October 2026): 1,055 questions, live gpt-5.4-nano answers, and
four problems planted on purpose. Search was broken on Wednesday from 09:00 to 11:00 (hours 57 and 58 of the week).
Run: pytest test_alerts_practice.py"""
from alerts_practice import above_baseline, bar, binom_sf, by_day, fired, over_threshold, score

# Normal: Monday and Tuesday, 46 "I don't know"s in 302 answers (15%).
BASE = 46 / 302
BROKEN = (57, 59)  # Wednesday 09:00-11:00, in hours from Monday 00:00
# Wednesday to Sunday, hour by hour from hour 48 (Wednesday midnight): [questions, "I don't know"].
WEEK = [
    [0,0],[0,0],[1,0],[0,0],[2,0],[9,0],[0,0],[2,0],[9,1],[21,19],[19,19],[11,0],[15,0],[7,0],[3,1],[8,0],[18,4],[12,0],
    [5,0],[2,1],[0,0],[0,0],[3,1],[3,0],[0,0],[0,0],[2,0],[0,0],[0,0],[11,3],[6,0],[7,1],[7,1],[18,2],[8,0],[3,2],[5,1],
    [5,0],[16,3],[12,1],[13,3],[15,1],[9,0],[5,1],[2,0],[6,1],[0,0],[0,0],[0,0],[0,0],[0,0],[2,0],[0,0],[0,0],[2,0],
    [4,2],[8,0],[4,1],[3,0],[7,0],[25,4],[26,4],[13,1],[8,0],[6,1],[16,1],[3,0],[5,1],[8,1],[11,1],[0,0],[1,0],[0,0],
    [0,0],[0,0],[1,0],[6,1],[3,0],[1,0],[6,0],[3,0],[21,1],[18,1],[25,1],[13,1],[6,0],[1,0],[14,2],[8,0],[4,0],[2,0],
    [16,5],[1,0],[0,0],[1,1],[0,0],[1,0],[0,0],[0,0],[2,0],[7,0],[1,1],[4,1],[3,1],[24,3],[16,1],[23,1],[14,2],[2,0],
    [6,0],[3,0],[10,1],[8,1],[18,3],[2,0],[5,0],[0,0],[0,0],[0,0],[2,0],
]
HOURS = [(48 + i, n, k) for i, (n, k) in enumerate(WEEK)]
SAT_7PM = 5 * 24 + 19  # hour 139


def test_threshold_rule_and_quiet_hours():
    assert over_threshold(5, 16)  # Saturday 7 p.m.: 31%
    assert not over_threshold(4, 16)  # 25%
    assert over_threshold(19, 21)
    assert not over_threshold(2, 4)  # 50%, but only 4 questions: not judged
    assert not over_threshold(3, 10)  # exactly 30% is not "more than"


def test_binomial_chance():
    assert binom_sf(0, 10, 0.5) == 1.0
    assert round(binom_sf(4, 6, 0.5), 2) == 0.34  # 4 or more heads in 6 flips: you shrug
    assert binom_sf(40, 60, 0.5) < 0.01  # 40 or more in 60: check the coin
    assert round(1 / binom_sf(5, 16, BASE)) == 12  # Saturday's hour: about 1 hour in 12 by chance
    assert binom_sf(19, 21, BASE) < 1e-9  # Wednesday 9 a.m.: practically never


def test_baseline_rule():
    assert not above_baseline(5, 16, BASE)
    assert above_baseline(19, 21, BASE)
    assert not above_baseline(2, 6, BASE)  # a third, in a tiny hour: weak evidence
    assert above_baseline(20, 60, BASE)  # a third, in a busy hour: strong evidence
    assert not above_baseline(4, 4, BASE)  # under 5 questions: not judged


def test_the_bar_depends_on_the_hours_size():
    assert [bar(n, BASE) for n in (5, 16, 21, 60)] == [5, 9, 10, 20]
    # every hour judged this week had a bar of at least 42%
    assert min(bar(n, BASE) / n for _, n, _ in HOURS if n >= 5) >= 0.42


def test_the_threshold_on_the_week():
    alerts = fired(HOURS, over_threshold)
    assert alerts == [57, 58, SAT_7PM]
    assert score(alerts, BROKEN) == (60, 1)  # caught in its first hour; Saturday for nothing


def test_the_baseline_on_the_week():
    alerts = fired(HOURS, lambda k, n: above_baseline(k, n, BASE))
    assert alerts == [57, 58]
    assert score(alerts, BROKEN) == (60, 0)
    # a fixed line at 40% would also have stayed quiet this week
    assert fired(HOURS, lambda k, n: over_threshold(k, n, 0.40)) == [57, 58]


def test_checked_once_a_day():
    days = by_day(HOURS)
    assert days[0] == (2, 150, 46)  # Wednesday: 46 "I don't know"s in 150
    assert [d for d, n, k in days if over_threshold(k, n)] == [2]
    # the day's check speaks at midnight: 15 hours after search broke at 09:00
    assert score([2], BROKEN, size=24) == (900, 0)
