"""Report generation for evaluation runs."""

import json
from datetime import datetime
from pathlib import Path
from typing import Any, Optional

from jinja2 import Template

from agenteval.storage.db import DATA_DIR, iso_utc
from agenteval.storage.repository import RunRepository, ScenarioRepository, TargetRepository
from agenteval.utils.llm import extract_reply_text

HTML_TEMPLATE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>评测报告 - {{ report.run_id }}</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 40px; background: #f5f5f5; }
        .container { max-width: 960px; margin: 0 auto; background: #fff; padding: 32px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
        h1 { margin-top: 0; }
        .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; margin: 24px 0; }
        .card { background: #fafafa; border-radius: 6px; padding: 16px; text-align: center; }
        .card .value { font-size: 24px; font-weight: 700; }
        .card .label { color: #666; font-size: 14px; margin-top: 4px; }
        .case { border: 1px solid #e0e0e0; border-radius: 6px; margin: 16px 0; padding: 16px; }
        .case-title { font-weight: 600; margin-bottom: 8px; }
        .turn { background: #f9f9f9; border-radius: 4px; padding: 12px; margin: 8px 0; }
        .message-label { color: #666; font-size: 12px; }
        .rule { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
        .badge { padding: 2px 8px; border-radius: 4px; font-size: 12px; }
        .pass { background: #e6f7e6; color: #2e7d32; }
        .fail { background: #ffebee; color: #c62828; }
        pre { white-space: pre-wrap; word-break: break-word; background: #f5f5f5; padding: 8px; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>评测报告</h1>
        <p>评测对象：{{ report.target_name }}（{{ report.target_id }}）</p>
        <p>评测场景：{{ report.scenario_name }}（{{ report.scenario_id }}）</p>
        <p>执行时间：{{ report.started_at }} 至 {{ report.completed_at or '进行中' }}</p>

        <div class="summary">
            <div class="card">
                <div class="value">{{ report.summary.total_cases }}</div>
                <div class="label">用例总数</div>
            </div>
            <div class="card">
                <div class="value">{{ report.summary.passed_cases }}</div>
                <div class="label">通过用例</div>
            </div>
            <div class="card">
                <div class="value">{{ report.summary.total_rules }}</div>
                <div class="label">规则总数</div>
            </div>
            <div class="card">
                <div class="value">{{ "%.2f"|format(report.summary.pass_rate * 100) }}%</div>
                <div class="label">规则通过率</div>
            </div>
        </div>

        {% for case in report.cases %}
        <div class="case">
            <div class="case-title">用例 {{ case.case_id }}</div>
            {% for turn in case.turns %}
            <div class="turn">
                <div class="message-label">用户消息</div>
                <pre>{{ turn.sent_text }}</pre>
                <div class="message-label">智能体回复（{{ turn.latency_ms }}ms）</div>
                <pre>{{ turn.reply_text or '（无回复）' }}</pre>
            </div>
            {% endfor %}
            <div>
                {% for result in case.results %}
                <div class="rule">
                    <span class="badge {{ 'pass' if result.passed else 'fail' }}">{{ '通过' if result.passed else '失败' }}</span>
                    <span>{{ result.rule_type }}: {{ result.reason }}</span>
                </div>
                {% endfor %}
            </div>
        </div>
        {% endfor %}
    </div>
</body>
</html>
"""


def _extract_text(data: Any) -> str:
    return extract_reply_text(data)


def generate_report(run_id: str, session=None) -> dict[str, Any]:
    """Build a structured report dict for a run."""
    run_repo = RunRepository(session)
    target_repo = TargetRepository(session)
    scenario_repo = ScenarioRepository(session)

    run = run_repo.get(run_id)
    if not run:
        raise ValueError(f"run not found: {run_id}")

    target = target_repo.get(run.target_id)
    scenario = scenario_repo.get(run.scenario_id)

    turns = run_repo.get_turns(run_id)
    results = run_repo.get_results(run_id)

    # Group by case
    case_map: dict[str, dict[str, Any]] = {}
    for turn in turns:
        case_map.setdefault(turn.case_id, {"turns": [], "results": [], "all_replied": True})
        sent = turn.get_sent_message()
        reply = turn.get_reply()
        if reply is None:
            case_map[turn.case_id]["all_replied"] = False
        case_map[turn.case_id]["turns"].append(
            {
                "round": turn.round_index,
                "sent_text": _extract_text(sent.get("msgBody")),
                "reply_text": _extract_text(reply.get("msgBody") if reply else None),
                "latency_ms": turn.latency_ms,
                "question_msg_id": turn.question_msg_id,
            }
        )

    for result in results:
        case_map.setdefault(result.case_id, {"turns": [], "results": [], "all_replied": True})
        case_map[result.case_id]["results"].append(
            {
                "rule_type": result.rule_type,
                "passed": result.passed,
                "score": result.score,
                "reason": result.reason,
            }
        )

    summary = run.summary or {}
    errored_case_ids = {e.get("case_id") for e in summary.get("case_errors", [])}

    cases = []
    for case_id in sorted(case_map.keys()):
        item = case_map[case_id]
        # 连通用例：无任何判定结果，且每轮都收到回复、无用例级错误（CONTEXT.md）
        connectivity = (
            not item["results"]
            and bool(item["turns"])
            and item["all_replied"]
            and case_id not in errored_case_ids
        )
        cases.append(
            {
                "case_id": case_id,
                "connectivity": connectivity,
                "turns": sorted(item["turns"], key=lambda x: x["round"]),
                "results": item["results"],
            }
        )

    total_cases = summary.get("total_cases", 0)
    passed_cases = summary.get("passed_cases", 0)
    connectivity_count = sum(1 for c in cases if c["connectivity"])
    judged_total = total_cases - connectivity_count
    # 连通用例按引擎口径计通过，判定型通过数 = 总通过数 - 连通用例数
    judged_pass_rate = round((passed_cases - connectivity_count) / judged_total, 4) if judged_total > 0 else None

    return {
        "run_id": run.id,
        "target_id": run.target_id,
        "target_name": target.name if target else "未知",
        "scenario_id": run.scenario_id,
        "scenario_name": scenario.name if scenario else "未知",
        "scenario_version": run.scenario_version,
        "status": run.status.value,
        "started_at": iso_utc(run.started_at),
        "completed_at": iso_utc(run.completed_at),
        "summary": {
            "total_cases": total_cases,
            "passed_cases": passed_cases,
            "failed_cases": summary.get("failed_cases", 0),
            "total_rules": summary.get("total_rules", 0),
            "passed_rules": summary.get("passed_rules", 0),
            "pass_rate": summary.get("pass_rate", 0.0),
            "connectivity_cases": connectivity_count,
            "judged_pass_rate": judged_pass_rate,
        },
        "cases": cases,
    }


def generate_compare_report(run_id_1: str, run_id_2: str, session=None) -> dict[str, Any]:
    """Build a side-by-side comparison dict for two runs of the same scenario."""
    report_a = generate_report(run_id_1, session)
    report_b = generate_report(run_id_2, session)

    # Cross-scenario case_ids never overlap, so every case would be flagged
    # "changed" and the diff would be meaningless — reject early.
    if report_a.get("scenario_id") != report_b.get("scenario_id"):
        raise ValueError("compare report requires both runs to use the same scenario")
    # 同场景还须同考纲版本才可比（ADR-0001）
    if report_a.get("scenario_version") != report_b.get("scenario_version"):
        raise ValueError(
            "compare report requires the same scenario version "
            f"(A: v{report_a.get('scenario_version')}, B: v{report_b.get('scenario_version')})"
        )

    def _summary_delta(key: str) -> float:
        return report_b["summary"][key] - report_a["summary"][key]

    # Case-level diff: match by case_id
    cases_a = {c["case_id"]: c for c in report_a.get("cases", [])}
    cases_b = {c["case_id"]: c for c in report_b.get("cases", [])}
    all_case_ids = sorted(set(cases_a) | set(cases_b))

    case_diffs = []
    for cid in all_case_ids:
        ca = cases_a.get(cid)
        cb = cases_b.get(cid)

        def _case_passed(c):
            if not c:
                return None
            if c.get("connectivity"):
                # 连通用例收到回复即通过（引擎口径）
                return True
            results = c.get("results", [])
            if not results:
                # No rule results (e.g. errored case) must not count as passed.
                return None
            return all(r["passed"] for r in results)

        case_diffs.append(
            {
                "case_id": cid,
                "connectivity": bool((ca and ca.get("connectivity")) or (cb and cb.get("connectivity"))),
                "run_a_passed": _case_passed(ca),
                "run_b_passed": _case_passed(cb),
                "changed": _case_passed(ca) != _case_passed(cb),
                "run_a_results": ca["results"] if ca else [],
                "run_b_results": cb["results"] if cb else [],
            }
        )

    return {
        "run_a": {
            "run_id": run_id_1,
            "target_name": report_a.get("target_name"),
            "scenario_name": report_a.get("scenario_name"),
            "scenario_version": report_a.get("scenario_version"),
            "status": report_a.get("status"),
            "started_at": report_a.get("started_at"),
            "summary": report_a["summary"],
        },
        "run_b": {
            "run_id": run_id_2,
            "target_name": report_b.get("target_name"),
            "scenario_name": report_b.get("scenario_name"),
            "scenario_version": report_b.get("scenario_version"),
            "status": report_b.get("status"),
            "started_at": report_b.get("started_at"),
            "summary": report_b["summary"],
        },
        "delta": {
            "pass_rate": round(_summary_delta("pass_rate"), 4),
            "passed_cases": int(_summary_delta("passed_cases")),
            "passed_rules": int(_summary_delta("passed_rules")),
        },
        "cases": case_diffs,
        "changed_cases": sum(1 for c in case_diffs if c["changed"]),
    }


def render_markdown_report(run_id: str, session=None) -> str:
    """Render a report as Markdown string."""
    report = generate_report(run_id, session)
    s = report["summary"]
    judged_rate = s.get("judged_pass_rate")
    judged_rate_text = "—" if judged_rate is None else f"{judged_rate * 100:.1f}%"
    lines: list[str] = [
        f"# 评测报告 — {report.get('scenario_name', run_id)}",
        "",
        f"**评测对象**: {report.get('target_name', '-')}  ",
        f"**评测场景**: {report.get('scenario_name', '-')}  ",
        f"**状态**: {report.get('status', '-')}  ",
        f"**开始时间**: {report.get('started_at', '-')}  ",
        f"**完成时间**: {report.get('completed_at', '-')}  ",
        "",
        "## 汇总",
        "",
        "| 指标 | 数值 |",
        "|------|------|",
        f"| 总用例数 | {s['total_cases']} |",
        f"| 通过用例 | {s['passed_cases']} |",
        f"| 失败用例 | {s['failed_cases']} |",
        f"| 总规则数 | {s['total_rules']} |",
        f"| 通过规则 | {s['passed_rules']} |",
        f"| 通过率 | {s['pass_rate'] * 100:.1f}% |",
        f"| 连通用例 | {s.get('connectivity_cases', 0)} |",
        f"| 判定型通过率 | {judged_rate_text} |",
        "",
        "## 用例明细",
        "",
    ]

    for case in report.get("cases", []):
        if case.get("connectivity"):
            badge = "🔗"
        else:
            case_passed = all(r["passed"] for r in case.get("results", []))
            badge = "✅" if case_passed else "❌"
        title = f"### {badge} 用例 `{case['case_id']}`"
        if case.get("connectivity"):
            title += "（连通用例，未配置判定标准）"
        lines.append(title)
        lines.append("")

        for turn in case.get("turns", []):
            lines.append(f"**第 {turn['round']} 轮**")
            lines.append("")
            lines.append(f"> **用户**: {turn.get('sent_text', '—')}")
            lines.append("")
            reply = turn.get("reply_text") or "（无回复）"
            lines.append(f"> **智能体**: {reply}")
            if turn.get("latency_ms") is not None:
                lines.append(f"> *延迟: {turn['latency_ms']}ms*")
            lines.append("")

        if case.get("results"):
            lines.append("**规则评估结果**")
            lines.append("")
            lines.append("| 规则 | 结果 | 评分 | 说明 |")
            lines.append("|------|------|------|------|")
            for r in case["results"]:
                badge = "✅" if r["passed"] else "❌"
                score = f"{r['score']:.2f}" if r.get("score") is not None else "-"
                lines.append(f"| {r['rule_type']} | {badge} | {score} | {r.get('reason', '')} |")
            lines.append("")

    return "\n".join(lines)


def render_json_report(run_id: str, session=None) -> str:
    """Render a report as JSON string."""
    report = generate_report(run_id, session)
    return json.dumps(report, ensure_ascii=False, indent=2)


def render_html_report(run_id: str, session=None) -> str:
    """Render a report as HTML string."""
    report = generate_report(run_id, session)
    template = Template(HTML_TEMPLATE)
    return template.render(report=report)


def save_report(run_id: str, fmt: str = "html", output_dir: Optional[Path] = None) -> Path:
    """Generate and save a report to disk."""
    output_dir = output_dir or DATA_DIR / "reports"
    output_dir.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    if fmt == "html":
        content = render_html_report(run_id)
        path = output_dir / f"report_{run_id}_{timestamp}.html"
    elif fmt == "json":
        content = render_json_report(run_id)
        path = output_dir / f"report_{run_id}_{timestamp}.json"
    elif fmt == "markdown":
        content = render_markdown_report(run_id)
        path = output_dir / f"report_{run_id}_{timestamp}.md"
    else:
        raise ValueError(f"unsupported report format: {fmt}")

    path.write_text(content, encoding="utf-8")
    return path
