import datetime
import os
import re
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, "sources", "openai_deprecations_md.txt")
MONTHS = {m: i for i, m in enumerate(["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"], 1)}

def pdate(s):
    s = s.replace("‑", "-").replace("at earliest", "").strip()
    m = re.match(r"(\d{4})-(\d{2})-(\d{2})$", s)
    if m:
        return datetime.date(*map(int, m.groups()))
    m = re.match(r"([A-Za-z]{3})[a-z]*\.? (\d{1,2}), (\d{4})$", s)
    if m:
        return datetime.date(int(m.group(3)), MONTHS[m.group(1).lower()], int(m.group(2)))
    return None

def kind_of(repl):
    r = repl.strip().strip("`").strip()
    if r in ("", "---", "-", "—", "–", "N/A", "n/a", "None"):
        return "NONE"
    if "`" in repl or re.search(r"\b(gpt|o\d|text-|dall|whisper|tts|omni|davinci|babbage|computer-use|sora|chatgpt|codex)", r, re.I):
        return "NAMED"

    if re.search(r"\bAPI\b|OpenAI-Beta:|/v1/", r) and not re.search(r"transition guide", r, re.I):
        return "NAMED"
    return "VAGUE"

def parse(text):
    anns, cur, header = [], None, None
    for line in text.splitlines():
        h = re.match(r"^### (?:(\d{4}-\d{2}-\d{2}): )?(.+)$", line)
        if h:
            cur = {"date": pdate(h.group(1)) if h.group(1) else None, "title": h.group(2).replace("`", ""), "rows": [],
                   "timeline": []}
            anns.append(cur)
            header = None
            continue
        if cur is None or not line.startswith("|"):
            continue
        cells = [c.strip() for c in re.split(r"(?<!\\)\|", line.strip())[1:-1]]
        if all(re.fullmatch(r":?-{3,}:?", c) for c in cells):
            continue
        if header is None or cells[0].lower() in ("shutdown date", "date") or cells[0].lower().startswith("shutdown"):
            header = [c.lower() for c in cells]
            continue
        if not any(("replacement" in c or "substitute" in c) for c in header):

            cur["timeline"].append({"date_raw": cells[0], "date": pdate(cells[0]), "update": cells[-1].replace("`", "")})
            continue
        rcol = next((i for i, c in enumerate(header) if "replacement" in c or "substitute" in c), len(cells) - 1)
        mcol = next((i for i, c in enumerate(header) if "model" in c or "system" in c), 1)
        raw_sd = cells[0]
        cur["rows"].append({"shutdown_raw": raw_sd, "shutdown": pdate(raw_sd), "earliest": "earliest" in raw_sd,
                            "model": cells[mcol].replace("`", "").replace("\\|", "|"),
                            "replacement": cells[rcol] if rcol < len(cells) else "", "kind": kind_of(cells[rcol] if rcol < len(cells) else "")})
    return anns

def main():
    text = open(SRC, encoding="utf-8").read()
    anns = parse(text)
    out = []
    p = out.append
    p("OpenAI API deprecations page, parsed: %d announcements, %d rows" % (len(anns), sum(len(a["rows"]) for a in anns)))
    p("")
    p("%-10s %-58s %4s %5s %5s %5s  %s" % ("announced", "title", "rows", "NONE", "VAGUE", "min_d", "notice days (distinct)"))
    rows_all = []
    for a in anns:
        ds = sorted({(r["shutdown"] - a["date"]).days for r in a["rows"] if r["shutdown"] and a["date"]})
        n_none = sum(r["kind"] == "NONE" for r in a["rows"])
        n_vague = sum(r["kind"] == "VAGUE" for r in a["rows"])
        p("%-10s %-58s %4d %5d %5d %5s  %s" % (a["date"], a["title"][:58], len(a["rows"]), n_none, n_vague,
                                               ds[0] if ds else "?", ds))
        for r in a["rows"]:
            rows_all.append(dict(r, ann=a["title"], ann_date=a["date"],
                                 days=(r["shutdown"] - a["date"]).days if r["shutdown"] and a["date"] else None))
    p("")
    p("TIMELINE sections (Date | Update tables: a product, not a model, being retired; no replacement column):")
    for a in anns:
        for t in a["timeline"]:
            p("  [%s] %s  %s" % (a["title"][:44], t["date_raw"], t["update"][:110]))
    p("")
    tot = len(rows_all)
    none = [r for r in rows_all if r["kind"] == "NONE"]
    vague = [r for r in rows_all if r["kind"] == "VAGUE"]
    p("TOTAL rows %d · named replacement %d · NO replacement %d · vague pointer %d" % (tot, tot - len(none) - len(vague), len(none), len(vague)))
    p("Announcements whose EVERY row has no replacement: %s" % [a["title"] for a in anns if a["rows"] and all(r["kind"] == "NONE" for r in a["rows"])])
    p("")
    p("Rows with NO replacement:")
    for r in none:
        p("  %s  %-28s  shutdown %s  (%s days' notice)  [%s]" % (r["ann_date"], r["model"], r["shutdown_raw"], r["days"], r["ann"]))
    p("Rows with a VAGUE pointer:")
    for r in vague:
        p("  %s  %-28s  -> %r  (%s days)  [%s]" % (r["ann_date"], r["model"], r["replacement"], r["days"], r["ann"]))
    p("")
    dated = [r for r in rows_all if r["days"] is not None]
    dd = sorted(r["days"] for r in dated)
    p("Notice days over %d dated rows: min %d · median %d · max %d" % (len(dd), dd[0], dd[len(dd) // 2], dd[-1]))
    for lo, hi, lab in ((0, 30, "< 30 days"), (30, 90, "30-89"), (90, 180, "90-179"), (180, 100000, ">= 180 (about 6 months)")):
        p("  %-24s %d rows" % (lab, sum(lo <= d < hi for d in dd)))
    p("Shortest-notice rows (under 90 days):")
    for r in sorted(dated, key=lambda r: r["days"]):
        if r["days"] < 90:
            p("  %4d days  %s -> %s  %-30s repl %r  [%s]" % (r["days"], r["ann_date"], r["shutdown_raw"], r["model"], r["replacement"][:40], r["ann"]))

    by_model = {r["model"]: r for r in rows_all}
    checks = [
        ("gpt-4-0314 (2023-06-13 table) names gpt-4o", "gpt-4o" in by_model.get("gpt-4-0314", {}).get("replacement", "")),
        ("code-davinci-002: 3 days' notice", by_model.get("code-davinci-002", {}).get("days") == 3),
        ("dall-e-3 shuts 2026-05-12 with a named replacement", by_model.get("dall-e-3", {}).get("kind") == "NAMED"),
        ("Assistants API -> Responses API counts as NAMED", by_model.get("Assistants API", {}).get("kind") == "NAMED"),
        ("gpt-5.4-cyber counts as VAGUE", by_model.get("gpt-5.4-cyber", {}).get("kind") == "VAGUE"),
        ("exactly the 6 Sora rows have NO replacement", sorted(r["model"] for r in none) == sorted(
            ["Videos API", "sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-2025-12-08", "sora-2-pro-2025-10-06"])),
    ]
    p("")
    p("CONTROLS: %d of %d pass" % (sum(ok for _, ok in checks), len(checks)))
    for what, ok in checks:
        p("  %s  %s" % ("ok  " if ok else "FAIL", what))
    text_out = "\n".join(out) + "\n"
    open(os.path.join(HERE, "deprecations_c7555.out.txt"), "w", encoding="utf-8", newline="\n").write(text_out)
    sys.stdout.write(text_out)

if __name__ == "__main__":
    main()
