"""Phase 2Q Category-Analysis 진단 — standards/exam 회귀 case 의 3-way rank 비교.

read-only. csv parse + queries.yaml graded_relevance lookup + standards 11 + exam 7
case 의 baseline / Phase 3 (RRF fallback) / Rerank-Fix (reranker 정상) rank 1~5
박제. 회귀 큰 case top + root cause 가설.
"""
from __future__ import annotations

import csv
import sys
from pathlib import Path

REPORTS = Path("reports")
CSV_BASELINE = REPORTS / "v0_2_phase2q_baseline_rebaseline_2026-05-24.csv"
CSV_PHASE3 = REPORTS / "v0_2_phase2q_cand_multi_query_macmini_2026-05-24_cold.csv"
CSV_RERANK_FIX = REPORTS / "v0_2_phase2q_rerank_fix_2026-05-24.csv"


def parse_csv(path: Path) -> dict[str, dict]:
    """id → row dict."""
    rows: dict[str, dict] = {}
    with path.open() as f:
        reader = csv.DictReader(f)
        for row in reader:
            rows[row["id"]] = row
    return rows


def parse_graded(s: str) -> dict[int, int]:
    """`3856:3;3868:2;3879:2` → {3856:3, 3868:2, 3879:2}."""
    if not s or s == "":
        return {}
    out = {}
    for tok in s.split(";"):
        if ":" in tok:
            doc_id, grade = tok.split(":")
            out[int(doc_id)] = int(grade)
    return out


def parse_returned(s: str) -> list[int]:
    if not s:
        return []
    return [int(x) for x in s.split(";") if x]


def fmt_top(ids: list[int], graded: dict[int, int], n: int = 5) -> str:
    """top-N doc_id 와 grade. grade=3 ★★★ / grade=2 ★★ / grade=1 ★ / 0 ✗."""
    out = []
    for i, doc_id in enumerate(ids[:n], 1):
        g = graded.get(doc_id, 0)
        mark = "★" * g if g > 0 else "✗"
        out.append(f"{i}.{doc_id}{mark}")
    return " | ".join(out)


def main():
    bl = parse_csv(CSV_BASELINE)
    p3 = parse_csv(CSV_PHASE3)
    rf = parse_csv(CSV_RERANK_FIX)

    # standards (11) + exam (7) 만
    target_cats = {"standards", "exam"}
    target_ids = [i for i, r in bl.items() if r["category"] in target_cats]
    target_ids.sort(key=lambda i: (bl[i]["category"], i))

    print(f"# Phase 2Q Category-Analysis — standards/exam 18 case 3-way rank 비교\n")
    print(f"**baseline (single-query) vs Phase 3 cand (RRF fallback 다수) vs Rerank-Fix (reranker 정상)**\n")
    print(f"Phase 3 NDCG 0.927 vs Rerank-Fix NDCG 0.876 (Δ -0.051). standards -0.28 / exam -0.19 회귀의 raw case 단위 박제.\n")
    print(f"각 case 의 top-5 rank doc_id. ★★★=grade 3 (관련 매우 높음) / ★★=2 / ★=1 / ✗=non-relevant.\n")

    # category 별 graded_ndcg 비교
    print("## 1. 카테고리 별 graded_ndcg 요약\n")
    print("| Category | n | baseline | Phase 3 cand | Rerank-Fix | Δ Phase 3 → Fix |")
    print("|---|---:|---:|---:|---:|---:|")
    cat_summary = {}
    for cat in ("standards", "exam"):
        ids = [i for i in target_ids if bl[i]["category"] == cat]
        bl_avg = sum(float(bl[i].get("graded_ndcg_at_10", 0) or 0) for i in ids) / len(ids)
        p3_avg = sum(float(p3[i].get("graded_ndcg_at_10", 0) or 0) for i in ids) / len(ids)
        rf_avg = sum(float(rf[i].get("graded_ndcg_at_10", 0) or 0) for i in ids) / len(ids)
        cat_summary[cat] = (bl_avg, p3_avg, rf_avg)
        print(f"| {cat} | {len(ids)} | {bl_avg:.3f} | {p3_avg:.3f} | {rf_avg:.3f} | {rf_avg-p3_avg:+.3f} |")
    print()

    # case 별 상세
    print("## 2. case 별 top-5 rank 비교\n")
    regressions = []  # (delta, id, ...) for sorting
    for cid in target_ids:
        bl_row, p3_row, rf_row = bl[cid], p3[cid], rf[cid]
        cat = bl_row["category"]
        query = bl_row["query"]
        graded = parse_graded(bl_row.get("graded_relevance", ""))
        bl_top = parse_returned(bl_row["returned_ids_top10"])
        p3_top = parse_returned(p3_row["returned_ids_top10"])
        rf_top = parse_returned(rf_row["returned_ids_top10"])

        bl_ndcg = float(bl_row.get("graded_ndcg_at_10", 0) or 0)
        p3_ndcg = float(p3_row.get("graded_ndcg_at_10", 0) or 0)
        rf_ndcg = float(rf_row.get("graded_ndcg_at_10", 0) or 0)
        delta = rf_ndcg - p3_ndcg

        print(f"### `{cid}` ({cat}) — `{query}`\n")
        relevant_str = ", ".join(f"{d}★{'★'*(g-1) if g > 1 else ''}" for d, g in sorted(graded.items(), key=lambda x: -x[1]))
        print(f"- **graded_relevance**: {relevant_str}")
        print(f"- baseline   NDCG {bl_ndcg:.3f} top: {fmt_top(bl_top, graded)}")
        print(f"- Phase 3    NDCG {p3_ndcg:.3f} top: {fmt_top(p3_top, graded)}")
        print(f"- Rerank-Fix NDCG {rf_ndcg:.3f} top: {fmt_top(rf_top, graded)}")
        print(f"- **Δ Phase 3 → Rerank-Fix**: {delta:+.3f}\n")
        regressions.append((delta, cid, cat, query, p3_ndcg, rf_ndcg))

    # 회귀 top
    print("## 3. 회귀 큰 case top 5 (Phase 3 → Rerank-Fix)\n")
    regressions.sort(key=lambda x: x[0])
    print("| Case | Category | Δ | Phase 3 | Rerank-Fix | Query |")
    print("|---|---|---:|---:|---:|---|")
    for delta, cid, cat, query, p3v, rfv in regressions[:8]:
        print(f"| {cid} | {cat} | {delta:+.3f} | {p3v:.3f} | {rfv:.3f} | `{query[:50]}` |")
    print()
    print("## 4. 회복 큰 case top 5 (Phase 3 → Rerank-Fix)\n")
    print("| Case | Category | Δ | Phase 3 | Rerank-Fix | Query |")
    print("|---|---|---:|---:|---:|---|")
    for delta, cid, cat, query, p3v, rfv in sorted(regressions, key=lambda x: -x[0])[:5]:
        print(f"| {cid} | {cat} | {delta:+.3f} | {p3v:.3f} | {rfv:.3f} | `{query[:50]}` |")
    print()


if __name__ == "__main__":
    main()
