#!/usr/bin/env python3
"""Summarize duplicate save-selector field-map sets around the route blocker."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
SOURCE = "map1_01a"
TARGET = "map2_02d"
SOURCE_SELECTOR = "0:0"
LOGICAL_PREDECESSOR_SELECTOR = "1:0"
CURRENT_SELECTOR = "2:0"
ADDRESS_PREDECESSOR_SELECTOR = "10:0"


def load_json(path: Path, fallback: Any) -> Any:
    if not path.exists():
        return fallback
    return json.loads(path.read_text(encoding="utf-8"))


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


def find_selector(selectors: list[dict], key: str) -> dict:
    for row in selectors:
        if selector_key(row) == key:
            return row
    raise ValueError(f"selector {key} not found")


def mapset_key(maps: list[str]) -> tuple[str, ...]:
    return tuple(sorted(maps))


def fill_by_root(secondary_fill_roots: dict) -> dict[str, dict]:
    return {
        row.get("rootHex"): row
        for row in secondary_fill_roots.get("roots") or []
        if row.get("rootHex")
    }


def sample_ids_by_selector(sample_coverage: dict) -> dict[str, list[str]]:
    result: dict[str, list[str]] = {}
    for sample in sample_coverage.get("samples") or []:
        selector = sample.get("selector")
        sample_id = sample.get("id")
        if selector and sample_id:
            result.setdefault(selector, []).append(sample_id)
    return result


def address_order(selectors: list[dict]) -> dict[str, int]:
    roots = sorted({
        row.get("selectedPointerHex")
        for row in selectors
        if row.get("fieldMaps") and row.get("selectedPointerHex")
    }, key=lambda value: int(value, 16))
    return {root: index for index, root in enumerate(roots)}


def alias_role(selector: str) -> str:
    if selector == SOURCE_SELECTOR:
        return "source-side confirmed selector"
    if selector == LOGICAL_PREDECESSOR_SELECTOR:
        return "logical target-side predecessor"
    if selector == ADDRESS_PREDECESSOR_SELECTOR:
        return "address-adjacent target-side predecessor"
    if selector == CURRENT_SELECTOR:
        return "current route-pair selector"
    return "additional map-set alias"


def alias_row(
    row: dict,
    *,
    selector_index: int,
    root_order: dict[str, int],
    fills_by_root: dict[str, dict],
    samples_by_selector: dict[str, list[str]],
) -> dict:
    selector = selector_key(row)
    root_hex = row.get("selectedPointerHex")
    fill_root = fills_by_root.get(root_hex) or {}
    fills = fill_root.get("fills") or []
    return {
        "selector": selector,
        "selectorIndex": selector_index,
        "role": alias_role(selector),
        "rootHex": root_hex,
        "rootAddressOrderIndex": root_order.get(root_hex),
        "rowPointerHex": row.get("rowPointerHex"),
        "selectedPointerEntryHex": row.get("selectedPointerVaHex"),
        "fieldMaps": row.get("fieldMaps") or [],
        "fillCount": fill_root.get("fillCount", 0),
        "firstFillHex": (fills[0] or {}).get("vaHex") if fills else None,
        "lastFillHex": (fills[-1] or {}).get("vaHex") if fills else None,
        "publicSampleIds": samples_by_selector.get(selector, []),
    }


def build_alias_groups(
    selectors: list[dict],
    secondary_fill_roots: dict | None = None,
    sample_coverage: dict | None = None,
    address_predecessor_context: dict | None = None,
    set_decomposition: dict | None = None,
) -> dict:
    secondary_fill_roots = secondary_fill_roots or {}
    sample_coverage = sample_coverage or {}
    address_predecessor_context = address_predecessor_context or {}
    set_decomposition = set_decomposition or {}
    fills_by_root = fill_by_root(secondary_fill_roots)
    samples_by_selector = sample_ids_by_selector(sample_coverage)
    root_order = address_order(selectors)
    groups: dict[tuple[str, ...], list[tuple[int, dict]]] = {}
    for index, row in enumerate(selectors):
        maps = row.get("fieldMaps") or []
        if maps:
            groups.setdefault(mapset_key(maps), []).append((index, row))
    duplicate_groups = []
    for maps_key, rows in groups.items():
        if len(rows) <= 1:
            continue
        aliases = [
            alias_row(
                row,
                selector_index=index,
                root_order=root_order,
                fills_by_root=fills_by_root,
                samples_by_selector=samples_by_selector,
            )
            for index, row in rows
        ]
        duplicate_groups.append({
            "mapCount": len(maps_key),
            "fieldMaps": list(maps_key),
            "aliasCount": len(aliases),
            "aliases": aliases,
            "containsSource": SOURCE in maps_key,
            "containsTarget": TARGET in maps_key,
        })
    duplicate_groups.sort(key=lambda group: (-group["aliasCount"], group["fieldMaps"]))

    source_row = find_selector(selectors, SOURCE_SELECTOR)
    predecessor_row = find_selector(selectors, LOGICAL_PREDECESSOR_SELECTOR)
    current_row = find_selector(selectors, CURRENT_SELECTOR)
    target_key = mapset_key(predecessor_row.get("fieldMaps") or [])
    target_alias_group = next(group for group in duplicate_groups if tuple(group["fieldMaps"]) == target_key)
    current_maps = set(current_row.get("fieldMaps") or [])
    source_maps = set(source_row.get("fieldMaps") or [])
    target_maps = set(predecessor_row.get("fieldMaps") or [])
    target_alias_selectors = [row["selector"] for row in target_alias_group["aliases"]]
    public_sample_ids = [
        sample_id
        for row in target_alias_group["aliases"]
        for sample_id in row.get("publicSampleIds") or []
    ]
    current_equals_target_alias_plus_source = current_maps == (target_maps | {SOURCE})
    source_target_union = source_maps | target_maps
    source_target_union_extra_maps = sorted(source_target_union - current_maps)
    source_target_union_missing_maps = sorted(current_maps - source_target_union)
    conclusion = (
        "The target-side map set for map2_02d has three selector aliases: 1:0, 10:0, and 17:0. "
        "Selector 1:0 is the logical selector-table predecessor and the only public-sample-covered alias; "
        "selector 10:0 is the address-adjacent root immediately before 2:0 and has many secondaryBranchState fills; "
        "selector 17:0 is another target-side alias without public sample coverage. The current 2:0 map set equals "
        "that target-side map set plus map1_01a, while source selector 0:0 plus the target-side set over-covers the "
        "current map set by map1_02b. This supports selector list recomposition/aliasing, but it still does not prove "
        "runtime execution order, state persistence into the 0x00542b0c reader, or a strict map1_01a source hotspot."
    )
    return {
        "source": SOURCE,
        "target": TARGET,
        "sourceSelector": SOURCE_SELECTOR,
        "logicalPredecessorSelector": LOGICAL_PREDECESSOR_SELECTOR,
        "addressPredecessorSelector": ADDRESS_PREDECESSOR_SELECTOR,
        "currentSelector": CURRENT_SELECTOR,
        "fieldMapSetGroupCount": len(groups),
        "duplicateMapSetGroupCount": len(duplicate_groups),
        "duplicateMapSetSelectorCount": sum(group["aliasCount"] for group in duplicate_groups),
        "duplicateGroups": duplicate_groups,
        "targetAliasGroup": target_alias_group,
        "targetAliasSelectors": target_alias_selectors,
        "targetAliasSelectorCount": len(target_alias_selectors),
        "targetAliasPublicSampleIds": public_sample_ids,
        "targetAliasPublicSampleCount": len(public_sample_ids),
        "currentEqualsTargetAliasPlusSource": current_equals_target_alias_plus_source,
        "sourceTargetUnionOvercoversCurrent": bool(source_target_union_extra_maps),
        "sourceTargetUnionExtraMaps": source_target_union_extra_maps,
        "sourceTargetUnionMissingMaps": source_target_union_missing_maps,
        "addressPredecessorEqualsLogicalPredecessorMapSet": address_predecessor_context.get("addressPredecessorEqualsLogicalPredecessorMapSet"),
        "addressPredecessorToCurrentRootRefCount": address_predecessor_context.get("addressPredecessorToCurrentRootRefCount"),
        "addressPredecessorCurrentRootRefClassification": address_predecessor_context.get("addressPredecessorCurrentRootRefClassification"),
        "addressContiguityProvesExecution": address_predecessor_context.get("addressContiguityProvesExecution"),
        "setDecompositionCurrentEqualsPredecessorPlusSource": set_decomposition.get("currentEqualsPredecessorPlusSource"),
        "setDecompositionExecutionOrderProven": set_decomposition.get("executionOrderProven"),
        "promotionStatus": "blocked",
        "remainingProofs": [
            "prove which target-side alias executes before current selector 2:0",
            "prove secondaryBranchState persists into the 0x00542b0c reader",
            "find a strict map1_01a source hotspot or equivalent original transition trigger",
        ],
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Map-Set Aliases",
        "",
        f"- route: `{summary['source']}` -> `{summary['target']}`",
        f"- duplicate map-set groups: {summary['duplicateMapSetGroupCount']} / selectors in duplicate groups: {summary['duplicateMapSetSelectorCount']}",
        f"- target alias selectors: `{', '.join(summary['targetAliasSelectors'])}`",
        f"- target alias public sample count: {summary['targetAliasPublicSampleCount']}",
        f"- current equals target alias plus source: {summary['currentEqualsTargetAliasPlusSource']}",
        f"- source+target union extra maps: `{', '.join(summary['sourceTargetUnionExtraMaps']) or '-'}`",
        f"- address predecessor equals logical predecessor map set: {summary['addressPredecessorEqualsLogicalPredecessorMapSet']}",
        f"- address predecessor -> current root refs: {summary['addressPredecessorToCurrentRootRefCount']}",
        f"- address contiguity proves execution: {summary['addressContiguityProvesExecution']}",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        summary["conclusion"],
        "",
        "## Target Alias Group",
        "",
        "| selector | role | root | address order | fills | public samples |",
        "| --- | --- | --- | ---: | ---: | --- |",
    ]
    for row in summary["targetAliasGroup"]["aliases"]:
        lines.append(
            f"| `{row['selector']}` | {row['role']} | `{row['rootHex']}` | {row['rootAddressOrderIndex']} | "
            f"{row['fillCount']} | `{', '.join(row['publicSampleIds']) or '-'}` |"
        )
    lines.extend([
        "",
        "## Duplicate Groups",
        "",
        "| aliases | maps |",
        "| --- | --- |",
    ])
    for group in summary["duplicateGroups"]:
        aliases = ", ".join(f"`{row['selector']}`" for row in group["aliases"])
        maps = ", ".join(group["fieldMaps"][:12])
        if len(group["fieldMaps"]) > 12:
            maps += f", +{len(group['fieldMaps']) - 12}"
        lines.append(f"| {aliases} | {maps} |")
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    target_rows = "\n".join(
        "<tr>"
        f"<td><code>{html.escape(row['selector'])}</code></td>"
        f"<td>{html.escape(row['role'])}</td>"
        f"<td><code>{html.escape(str(row['rootHex']))}</code></td>"
        f"<td>{html.escape(str(row['rootAddressOrderIndex']))}</td>"
        f"<td>{html.escape(str(row['fillCount']))}</td>"
        f"<td><code>{html.escape(', '.join(row['publicSampleIds']) or '-')}</code></td>"
        "</tr>"
        for row in summary["targetAliasGroup"]["aliases"]
    )
    group_rows = "\n".join(
        "<tr>"
        f"<td>{', '.join('<code>' + html.escape(row['selector']) + '</code>' for row in group['aliases'])}</td>"
        f"<td>{html.escape(', '.join(group['fieldMaps']))}</td>"
        "</tr>"
        for group in summary["duplicateGroups"]
    )
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Map-Set Aliases</title>",
        "<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;max-width:1100px;margin:24px auto}table{border-collapse:collapse;width:100%}td,th{border:1px solid #444;padding:6px 8px;vertical-align:top}code{color:#9bd4ff}</style>",
        "<h1>Save Selector Map-Set Aliases</h1>",
        "<ul>",
        f"<li>route: <code>{html.escape(summary['source'])}</code> -&gt; <code>{html.escape(summary['target'])}</code></li>",
        f"<li>duplicate map-set groups: {summary['duplicateMapSetGroupCount']} / selectors in duplicate groups: {summary['duplicateMapSetSelectorCount']}</li>",
        f"<li>target alias selectors: <code>{html.escape(', '.join(summary['targetAliasSelectors']))}</code></li>",
        f"<li>target alias public sample count: {summary['targetAliasPublicSampleCount']}</li>",
        f"<li>current equals target alias plus source: {summary['currentEqualsTargetAliasPlusSource']}</li>",
        f"<li>source+target union extra maps: <code>{html.escape(', '.join(summary['sourceTargetUnionExtraMaps']) or '-')}</code></li>",
        f"<li>address predecessor equals logical predecessor map set: {summary['addressPredecessorEqualsLogicalPredecessorMapSet']}</li>",
        f"<li>address predecessor -&gt; current root refs: {summary['addressPredecessorToCurrentRootRefCount']}</li>",
        f"<li>address contiguity proves execution: {summary['addressContiguityProvesExecution']}</li>",
        f"<li>promotion status: <code>{html.escape(summary['promotionStatus'])}</code></li>",
        "</ul>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<h2>Target Alias Group</h2>",
        "<table><thead><tr><th>selector</th><th>role</th><th>root</th><th>address order</th><th>fills</th><th>public samples</th></tr></thead><tbody>",
        target_rows,
        "</tbody></table>",
        "<h2>Duplicate Groups</h2>",
        "<table><thead><tr><th>aliases</th><th>maps</th></tr></thead><tbody>",
        group_rows,
        "</tbody></table>",
    ])


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


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--selectors", type=Path, default=OUT / "save_scene_selectors.json")
    parser.add_argument("--secondary-fill-roots", type=Path, default=OUT / "save_selector_secondary_fill_roots.json")
    parser.add_argument("--sample-coverage", type=Path, default=OUT / "savedata_sample_coverage.json")
    parser.add_argument("--address-context", type=Path, default=OUT / "save_selector_address_predecessor_context.json")
    parser.add_argument("--set-decomposition", type=Path, default=OUT / "save_selector_set_decomposition.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_alias_groups(
        load_json(args.selectors, []),
        load_json(args.secondary_fill_roots, {}),
        load_json(args.sample_coverage, {}),
        load_json(args.address_context, {}),
        load_json(args.set_decomposition, {}),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote save selector map-set aliases -> {args.out_dir / 'save_selector_mapset_aliases.html'}")


if __name__ == "__main__":
    main()
