"""Print every number the lesson computes. Run it from this folder: python3 show_numbers.py"""
from dataclasses import replace
from decimal import Decimal

from bill import (DAYS_PER_MONTH, EXAMPLE, EXAMPLE_CHATS_PER_DAY, EXAMPLE_KEEP, EXAMPLE_PRICES as PRICES, bill, closed_form,
                  naive_bill, simulate, turn_cost, turn_parts)

P_IN, P_OUT = PRICES


def pct(part, whole):
    return f"{100 * Decimal(part) / Decimal(whole):.1f}%"


def money(x):
    text = f"{x.normalize():f}"
    whole, _, cents = text.partition(".")
    return f"${int(whole):,}.{cents.ljust(2, '0')}"


print(f"== The example chat: {EXAMPLE.turns} turns, system prompt {EXAMPLE.system}, each message {EXAMPLE.message}, "
      f"each reply {EXAMPLE.reply} tokens. Example prices ${P_IN} in, ${P_OUT} out per million tokens")
print("  turn | system | history | message | input | output | cost | running total")
running = Decimal(0)
for k in range(1, EXAMPLE.turns + 1):
    t, c = turn_parts(EXAMPLE, k), turn_cost(EXAMPLE, k, *PRICES)
    running += c
    print(f"  {k} | {t.system} | {t.history:,} | {t.message} | {t.input:,} | {t.output} | {money(c)} | {money(running)}")

b = bill(EXAMPLE, *PRICES)
loop, closed = simulate(EXAMPLE), closed_form(EXAMPLE)
print(f"\n== Whole chat: simulated {loop[0]:,} in / {loop[1]:,} out; closed form {closed[0]:,} in / {closed[1]:,} out")
print(f"  input {money(b.input_cost)} + output {money(b.output_cost)} = {money(b.total)}; output is {pct(b.output_cost, b.total)} of the money "
      f"and {pct(b.output_tokens, b.input_tokens + b.output_tokens)} of the tokens")
sys_tokens = EXAMPLE.turns * EXAMPLE.system
hist_tokens = sum(turn_parts(EXAMPLE, k).history for k in range(1, EXAMPLE.turns + 1))
new_tokens = EXAMPLE.turns * EXAMPLE.message
print(f"  input tokens by part: system {sys_tokens:,} ({pct(sys_tokens, b.input_tokens)}), history {hist_tokens:,} "
      f"({pct(hist_tokens, b.input_tokens)}), new messages {new_tokens:,} ({pct(new_tokens, b.input_tokens)})")
last = turn_parts(EXAMPLE, EXAMPLE.turns)
print(f"  turn {EXAMPLE.turns}: {last.input:,} input tokens to answer a {EXAMPLE.message}-token message "
      f"({last.input // EXAMPLE.message}x, rounded down)")

print("\n== Longer chats, same sizes (a 'fresh start' is a one-turn chat: system prompt + one message + one reply)")
fresh = bill(replace(EXAMPLE, turns=1), *PRICES).total
print(f"  one fresh start: {money(fresh)}")
print("  turns | input tokens | output tokens | cost | cost of that many fresh starts | ratio")
for n in (10, 20, 30):
    c = replace(EXAMPLE, turns=n)
    x = bill(c, *PRICES)
    print(f"  {n} | {x.input_tokens:,} | {x.output_tokens:,} | {money(x.total)} | {money(fresh * n)} | {x.total / (fresh * n):.2f}x")

print(f"\n== Trim to the last {EXAMPLE_KEEP} earlier turns")
for n in (10, 30):
    full, trimmed = bill(replace(EXAMPLE, turns=n), *PRICES), bill(replace(EXAMPLE, turns=n, keep=EXAMPLE_KEEP), *PRICES)
    print(f"  {n} turns: input {full.input_tokens:,} -> {trimmed.input_tokens:,}; cost {money(full.total)} -> {money(trimmed.total)} "
          f"({pct(full.total - trimmed.total, full.total)} less)")
kept = replace(EXAMPLE, turns=30, keep=EXAMPLE_KEEP)
print(f"  per-turn input tokens, trimmed 30-turn chat, turns 1-6: {[turn_parts(kept, k).input for k in range(1, 7)]}; "
      f"from turn 4 on every turn sends {turn_parts(kept, 4).input:,}")
print(f"  untrimmed turns 1-6: {[turn_parts(replace(EXAMPLE, turns=30), k).input for k in range(1, 7)]}; turn 30 sends "
      f"{turn_parts(replace(EXAMPLE, turns=30), 30).input:,}")

print("\n== Other levers, 10-turn chat, example prices")
levers = [("baseline", EXAMPLE), ("system prompt 2,000 (tools and pasted documents)", replace(EXAMPLE, system=2000)),
          ("system prompt 100", replace(EXAMPLE, system=100)), ("replies capped at 100 tokens", replace(EXAMPLE, reply=100)),
          ("hidden reasoning 800 tokens per reply", replace(EXAMPLE, reasoning=800))]
for name, c in levers:
    x = bill(c, *PRICES)
    print(f"  {name}: {x.input_tokens:,} in / {x.output_tokens:,} out = {money(x.total)} ({x.total / b.total:.2f}x baseline)")
think = bill(replace(EXAMPLE, reasoning=800), *PRICES)
print(f"  reasoning adds {money(think.total - b.total)}; billed output tokens {think.output_tokens:,} against "
      f"{EXAMPLE.turns * EXAMPLE.reply:,} visible ({think.output_tokens // (EXAMPLE.turns * EXAMPLE.reply)}x)")

print("\n== Retries: one call in ten is made twice")
r = bill(EXAMPLE, *PRICES, retry_rate="0.1")
print(f"  base {money(r.input_cost + r.output_cost)} + retries {money(r.retry_cost)} = {money(r.total)}")

print("\n== Smaller model for the easy steps (prices read 2026-10-19: Sonnet 5.5 $2 / $10, Haiku 5.5 $0.10 / $0.50 per million)")
big, small = bill(EXAMPLE, 2, 10), bill(EXAMPLE, "0.10", "0.50")
print(f"  10-turn chat: {money(big.total)} at $2 / $10 against {money(small.total)} at $0.10 / $0.50 = {big.total / small.total:.0f}x cheaper")

print("\n== The naive estimate: only what you type and what comes back, each billed once")
for turns in (10, 30):
    c = replace(EXAMPLE, turns=turns)
    naive, real = naive_bill(c, *PRICES), bill(c, *PRICES).total
    print(f"  {turns}-turn chat: naive {money(naive)}, real {money(real)}, real is {real / naive:.1f}x the estimate")

print(f"\n== A month: {EXAMPLE_CHATS_PER_DAY} chats a day, {DAYS_PER_MONTH} days")
chats = EXAMPLE_CHATS_PER_DAY * DAYS_PER_MONTH
for name, c in (("10-turn chat", EXAMPLE), (f"10-turn chat trimmed to {EXAMPLE_KEEP}", replace(EXAMPLE, keep=EXAMPLE_KEEP)),
                ("30-turn chat", replace(EXAMPLE, turns=30)), (f"30-turn chat trimmed to {EXAMPLE_KEEP}", replace(EXAMPLE, turns=30, keep=EXAMPLE_KEEP))):
    print(f"  {name}: {chats:,} chats x {money(bill(c, *PRICES).total)} = {money(chats * bill(c, *PRICES).total)}")
