from __future__ import annotations

import csv
import sys
from collections import Counter
from pathlib import Path
from typing import Dict, Iterable, List, Tuple


def _load_rows(path: Path) -> List[Dict[str, str]]:
    with path.open("r", encoding="utf-8-sig", newline="") as f:
        reader = csv.DictReader(f)
        return [
            {str(k).strip(): (str(v).strip() if v is not None else "") for k, v in row.items()}
            for row in reader
        ]


def _norm_name(name: str) -> str:
    text = (name or "").replace("\\", "/").split("/")[-1].strip().strip('"')
    if text.startswith("Finished Render Models-"):
        text = text[len("Finished Render Models-") :]
    return text


def _keyed_by(rows: Iterable[Dict[str, str]], *, field: str) -> Dict[str, Dict[str, str]]:
    out: Dict[str, Dict[str, str]] = {}
    for row in rows:
        k = (row.get(field) or "").strip()
        if k:
            out[k] = row
    return out


def _diff_maps(
    a: Dict[str, Dict[str, str]],
    b: Dict[str, Dict[str, str]],
    *,
    compare_fields: Tuple[str, ...] = ("URL", "Key", "Size", "Modified"),
) -> Dict[str, object]:
    ka = set(a)
    kb = set(b)
    added = sorted(kb - ka)
    removed = sorted(ka - kb)
    common = sorted(ka & kb)
    changed: List[Tuple[str, List[str]]] = []
    change_fields = Counter()
    for k in common:
        diffs = [f for f in compare_fields if (a[k].get(f) or "") != (b[k].get(f) or "")]
        if diffs:
            changed.append((k, diffs))
            change_fields.update(diffs)
    return {
        "keys_a": len(ka),
        "keys_b": len(kb),
        "common": len(common),
        "added": len(added),
        "removed": len(removed),
        "changed": len(changed),
        "change_fields": dict(change_fields),
        "sample_added": added[:20],
        "sample_removed": removed[:20],
        "sample_changed": changed[:20],
    }


def main(argv: List[str]) -> int:
    if len(argv) != 3:
        print("Usage: python scripts/diff_manifest.py <manifestA.csv> <manifestB.csv>")
        print("Compares A → B (added/removed/changed).")
        return 2

    a_path = Path(argv[1])
    b_path = Path(argv[2])
    a_rows = _load_rows(a_path)
    b_rows = _load_rows(b_path)

    print(f"A: {a_path} rows={len(a_rows)}")
    print(f"B: {b_path} rows={len(b_rows)}")
    print()

    # 1) Strict diff on Key (most stable if folder prefix changes)
    a_by_key = _keyed_by(a_rows, field="Key")
    b_by_key = _keyed_by(b_rows, field="Key")
    print("== Diff by Key ==")
    print(_diff_maps(a_by_key, b_by_key))
    print()

    # 2) Diff by Name (verbatim)
    a_by_name = _keyed_by(a_rows, field="Name")
    b_by_name = _keyed_by(b_rows, field="Name")
    print("== Diff by Name ==")
    print(_diff_maps(a_by_name, b_by_name))
    print()

    # 3) Diff by normalized Name (strip 'Finished Render Models-' only)
    a_norm = {_norm_name(r.get("Name", "")): r for r in a_rows if _norm_name(r.get("Name", ""))}
    b_norm = {_norm_name(r.get("Name", "")): r for r in b_rows if _norm_name(r.get("Name", ""))}
    print("== Diff by normalized Name ==")
    print(_diff_maps(a_norm, b_norm))
    print()

    # 4) Quick string scan for ASM-BRO
    needle = "ASM-BRO"
    hits = []
    for tag, rows in (("A", a_rows), ("B", b_rows)):
        for r in rows:
            blob = f"{r.get('Name','')} {r.get('Key','')}"
            if needle in blob:
                hits.append((tag, r.get("Name", ""), r.get("Key", "")))
    print(f"== {needle} matches ==")
    print({"count": len(hits), "sample": hits[:20]})
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))

