"""Tests on Lab 12's recorded comparison (12_self_host_vs_api.py, an Apple M4 Max, October 2026): the same 100
made-up test emails, one at a time, through Lab 11's 4-bit fine-tune on the laptop, gpt-5.4-nano and gpt-5.4-mini.
Run: pytest test_selfhost_practice.py"""
from selfhost_practice import always_on_per_1000, api_per_1000, paired_p, percentile, self_host_per_1000

# Per email: 1 = all four fields right. Seconds: the full round trip as the app saw it (rounded to 1 ms).
MINE_RIGHT = "1111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111"
NANO_RIGHT = "0111101000101011011110111111111011011011100101101110111101011111110111111111101011011101111011111111"
MINI_RIGHT = "1111111110111111111111111111111111111011111111111111111111111111111111111111111111111111111101111111"

MINE_SECONDS = [
    0.154, 0.106, 0.119, 0.108, 0.119, 0.14, 0.12, 0.102, 0.105, 0.122, 0.106, 0.095, 0.093, 0.119, 0.123,
    0.119, 0.123, 0.114, 0.112, 0.103, 0.114, 0.111, 0.123, 0.09, 0.102, 0.128, 0.119, 0.171, 0.108, 0.107,
    0.108, 0.108, 0.125, 0.098, 0.117, 0.106, 0.119, 0.108, 0.106, 0.092, 0.088, 0.125, 0.106, 0.107, 0.118,
    0.115, 0.1, 0.122, 0.105, 0.108, 0.104, 0.119, 0.106, 0.131, 0.117, 0.125, 0.115, 0.105, 0.13, 0.119,
    0.116, 0.102, 0.128, 0.127, 0.12, 0.124, 0.128, 0.121, 0.104, 0.123, 0.117, 0.101, 0.107, 0.106, 0.096,
    0.103, 0.103, 0.124, 0.121, 0.125, 0.111, 0.128, 0.127, 0.115, 0.118, 0.103, 0.119, 0.127, 0.134, 0.097,
    0.125, 0.122, 0.099, 0.116, 0.101, 0.113, 0.117, 0.088, 0.106, 0.124,
]
NANO_SECONDS = [
    1.63, 0.824, 0.946, 0.628, 1.022, 1.043, 4.89, 0.862, 1.162, 1.216, 0.763, 0.77, 0.765, 0.944, 2.866,
    0.758, 1.005, 0.746, 0.787, 0.719, 0.939, 1.094, 0.771, 0.737, 0.949, 1.093, 0.803, 0.975, 0.662, 1.812,
    0.655, 0.678, 0.829, 0.792, 0.694, 0.867, 0.822, 0.684, 1.186, 0.787, 0.741, 0.662, 1.038, 1.738, 0.73,
    0.745, 0.858, 1.056, 0.87, 0.949, 0.69, 0.798, 0.757, 0.875, 1.03, 1.061, 0.799, 0.851, 0.757, 0.673,
    0.763, 0.919, 0.882, 0.745, 0.706, 0.74, 0.772, 1.584, 0.743, 2.164, 0.995, 0.666, 0.731, 0.723, 1.36,
    0.711, 1.038, 0.942, 0.806, 1.045, 1.616, 1.102, 1.33, 1.861, 0.68, 3.858, 0.772, 0.898, 0.726, 0.734,
    1.074, 0.734, 0.693, 0.682, 0.694, 0.721, 0.669, 0.656, 0.739, 0.749,
]
MINI_SECONDS = [
    0.728, 1.103, 1.152, 1.058, 1.084, 1.016, 0.93, 0.971, 1.632, 1.298, 2.476, 0.988, 0.848, 1.873, 2.122,
    0.911, 1.073, 0.873, 0.855, 1.192, 0.715, 1.846, 0.93, 1.717, 1.45, 0.91, 2.091, 0.753, 0.967, 0.926,
    0.921, 0.76, 1.793, 1.078, 1.1, 0.901, 0.815, 0.729, 0.959, 0.799, 1.089, 1.199, 0.748, 1.261, 0.777,
    0.928, 1.026, 1.127, 0.816, 0.828, 0.757, 0.881, 1.78, 1.991, 0.947, 0.852, 1.046, 1.369, 0.729, 0.919,
    1.24, 1.18, 1.023, 1.139, 1.314, 0.786, 1.177, 0.866, 1.05, 1.091, 0.928, 0.977, 1.127, 0.761, 1.081,
    0.891, 0.98, 0.896, 0.848, 1.083, 3.358, 1.017, 6.962, 2.766, 1.45, 1.014, 0.985, 1.015, 1.114, 1.051,
    1.023, 1.054, 0.855, 1.175, 0.903, 0.755, 0.837, 1.281, 0.84, 1.276,
]

MINE, NANO, MINI = ([c == "1" for c in s] for s in (MINE_RIGHT, NANO_RIGHT, MINI_RIGHT))
PER_HOUR = 87263  # emails an hour on the laptop server, measured at 32 simulated users (24.2 a second)


def test_percentile_typical_and_slow_end():
    assert sum(MINE) == 99 and sum(NANO) == 75 and sum(MINI) == 97
    assert (percentile(MINE_SECONDS, 50), percentile(MINE_SECONDS, 95)) == (0.115, 0.13)
    assert (round(percentile(NANO_SECONDS, 50), 2), round(percentile(NANO_SECONDS, 95), 2)) == (0.8, 1.81)
    assert (round(percentile(MINI_SECONDS, 50), 2), round(percentile(MINI_SECONDS, 95), 2)) == (1.02, 2.09)
    assert round(percentile(MINI_SECONDS, 50) / percentile(MINE_SECONDS, 50)) == 9
    assert percentile([3, 1, 2], 50) == 2 and percentile([5], 95) == 5 and percentile([], 50) == 0.0


def test_paired_p_tie_and_clear_gap():
    assert round(paired_p(MINE, MINI), 3) == 0.625  # they disagreed on 4 emails: 3 went my way, 1 went mini's
    assert paired_p(MINE, NANO) < 0.001
    assert paired_p([True, False], [True, False]) == 1.0
    assert paired_p([True] * 6, [False] * 6) == 0.03125  # 6 of 6 disagreements one way: unlikely by chance


def test_api_cost_list_price_and_cache_estimate():
    # Average tokens per call in the recorded run: mini 1,766.27 in / 29.55 out, nano 655.27 in / 37.88 out.
    assert round(api_per_1000("gpt-5.4-mini", 1766.27, 29.55), 2) == 1.46
    assert round(api_per_1000("gpt-5.4-nano", 655.27, 37.88), 2) == 0.18
    # Not measured: if 1,536 or 1,024 of mini's prompt tokens were cached on every call
    assert round(api_per_1000("gpt-5.4-mini", 1766.27, 29.55, cached=1536), 2) == 0.42
    assert round(api_per_1000("gpt-5.4-mini", 1766.27, 29.55, cached=1024), 2) == 0.77


def test_self_host_cost_depends_on_busy():
    assert round(self_host_per_1000(2, PER_HOUR), 3) == 0.023
    assert round(self_host_per_1000(2, PER_HOUR, busy=0.05), 3) == 0.458
    assert round(self_host_per_1000(2, PER_HOUR, 0.05) / self_host_per_1000(2, PER_HOUR)) == 20
    assert round(self_host_per_1000(5, PER_HOUR, busy=0.05), 3) == 1.146  # under mini's $1.46 list, over the estimate
    assert round(self_host_per_1000(0.5, PER_HOUR, busy=0.25), 3) == 0.023


def test_always_on_at_low_volume():
    assert round(5000 / PER_HOUR * 60, 1) == 3.4  # 5,000 emails a day is 3.4 minutes of this server's time
    assert round(always_on_per_1000(2, 5000), 2) == 9.6  # but the machine is paid for all day
