#!/usr/bin/env python3
"""Export small, named Korean text tables for the browser runtime."""
from __future__ import annotations

import argparse
import html
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from summarize_korean_text_candidates import build_summary as build_korean_text_summary


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"

KNOWN_TABLES = [
    {
        "key": "items",
        "title": "Item Names",
        "clusterStartVaHex": "0x0048b984",
        "description": "Six item names matching the known savedat item count slots.",
    },
    {
        "key": "equipment",
        "title": "Equipment Names",
        "clusterStartVaHex": "0x0048b244",
        "description": "Equipment-like labels referenced by an EXE data pointer table.",
    },
    {
        "key": "atahoActions",
        "title": "Ataho Action Names",
        "clusterStartVaHex": "0x004d24ac",
        "description": "Action names used near the initial Ataho action table.",
    },
    {
        "key": "actionSetA",
        "title": "Action Name Set A",
        "clusterStartVaHex": "0x004d26bc",
        "description": "Short battle/action labels from a compact pointer table.",
    },
    {
        "key": "actionSetB",
        "title": "Action Name Set B",
        "clusterStartVaHex": "0x004d2864",
        "description": "Short battle/action labels from a compact pointer table.",
    },
    {
        "key": "battleCommands",
        "title": "Battle Command Names",
        "clusterStartVaHex": "0x004d2974",
        "description": "Battle command and action labels from a medium pointer table.",
    },
    {
        "key": "skillNamesA",
        "title": "Skill Names A",
        "clusterStartVaHex": "0x004d2a34",
        "description": "Large skill/name pointer table candidate.",
    },
    {
        "key": "skillNamesB",
        "title": "Skill Names B",
        "clusterStartVaHex": "0x004d2c04",
        "description": "Large skill/name pointer table candidate.",
    },
    {
        "key": "magicNames",
        "title": "Magic Names",
        "clusterStartVaHex": "0x004d2d64",
        "description": "Small magic/name pointer table candidate.",
    },
]

SAVE_ITEM_KEYS = [
    ("herb", 0x0015),
    ("item_2", 0x0017),
    ("refresh_water", 0x0019),
    ("mp_recovery", 0x001B),
    ("item_5", 0x001D),
    ("item_6", 0x001F),
]


def js_json(data) -> str:
    return json.dumps(data, ensure_ascii=False, separators=(",", ":"))


def load_korean_summary(exe: bytes | None = None, out_dir: Path = OUT) -> dict:
    path = out_dir / "korean_text_candidates.json"
    if path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    if exe is None:
        exe = (ROOT / "Hwanse2.exe").read_bytes()
    return build_korean_text_summary(exe)


def table_from_cluster(cluster: dict, spec: dict) -> dict:
    entries = [
        {
            "index": index,
            "refVaHex": entry["refVaHex"],
            "textVaHex": entry["textVaHex"],
            "text": entry["text"],
        }
        for index, entry in enumerate(cluster.get("entries") or [])
    ]
    return {
        **spec,
        "clusterEndVaHex": cluster.get("endVaHex"),
        "entryCount": len(entries),
        "inboundRefCount": cluster.get("inboundRefCount", 0),
        "entries": entries,
    }


def build_summary(korean_summary: dict) -> dict:
    clusters = {cluster["startVaHex"]: cluster for cluster in korean_summary.get("refClusters") or []}
    tables = []
    for spec in KNOWN_TABLES:
        cluster = clusters.get(spec["clusterStartVaHex"])
        if cluster:
            tables.append(table_from_cluster(cluster, spec))
    tables_by_key = {table["key"]: table for table in tables}
    item_entries = tables_by_key.get("items", {}).get("entries") or []
    save_items = []
    for index, (key, offset) in enumerate(SAVE_ITEM_KEYS):
        entry = item_entries[index] if index < len(item_entries) else {}
        save_items.append({
            "key": key,
            "offset": offset,
            "offsetHex": f"0x{offset:04x}",
            "name": entry.get("text") or key,
            "sourceClusterStartVaHex": tables_by_key.get("items", {}).get("clusterStartVaHex"),
            "sourceTextVaHex": entry.get("textVaHex"),
        })
    return {
        "scope": "Named Korean text tables extracted from executable pointer-table clusters.",
        "source": "out/korean_text_candidates.json",
        "tableCount": len(tables),
        "tables": tables,
        "saveItems": save_items,
        "notes": [
            "Save item names are inferred by ordering the six names in cluster 0x0048b984 against the six known savedat item count offsets.",
            "Other tables are exported as candidates until the menu/battle handlers are fully mapped.",
        ],
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Text Tables",
        "",
        summary["scope"],
        "",
        "## Save Items",
        "",
        "| key | offset | name | source text |",
        "| --- | ---: | --- | --- |",
    ]
    for row in summary.get("saveItems") or []:
        lines.append(
            f"| {row['key']} | `{row['offsetHex']}` | {row['name']} | `{row.get('sourceTextVaHex') or '-'}` |"
        )
    lines.extend(["", "## Tables", "", "| key | cluster | entries | samples |", "| --- | --- | ---: | --- |"])
    for table in summary.get("tables") or []:
        samples = ", ".join(entry["text"] for entry in table.get("entries", [])[:8]).replace("|", "\\|")
        lines.append(
            f"| {table['key']} | `{table['clusterStartVaHex']}` | {table['entryCount']} | {samples} |"
        )
    lines.extend(["", "## Notes", ""])
    lines.extend(f"- {note}" for note in summary.get("notes") or [])
    return "\n".join(lines) + "\n"


def html_page(summary: dict) -> str:
    save_rows = "".join(
        "<tr>"
        f"<td>{html.escape(row['key'])}</td>"
        f"<td><code>{html.escape(row['offsetHex'])}</code></td>"
        f"<td>{html.escape(row['name'])}</td>"
        f"<td><code>{html.escape(row.get('sourceTextVaHex') or '-')}</code></td>"
        "</tr>"
        for row in summary.get("saveItems") or []
    )
    table_rows = "".join(
        "<tr>"
        f"<td>{html.escape(table['key'])}</td>"
        f"<td><code>{html.escape(table['clusterStartVaHex'])}</code></td>"
        f"<td>{table['entryCount']}</td>"
        f"<td>{html.escape(', '.join(entry['text'] for entry in table.get('entries', [])[:12]))}</td>"
        "</tr>"
        for table in summary.get("tables") or []
    )
    return "\n".join([
        "<!doctype html>",
        '<html lang="en">',
        "<head>",
        '  <meta charset="utf-8">',
        '  <meta name="viewport" content="width=device-width, initial-scale=1">',
        "  <title>Hwanse Text Tables</title>",
        "  <style>body{font-family:system-ui,sans-serif;background:#101010;color:#eee;margin:24px}table{border-collapse:collapse;width:100%}td,th{border:1px solid #333;padding:6px 8px;vertical-align:top}th{background:#1d1d1d}code{color:#f5d76e}</style>",
        "</head>",
        "<body>",
        "  <h1>Hwanse Text Tables</h1>",
        f"  <p>{html.escape(summary['scope'])}</p>",
        "  <h2>Save Items</h2>",
        "  <table><thead><tr><th>key</th><th>offset</th><th>name</th><th>source text</th></tr></thead>",
        f"  <tbody>{save_rows}</tbody></table>",
        "  <h2>Tables</h2>",
        "  <table><thead><tr><th>key</th><th>cluster</th><th>entries</th><th>samples</th></tr></thead>",
        f"  <tbody>{table_rows}</tbody></table>",
        "</body>",
        "</html>",
        "",
    ])


def write_outputs(summary: dict, out_dir: Path = OUT) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "text_tables.json").write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    (out_dir / "text_tables.js").write_text(
        "window.HWANSE_TEXT_TABLES = " + js_json(summary) + ";\n",
        encoding="utf-8",
    )
    (out_dir / "text_tables.html").write_text(html_page(summary), encoding="utf-8")


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--exe", type=Path, default=ROOT / "Hwanse2.exe")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    exe = args.exe.read_bytes() if not (args.out_dir / "korean_text_candidates.json").exists() else None
    summary = build_summary(load_korean_summary(exe, args.out_dir))
    write_outputs(summary, args.out_dir)


if __name__ == "__main__":
    main()
