#!/usr/bin/env python3
"""Classify raw references to the status/comment backing base.

The absolute writer scan intentionally looked for memory operands and therefore
left references to 0x004576d8 itself as "unclassified" immediates.  For the
status/comment selector these immediates are important:

* two sites bind object+0xa8 to 0x004576d8 with `c7 80 a8 00 00 00 ...`;
* two sites derive sub-pointers inside the backing block;
* two sites pass the whole 0x72-byte block to save/load-like routines.

This proves the backing-base bridge without pretending that selector byte +0
itself is written here.
"""
from __future__ import annotations

import html
import json
from collections import Counter
from pathlib import Path
from typing import Any


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


def load_json(name: str) -> dict[str, Any]:
    try:
        data = json.loads((OUT / name).read_text(encoding="utf-8"))
    except FileNotFoundError:
        return {}
    return data if isinstance(data, dict) else {}


def h(value: Any) -> str:
    return html.escape("" if value is None else str(value), quote=True)


def classify_ref(row: dict[str, Any]) -> dict[str, Any]:
    context = str(row.get("contextHex", "")).lower()
    ref = str(row.get("refVaHex", ""))
    if "c7 80 a8 00 00 00 d8 76 45 00" in context:
        return {
            "classification": "object-a8-backing-base-bind",
            "confidence": "high",
            "meaning": "writes immediate 0x004576d8 into object+0xa8; this is the base binding used by 0x40 status/menu selector reads",
        }
    if "b8 d8 76 45 00 83 c0 20" in context:
        return {
            "classification": "backing-subpointer-plus-0x20",
            "confidence": "high",
            "meaning": "derives pointer 0x004576d8+0x20 for adjacent status/menu data",
        }
    if "b8 d8 76 45 00 83 c0 14" in context:
        return {
            "classification": "backing-subpointer-plus-0x14",
            "confidence": "high",
            "meaning": "derives pointer 0x004576d8+0x14 for adjacent status/menu data",
        }
    if "6a 72 68 d8 76 45 00" in context:
        return {
            "classification": "backing-block-transfer-arg",
            "confidence": "medium-high",
            "meaning": "pushes backing base with size 0x72 to an imported/helper routine; likely save/load or block copy path",
        }
    return {
        "classification": "unclassified-base-immediate",
        "confidence": "low",
        "meaning": f"raw immediate reference at {ref}; no specific backing role recognized",
    }


def build() -> dict[str, Any]:
    backing = load_json("status_comment_backing_writer_scan.json")
    refs = backing.get("offset0Refs", [])
    rows = []
    for row in refs if isinstance(refs, list) else []:
        if not isinstance(row, dict):
            continue
        cls = classify_ref(row)
        rows.append(
            {
                **{key: row.get(key, "") for key in ("refVaHex", "functionVaHex", "functionEndVaHex", "contextHex")},
                **cls,
            }
        )

    counts = Counter(row["classification"] for row in rows)
    function_counts = Counter(row.get("functionVaHex", "") for row in rows)
    base_bind_rows = [row for row in rows if row["classification"] == "object-a8-backing-base-bind"]
    block_transfer_rows = [row for row in rows if row["classification"] == "backing-block-transfer-arg"]
    summary = {
        "backingBaseVaHex": "0x004576d8",
        "backingSizeHex": "0x72",
        "offset0RawRefCount": len(rows),
        "classificationCounts": dict(counts),
        "functionCounts": dict(function_counts),
        "objectA8BackingBaseBindCount": len(base_bind_rows),
        "blockTransferArgCount": len(block_transfer_rows),
        "selectorByteValueWriterProven": False,
        "decision": (
            "Backing-base binding is proven: object+0xa8 is assigned 0x004576d8 at two sites. "
            "These sites establish the status/menu selector base, but they do not write the selector byte at backing +0."
        ),
    }
    return {
        "version": 1,
        "kind": "hwanse-status-comment-backing-base-ref-review",
        "sourceArtifacts": ["out/status_comment_backing_writer_scan.json"],
        "summary": summary,
        "rows": rows,
    }


def render_html(data: dict[str, Any]) -> str:
    cards = "".join(
        f"<div class='card'><b>{h(k)}</b><span>{h(v)}</span></div>"
        for k, v in data["summary"].items()
    )
    rows = "".join(
        "<tr>"
        f"<td><code>{h(row['refVaHex'])}</code><br><code>{h(row['functionVaHex'])}</code></td>"
        f"<td>{h(row['classification'])}<br><small>{h(row['confidence'])}</small></td>"
        f"<td>{h(row['meaning'])}</td>"
        f"<td><code>{h(row['contextHex'])}</code></td>"
        "</tr>"
        for row in data["rows"]
    )
    return f"""<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Status Comment Backing Base Reference Review</title>
<style>
  :root {{ color-scheme: light dark; }}
  body {{ margin:24px; font-family:system-ui,-apple-system,Segoe UI,sans-serif; line-height:1.45; }}
  h1 {{ margin:0 0 8px; font-size:24px; }}
  .muted {{ color:#667085; }}
  .cards {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:10px; margin:16px 0; }}
  .card {{ border:1px solid #d0d5dd; border-radius:8px; padding:10px 12px; background:rgba(127,127,127,.05); }}
  .card b {{ display:block; color:#667085; font-size:12px; }}
  .card span {{ font-family:ui-monospace,SFMono-Regular,Menlo,monospace; overflow-wrap:anywhere; }}
  .scroll {{ overflow-x:auto; }}
  table {{ width:100%; border-collapse:collapse; }}
  th,td {{ border:1px solid #d0d5dd; padding:7px 8px; vertical-align:top; }}
  th {{ text-align:left; background:rgba(127,127,127,.08); }}
  code {{ font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; }}
</style>
<h1>Status Comment Backing Base Reference Review</h1>
<p class="muted">기존 writer scan에서 unclassified였던 <code>0x004576d8</code> 즉시값 참조 6개를 문맥별로 재분류합니다.</p>
<div class="cards">{cards}</div>
<div class="scroll"><table>
<thead><tr><th>reference/function</th><th>classification</th><th>meaning</th><th>context</th></tr></thead>
<tbody>{rows}</tbody>
</table></div>
"""


def main() -> None:
    data = build()
    OUT.mkdir(exist_ok=True)
    WEB.mkdir(exist_ok=True)
    (OUT / "status_comment_backing_base_ref_review.json").write_text(
        json.dumps(data, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    (WEB / "status_comment_backing_base_ref_review.html").write_text(
        render_html(data),
        encoding="utf-8",
    )


if __name__ == "__main__":
    main()
