"""Exact teaching model; Python 3.11+, standard library; no random sampling."""
import csv
import io
import json
from collections import defaultdict
from fractions import Fraction as F
from itertools import product
from pathlib import Path

ROOT = Path(__file__).resolve().parent
N, K, P = 10, 3, F(3, 10)


def csv_text(header, rows):
    stream = io.StringIO(newline="")
    writer = csv.writer(stream, lineterminator="\n")
    writer.writerow(header)
    writer.writerows(rows)
    return stream.getvalue()


def generate():
    sequences = ["".join(x) for x in product("BC", repeat=N)]
    quota_count = sum(s.count("B") == K for s in sequences)
    weights = {
        "independent": {s: P**s.count("B") * (1-P)**s.count("C") for s in sequences},
        "quota": {s: F(1, quota_count) if s.count("B") == K else F(0) for s in sequences},
    }
    positions, prefixes = [], []
    for index in range(N):
        row = {"opportunity": index+1}
        for model, distribution in weights.items():
            row[f"{model}_bet"] = str(sum(w for s, w in distribution.items() if s[index] == "B"))
            histories = defaultdict(lambda: [F(0), F(0)])
            for s, w in distribution.items():
                histories[s[:index]][s[index] == "C"] += w
            row[f"{model}_guess_accuracy"] = str(sum(max(pair) for pair in histories.values()))
        positions.append(row)
    for history in ["", "CCC", "BBB", "BBCCCCCCC", "BBBCCCCCC"]:
        row = {"history": history}
        for model, distribution in weights.items():
            total = sum(w for s, w in distribution.items() if s.startswith(history))
            bet = sum(w for s, w in distribution.items() if s.startswith(history+"B"))
            row[f"{model}_next_bet"] = str(bet/total) if total else None
        prefixes.append(row)
    result = {
        "settings": {"opportunities": N, "quota_bets": K, "bet_probability": str(P)},
        "sequence_counts": {"independent": len(sequences), "quota": quota_count},
        "count_distribution": [{"bets": k, **{model: str(sum(w for s, w in dist.items() if s.count("B") == k)) for model, dist in weights.items()}} for k in range(N+1)],
        "positions": positions,
        "prefixes": prefixes,
        "mean_guess_accuracy": {model: str(sum(F(row[f"{model}_guess_accuracy"]) for row in positions)/N) for model in weights},
    }
    (ROOT / "results.json").write_text(json.dumps(result, indent=2)+"\n", encoding="utf-8", newline="\n")
    (ROOT / "sequences.csv").write_text(csv_text(["sequence", "independent_weight", "quota_weight"], [[s, str(weights["independent"][s]), str(weights["quota"][s])] for s in sequences]), encoding="utf-8", newline="\n")
    (ROOT / "mapping.csv").write_text(csv_text(["roll", "two_action", "three_action"], [[r, "Bet" if r <= 30 else "Check", "Small" if r <= 40 else "Large" if r <= 75 else "Check"] for r in range(1, 101)]), encoding="utf-8", newline="\n")
    print("Generated exact results: 1024 sequences, 120 quota sequences, 100 integer mappings.")


if __name__ == "__main__":
    generate()
