"""Markdown report generator for evaluation results."""

from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List

import numpy as np

from .config import EXPERIMENT_CONFIG

CONDITIONS = EXPERIMENT_CONFIG["conditions"]


def _sig(p: float) -> str:
    if p < 0.001:
        return "***"
    if p < 0.01:
        return "**"
    if p < 0.05:
        return "*"
    return "ns"


def generate_report(
    *,
    run_dir: Path,
    corpus: List[Any],
    scores: Dict,
    pairwise: List[Any],
    bt_strengths: Dict[str, float],
    elo_ratings: Dict[str, float],
    hypotheses: Dict[str, Dict],
) -> Path:
    """Write a Markdown report summarising the experiment run."""
    lines: List[str] = []
    a = lines.append

    a(f"# Dusun MCP Evaluation Report")
    a(f"")
    a(f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    a(f"**Git commit:** `{EXPERIMENT_CONFIG.get('git_commit', 'unknown')}`")
    a(f"**Corpus size:** {len(corpus)} texts")
    a(f"**Conditions:** {', '.join(CONDITIONS)}")
    a(f"")

    # ── Summary table ─────────────────────────────────────────────
    a("## Summary Metrics")
    a("")
    a("| Condition | Lens Coverage | ADS (mean) | CLIS (mean) | Discovery (mean) |")
    a("|-----------|:------------:|:----------:|:-----------:|:----------------:|")
    for cond in CONDITIONS:
        lc = [scores[tid][cond]["lens_coverage"] for tid in scores]
        ads = [scores[tid][cond]["ads"] for tid in scores]
        clis = [scores[tid][cond]["clis"] for tid in scores]
        disc = [scores[tid][cond]["discovery"] for tid in scores]
        a(
            f"| {cond} | {np.mean(lc):.2f} | {np.mean(ads):.2f} "
            f"| {np.mean(clis):.2f} | {np.mean(disc):.2f} |"
        )
    a("")

    # ── Bradley-Terry / Elo ───────────────────────────────────────
    a("## Bradley-Terry Rankings")
    a("")
    a("| Condition | Strength | Elo |")
    a("|-----------|:--------:|:---:|")
    for cond in sorted(elo_ratings, key=elo_ratings.get, reverse=True):
        a(f"| {cond} | {bt_strengths[cond]:.4f} | {elo_ratings[cond]:.0f} |")
    a("")

    # ── Hypothesis results ────────────────────────────────────────
    a("## Hypothesis Tests")
    a("")
    for hid, hdata in sorted(hypotheses.items()):
        a(f"### {hid}")
        a("")
        a("```json")
        a(json.dumps(hdata, indent=2, default=str))
        a("```")
        a("")

    # ── Per-domain breakdown ──────────────────────────────────────
    domains = sorted({t.domain for t in corpus})
    a("## Per-Domain ADS Means")
    a("")
    header = "| Domain | " + " | ".join(CONDITIONS) + " |"
    sep = "|--------|" + "|".join(":---:" for _ in CONDITIONS) + "|"
    a(header)
    a(sep)
    for domain in domains:
        tids = [t.id for t in corpus if t.domain == domain]
        vals = []
        for cond in CONDITIONS:
            m = np.mean([scores[tid][cond]["ads"] for tid in tids])
            vals.append(f"{m:.2f}")
        a(f"| {domain} | " + " | ".join(vals) + " |")
    a("")

    # ── Pairwise summary ─────────────────────────────────────────
    a("## Pairwise Winner Counts")
    a("")
    win_count: Dict[str, int] = {c: 0 for c in CONDITIONS}
    tie_count = 0
    for r in pairwise:
        if r.true_winner == "TIE":
            tie_count += 1
        elif r.true_winner in win_count:
            win_count[r.true_winner] += 1
    a("| Condition | Wins |")
    a("|-----------|:----:|")
    for c in sorted(win_count, key=win_count.get, reverse=True):
        a(f"| {c} | {win_count[c]} |")
    a(f"| TIE | {tie_count} |")
    a("")

    # Write to disk
    report_path = run_dir / "report.md"
    report_path.write_text("\n".join(lines), encoding="utf-8")

    # Also dump raw data
    (run_dir / "hypotheses.json").write_text(
        json.dumps(hypotheses, indent=2, default=str, ensure_ascii=False),
        encoding="utf-8",
    )
    (run_dir / "elo_ratings.json").write_text(
        json.dumps(elo_ratings, indent=2, ensure_ascii=False),
        encoding="utf-8",
    )

    return report_path


def regenerate_report(run_dir: Path) -> Path:
    """Re-generate report from persisted JSON files in *run_dir*."""
    # Lazy imports to avoid circular deps
    from .corpus.loader import load_corpus

    corpus = load_corpus()

    scores_dir = run_dir / "scores"
    scores: Dict[str, Dict[str, Dict]] = {}
    for cond_dir in scores_dir.iterdir():
        if not cond_dir.is_dir():
            continue
        cond = cond_dir.name
        for fp in cond_dir.glob("*.json"):
            tid = fp.stem
            data = json.loads(fp.read_text(encoding="utf-8"))
            scores.setdefault(tid, {})[cond] = data

    pw_path = run_dir / "pairwise" / "all_comparisons.json"
    pairwise_raw = json.loads(pw_path.read_text(encoding="utf-8"))

    from .judges.pairwise import PairwiseResult

    pairwise = [PairwiseResult(**r) for r in pairwise_raw]

    from .stats.bradley_terry import fit_bradley_terry, strengths_to_elo

    bt = fit_bradley_terry(
        json.loads((run_dir / "pairwise" / "bt_records.json").read_text(encoding="utf-8")),
        CONDITIONS,
    )
    elo = strengths_to_elo(bt)

    hyp = json.loads(
        (run_dir / "hypotheses.json").read_text(encoding="utf-8")
    )

    return generate_report(
        run_dir=run_dir,
        corpus=corpus,
        scores=scores,
        pairwise=pairwise,
        bt_strengths=bt,
        elo_ratings=elo,
        hypotheses=hyp,
    )
