#!/usr/bin/env python3
"""Rank inherited secondaryBranchState producer candidates for the current route."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
CURRENT_SELECTOR = "2:0"
CURRENT_ROOT = "0x00540714"


def selector_key(row: dict) -> str:
    return f"{row.get('group')}:{row.get('slot')}"


def build_progress_edges(selectors: list[dict]) -> dict[str, dict]:
    field_rows = [row for row in selectors if row.get("fieldMaps")]
    edges = {}
    for row in field_rows:
        key = selector_key(row)
        maps = set(row.get("fieldMaps") or [])
        previous = []
        for prev in field_rows:
            prev_key = selector_key(prev)
            if prev_key == key:
                continue
            if (prev.get("group"), prev.get("slot")) >= (row.get("group"), row.get("slot")):
                continue
            prev_maps = set(prev.get("fieldMaps") or [])
            shared = sorted(maps & prev_maps)
            introduced = sorted(maps - prev_maps)
            dropped = sorted(prev_maps - maps)
            if not shared or not introduced:
                continue
            previous.append({
                "selector": prev_key,
                "rootHex": prev.get("selectedPointerHex"),
                "sharedMaps": shared,
                "introducedMaps": introduced,
                "droppedMaps": dropped,
                "score": len(shared) * 10 + len(introduced) - len(dropped),
            })
        previous.sort(key=lambda item: (-item["score"], item["selector"]))
        edges[key] = {
            "selector": key,
            "rootHex": row.get("selectedPointerHex"),
            "fieldMaps": row.get("fieldMaps") or [],
            "previous": previous,
            "bestPrevious": previous[0] if previous else None,
        }
    return edges


def build_summary(selectors: list[dict], fill_roots: dict) -> dict:
    edges = build_progress_edges(selectors)
    fill_by_root = {row.get("rootHex"): row for row in fill_roots.get("roots") or []}
    current = edges.get(CURRENT_SELECTOR) or {}
    candidates = []
    for item in current.get("previous") or []:
        fill = fill_by_root.get(item.get("rootHex"))
        candidates.append({
            **item,
            "hasSecondaryFill": bool(fill),
            "secondaryFillCount": (fill or {}).get("fillCount", 0),
            "firstFills": (fill or {}).get("fills", [])[:5],
            "candidateRole": (
                "best previous and has secondary fill"
                if item == current.get("bestPrevious") and fill
                else "alternate previous and has secondary fill"
                if fill
                else "progress predecessor without secondary fill"
            ),
        })
    conclusion = (
        "The selector-progress predecessor for current root 2:0 is 1:0, and that predecessor has opcode-shaped "
        "secondaryBranchState fills. This makes 1:0/root 0x00478364 the strongest inherited-state producer candidate "
        "for the map1_01a->map2_02d blocker. It still does not promote the transition: the remaining proof is to decode "
        "which slot/value 1:0 leaves in secondaryBranchState and to find a strict map1_01a source hotspot."
    )
    return {
        "currentSelector": CURRENT_SELECTOR,
        "currentRootHex": CURRENT_ROOT,
        "currentFieldMaps": current.get("fieldMaps") or [],
        "bestPrevious": candidates[0] if candidates else None,
        "candidates": candidates,
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Inherited State Candidates",
        "",
        f"- current selector: `{summary['currentSelector']}`",
        f"- current root: `{summary['currentRootHex']}`",
        f"- conclusion: {summary['conclusion']}",
        "",
        "| predecessor | root | secondary fills | shared maps | introduced maps | role | first fills |",
        "| --- | --- | ---: | --- | --- | --- | --- |",
    ]
    for row in summary["candidates"]:
        fills = ", ".join(f"`{item['vaHex']}={item['valueHex']}`" for item in row.get("firstFills") or []) or "-"
        lines.append(
            f"| `{row['selector']}` | `{row['rootHex']}` | {row['secondaryFillCount']} | "
            f"{', '.join(row.get('sharedMaps') or []) or '-'} | {', '.join(row.get('introducedMaps') or []) or '-'} | "
            f"{row['candidateRole']} | {fills} |"
        )
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    rows = "\n".join(
        "<tr>"
        f"<td><code>{html.escape(row['selector'])}</code></td>"
        f"<td><code>{html.escape(str(row['rootHex']))}</code></td>"
        f"<td>{row['secondaryFillCount']}</td>"
        f"<td>{html.escape(', '.join(row.get('sharedMaps') or []) or '-')}</td>"
        f"<td>{html.escape(', '.join(row.get('introducedMaps') or []) or '-')}</td>"
        f"<td>{html.escape(row['candidateRole'])}</td>"
        f"<td>{', '.join('<code>' + html.escape(item['vaHex']) + '=' + html.escape(item['valueHex']) + '</code>' for item in row.get('firstFills') or []) or '-'}</td>"
        "</tr>"
        for row in summary["candidates"]
    )
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Inherited State Candidates</title>",
        "<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee}table{border-collapse:collapse}td,th{border:1px solid #444;padding:6px 8px;vertical-align:top}code{color:#9bd4ff}</style>",
        "<h1>Save Selector Inherited State Candidates</h1>",
        f"<p>Current selector: <code>{summary['currentSelector']}</code>; current root: <code>{summary['currentRootHex']}</code></p>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<table><thead><tr><th>predecessor</th><th>root</th><th>secondary fills</th><th>shared maps</th><th>introduced maps</th><th>role</th><th>first fills</th></tr></thead><tbody>",
        rows,
        "</tbody></table>",
    ])


def write_outputs(summary: dict, out_dir: Path) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "save_selector_inherited_state_candidates.json").write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--selectors", type=Path, default=OUT / "save_scene_selectors.json")
    parser.add_argument("--fill-roots", type=Path, default=OUT / "save_selector_secondary_fill_roots.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_summary(
        json.loads(args.selectors.read_text(encoding="utf-8")),
        json.loads(args.fill_roots.read_text(encoding="utf-8")),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote inherited state candidates -> {args.out_dir / 'save_selector_inherited_state_candidates.json'}")


if __name__ == "__main__":
    main()
