"""
exporter.py — Audit & Certification Export Engine for LLMAuditor.

Two export paths:
  1. export_execution()      — single execution audit (MD / HTML / PDF)
  2. export_certification()  — full evaluation certification (MD / HTML / PDF)

Certification report sections:
  1. Application Identity    7. Scoring Breakdown
  2. Evaluation Summary      8. Certification Verdict
  3. Metrics & Distributions 9. Improvement Recommendations
  4. Hallucination Analysis 10. Integrity Hash
  5. Governance Compliance  11. Understanding the Numbers
  6. Stability Analysis     12. Report Summary (Plain Language)
  + Certification Stamp (top-right)  + Digital Signature (bottom)
"""

from __future__ import annotations

import hashlib
import json
import math
import os
import textwrap
from datetime import datetime
from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    from llmauditor.evaluation import EvaluationReport

_VERSION = "LLMAuditor v1.1.0"


# ═══════════════════════════════════════════════════════════════════════════════
#  PART 1 — Per-Execution Audit Export (preserved from llmsupervisor)
# ═══════════════════════════════════════════════════════════════════════════════

def export_execution(
    data: dict[str, Any],
    quality: dict[str, Any],
    fmt: str,
    output_dir: str = ".",
) -> str:
    """Export a single execution audit report."""
    try:
        return _export_execution_impl(data, quality, fmt, output_dir)
    except Exception as exc:
        return f"Export failed: {exc}"


def _export_execution_impl(
    data: dict[str, Any],
    quality: dict[str, Any],
    fmt: str,
    output_dir: str = ".",
) -> str:
    """Internal export logic (separated for error isolation)."""
    fmt = fmt.lower().strip()
    if fmt not in ("md", "html", "pdf"):
        raise ValueError(f"Unsupported format: '{fmt}'")

    os.makedirs(output_dir, exist_ok=True)
    integrity_hash = _hash(data)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    eid_short = str(data.get("execution_id", "unknown"))[:8]
    base = f"audit_{eid_short}_{ts}"

    if fmt == "md":
        content = _exec_md(data, quality, integrity_hash)
        path = os.path.join(output_dir, f"{base}.md")
        _write(path, content)
    elif fmt == "html":
        content = _exec_html(data, quality, integrity_hash)
        path = os.path.join(output_dir, f"{base}.html")
        _write(path, content)
    else:
        path = os.path.join(output_dir, f"{base}.pdf")
        _exec_pdf(data, quality, integrity_hash, path)

    return os.path.abspath(path)


# ── Execution: Markdown ───────────────────────────────────────────────────── #

def _exec_md(data: dict, quality: dict, ihash: str) -> str:
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    conf = quality.get("confidence_score", "N/A")
    risk = quality.get("risk_level", "N/A")
    notes = quality.get("notes", [])
    summary = quality.get("summary", "")

    lines = [
        "# AI Execution Audit Report", "",
        f"> Generated by **{_VERSION}**", "", "---", "",
        "## Cover", "",
        "| Field | Value |", "|---|---|",
        f"| Execution ID | `{data.get('execution_id', '')}` |",
        f"| Model | {data.get('model_name', '')} |",
        f"| Date & Time | {ts} |",
        f"| Generated By | {_VERSION} |",
        "", "---", "", "## Executive Summary", "", summary,
        "", "---", "", "## Execution Overview", "",
        "| Metric | Value |", "|---|---|",
        f"| Model | {data.get('model_name', '')} |",
        f"| Execution Time | {data.get('execution_time', '')} sec |",
        f"| Input Tokens | {data.get('input_tokens', 0):,} |",
        f"| Output Tokens | {data.get('output_tokens', 0):,} |",
        f"| Total Tokens | {data.get('total_tokens', 0):,} |",
        f"| Estimated Cost | ${data.get('estimated_cost', 0):.6f} USD |",
        f"| Confidence | {conf}% |",
        f"| Risk Level | {risk} |",
        "", "---", "", "## Quality & Risk Assessment", "",
    ]
    for n in notes:
        lines.append(f"- {n}")

    # Hallucination section
    hal = data.get("hallucination")
    if hal:
        lines += ["", "---", "", "## Hallucination Analysis", "",
                   "| Metric | Value |", "|---|---|",
                   f"| Risk Score | {hal.get('risk_score_pct', 'N/A')}% ({hal.get('risk_level', 'N/A')}) |",
                   f"| Method | {hal.get('method', 'N/A')} |",
                   f"| Factual Claims | {hal.get('factual_claims_count', 0)} |",
                   f"| Hedging Ratio | {hal.get('hedging_ratio', 0):.2%} |",
                   f"| Absolute Claims | {hal.get('absolute_claims_count', 0)} |"]

    lines += ["", "---", "",
              f"**Integrity Hash:** `{ihash}`", "",
              f"*Generated at {ts} by {_VERSION}*"]

    return "\n".join(lines)


# ── Execution: HTML ───────────────────────────────────────────────────────── #

def _exec_html(data: dict, quality: dict, ihash: str) -> str:
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    conf = quality.get("confidence_score", "N/A")
    risk = quality.get("risk_level", "N/A")
    notes = quality.get("notes", [])
    summary = quality.get("summary", "")

    conf_color = "#27ae60" if conf >= 85 else "#f39c12" if conf >= 70 else "#e74c3c"
    risk_color = "#27ae60" if risk == "LOW" else "#f39c12"
    notes_html = "\n".join(f"<li>{n}</li>" for n in notes)

    return _html_shell(
        title="AI Execution Audit Report",
        subtitle=f"{_VERSION} | {ts}",
        body=f"""
        <div class="section"><h2>Executive Summary</h2>
            <div class="summary-box">{summary}</div></div>
        <div class="section"><h2>Execution Overview</h2>
            <table><tr><th>Metric</th><th>Value</th></tr>
            <tr><td>Model</td><td>{data.get('model_name','')}</td></tr>
            <tr><td>Execution Time</td><td>{data.get('execution_time','')} sec</td></tr>
            <tr><td>Input Tokens</td><td>{data.get('input_tokens',0):,}</td></tr>
            <tr><td>Output Tokens</td><td>{data.get('output_tokens',0):,}</td></tr>
            <tr><td>Total Tokens</td><td><strong>{data.get('total_tokens',0):,}</strong></td></tr>
            <tr><td>Estimated Cost</td><td><strong>${data.get('estimated_cost',0):.6f} USD</strong></td></tr>
            <tr><td>Confidence</td><td><span class="badge" style="background:{conf_color}">{conf}%</span></td></tr>
            <tr><td>Risk Level</td><td><span class="badge" style="background:{risk_color}">{risk}</span></td></tr>
            </table></div>
        <div class="section"><h2>Quality &amp; Risk</h2>
            <ul class="notes-list">{notes_html}</ul></div>
        """,
        footer=f"Integrity Hash: {ihash} | {ts} | {_VERSION}",
    )


# ── Execution: PDF ────────────────────────────────────────────────────────── #

def _exec_pdf(data: dict, quality: dict, ihash: str, path: str) -> None:
    rl = _import_reportlab()
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    conf = quality.get("confidence_score", "N/A")
    risk = quality.get("risk_level", "N/A")
    notes = quality.get("notes", [])
    summary = quality.get("summary", "")

    doc, styles, story, colors_map = _pdf_base(path, "AI Execution Audit Report", ts)

    story += _pdf_section("Cover", styles)
    story.append(_pdf_table([
        ["Field", "Value"],
        ["Execution ID", str(data.get("execution_id", ""))],
        ["Model", str(data.get("model_name", ""))],
        ["Date & Time", ts],
        ["Generated By", _VERSION],
    ], colors_map))
    story.append(rl["Spacer"](1, 4 * rl["mm"]))

    story += _pdf_section("Executive Summary", styles)
    story.append(rl["Paragraph"](summary, styles["summary"]))
    story.append(rl["Spacer"](1, 4 * rl["mm"]))

    story += _pdf_section("Execution Overview", styles)
    story.append(_pdf_table([
        ["Metric", "Value"],
        ["Model", str(data.get("model_name", ""))],
        ["Execution Time", f"{data.get('execution_time', '')} sec"],
        ["Input Tokens", f"{data.get('input_tokens', 0):,}"],
        ["Output Tokens", f"{data.get('output_tokens', 0):,}"],
        ["Total Tokens", f"{data.get('total_tokens', 0):,}"],
        ["Estimated Cost", f"${data.get('estimated_cost', 0):.6f} USD"],
        ["Confidence", f"{conf}%"],
        ["Risk Level", str(risk)],
    ], colors_map))
    story.append(rl["Spacer"](1, 4 * rl["mm"]))

    story += _pdf_section("Quality & Risk Assessment", styles)
    for note in notes:
        story.append(rl["Paragraph"](f"\u2022 {note}", styles["body"]))

    # Hallucination section
    hal = data.get("hallucination")
    if hal:
        story.append(rl["Spacer"](1, 4 * rl["mm"]))
        story += _pdf_section("Hallucination Analysis", styles)
        story.append(_pdf_table([
            ["Metric", "Value"],
            ["Risk Score", f"{hal.get('risk_score_pct', 'N/A')}% ({hal.get('risk_level', 'N/A')})"],
            ["Method", str(hal.get("method", "N/A"))],
            ["Factual Claims", str(hal.get("factual_claims_count", 0))],
            ["Hedging Ratio", f"{hal.get('hedging_ratio', 0):.2%}"],
        ], colors_map))

    # Integrity hash
    story.append(rl["Spacer"](1, 6 * rl["mm"]))
    story.append(rl["HRFlowable"](width="100%", thickness=0.5,
                 color=colors_map["LIGHT_GREY"], spaceAfter=4, spaceBefore=6))
    story.append(rl["Paragraph"](
        f"<font size='7' color='#7f8c8d'><b>Integrity Hash:</b> {ihash}</font>",
        styles["body"]))

    doc.build(story, onFirstPage=_pdf_page_handler(ts, "AI Execution Audit Report"),
              onLaterPages=_pdf_page_handler(ts, "AI Execution Audit Report"))


# ═══════════════════════════════════════════════════════════════════════════════
#  PART 2 — Certification Report Export (NEW)
# ═══════════════════════════════════════════════════════════════════════════════

def _generate_cert_number(ihash: str) -> str:
    """Generate a unique certificate number from integrity hash and timestamp."""
    ts = datetime.now().strftime("%Y%m%d")
    return f"LMA-{ts}-{ihash[:6].upper()}"


def export_certification(report: "EvaluationReport", fmt: str, output_dir: str = ".") -> str:
    """Export a full evaluation certification report."""
    try:
        return _export_certification_impl(report, fmt, output_dir)
    except Exception as exc:
        return f"Export failed: {exc}"


def _export_certification_impl(report: "EvaluationReport", fmt: str, output_dir: str = ".") -> str:
    """Internal certification export logic (separated for error isolation)."""
    fmt = fmt.lower().strip()
    if fmt not in ("md", "html", "pdf"):
        raise ValueError(f"Unsupported format: '{fmt}'")

    os.makedirs(output_dir, exist_ok=True)
    ihash = _hash(report.to_dict())
    cert_number = _generate_cert_number(ihash)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    base = f"certification_{report.session.app_name.replace(' ', '_')}_{ts}"

    if fmt == "md":
        content = _cert_md(report, ihash, cert_number)
        path = os.path.join(output_dir, f"{base}.md")
        _write(path, content)
    elif fmt == "html":
        content = _cert_html(report, ihash, cert_number)
        path = os.path.join(output_dir, f"{base}.html")
        _write(path, content)
    else:
        path = os.path.join(output_dir, f"{base}.pdf")
        _cert_pdf(report, ihash, cert_number, path)

    return os.path.abspath(path)


def export_certification_all(report: "EvaluationReport", output_dir: str = ".") -> dict[str, str]:
    """
    Export the certification report in all three formats (MD, HTML, PDF).

    Returns a dict mapping format name to absolute file path:
        {"md": "/path/to.md", "html": "/path/to.html", "pdf": "/path/to.pdf"}
    """
    os.makedirs(output_dir, exist_ok=True)
    ihash = _hash(report.to_dict())
    cert_number = _generate_cert_number(ihash)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    base = f"certification_{report.session.app_name.replace(' ', '_')}_{ts}"
    results: dict[str, str] = {}

    # Markdown
    try:
        md_content = _cert_md(report, ihash, cert_number)
        md_path = os.path.join(output_dir, f"{base}.md")
        _write(md_path, md_content)
        results["md"] = os.path.abspath(md_path)
    except Exception as exc:
        results["md"] = f"ERROR: {exc}"

    # HTML
    try:
        html_content = _cert_html(report, ihash, cert_number)
        html_path = os.path.join(output_dir, f"{base}.html")
        _write(html_path, html_content)
        results["html"] = os.path.abspath(html_path)
    except Exception as exc:
        results["html"] = f"ERROR: {exc}"

    # PDF
    try:
        pdf_path = os.path.join(output_dir, f"{base}.pdf")
        _cert_pdf(report, ihash, cert_number, pdf_path)
        results["pdf"] = os.path.abspath(pdf_path)
    except Exception as exc:
        results["pdf"] = f"ERROR: {exc}"

    return results


# ── Certification: Markdown ───────────────────────────────────────────────── #

def _cert_md(r: "EvaluationReport", ihash: str, cert_number: str) -> str:
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    s = r.score
    m = r.metrics
    sess = r.session

    # Mode label
    mode_raw = getattr(sess, "mode", "simulated") or "simulated"
    is_live = mode_raw.lower() in ("live", "api", "real")
    mode_label = "With API Call" if is_live else "Without API Call"

    lines = [
        "# GenAI Application Certification Report", "",
        f"> Generated by **{_VERSION}**", "",
        "---", "",
        '<div align="center">', "",
        "**\u2605 CERTIFIED \u2605**", "",
        f"### \U0001f3c6 {s.level.upper()} \U0001f3c6", "",
        f"\U0001f4cb **Certificate No:** `{cert_number}`", "",
        f"*Issued by {_VERSION} Certification Authority*", "",
        "</div>", "",
        "---", "",

        # Section 1 — Application Identity
        "## 1. Application Identity", "",
        "| Field | Value |", "|---|---|",
        f"| Application | {sess.app_name} |",
        f"| Version | {sess.version} |",
        f"| Evaluation Mode | {mode_label} |",
        f"| Evaluation Period | {sess.start_time:%Y-%m-%d %H:%M} → {(sess.end_time or datetime.now()):%Y-%m-%d %H:%M} |",
        f"| Duration | {sess.duration_seconds:.1f}s |",
        f"| Total Executions | {m.total_runs} |",
        f"| Models Used | {', '.join(m.models_used) or 'N/A'} |",
        "", "---", "",

        # Section 2 — Evaluation Summary
        "## 2. Evaluation Summary", "",
        f"**Overall Score: {s.overall:.1f}/100**", "",
        f"**Certification Level: {s.level_emoji} {s.level}**", "",
        "", "---", "",

        # Section 3 — Metrics & Distributions
        "## 3. Metrics & Distributions", "",
        "### Token Usage", "",
        "| Metric | Mean | StdDev | Min | Max |", "|---|---|---|---|---|",
        f"| Total Tokens | {m.token_stats.mean:.0f} | {m.token_stats.stddev:.0f} | {m.token_stats.min_val:.0f} | {m.token_stats.max_val:.0f} |",
        f"| Input Tokens | {m.input_token_stats.mean:.0f} | {m.input_token_stats.stddev:.0f} | {m.input_token_stats.min_val:.0f} | {m.input_token_stats.max_val:.0f} |",
        f"| Output Tokens | {m.output_token_stats.mean:.0f} | {m.output_token_stats.stddev:.0f} | {m.output_token_stats.min_val:.0f} | {m.output_token_stats.max_val:.0f} |",
        "",
        "### Cost", "",
        "| Metric | Value |", "|---|---|",
        f"| Total Cost | ${m.cost_stats.total:.6f} |",
        f"| Mean Cost | ${m.cost_stats.mean:.6f} |",
        f"| StdDev | ${m.cost_stats.stddev:.6f} |",
        "",
        "### Latency", "",
        f"| Mean | {m.latency_stats.mean:.3f}s | StdDev | {m.latency_stats.stddev:.3f}s | Max | {m.latency_stats.max_val:.3f}s |",
        "", "---", "",

        # Section 4 — Hallucination Analysis
        "## 4. Hallucination Analysis", "",
    ]

    if m.hallucination_stats.count > 0:
        h = m.hallucination_stats
        lines += [
            "| Metric | Value |", "|---|---|",
            f"| Mean Risk | {h.mean:.1%} |",
            f"| Max Risk | {h.max_val:.1%} |",
            f"| Min Risk | {h.min_val:.1%} |",
            f"| Executions Analysed | {h.count} |",
        ]
    else:
        lines.append("*No hallucination analysis data available.*")

    lines += ["", "---", "",

        # Section 5 — Governance Compliance
        "## 5. Governance Compliance", "",
        "| Metric | Value |", "|---|---|",
        f"| Guard Violations | {m.guard_violations} |",
        f"| Budget Violations | {m.budget_violations} |",
        f"| Total Warnings | {m.total_warnings} |",
        "", "---", "",

        # Section 6 — Stability Analysis
        "## 6. Stability Analysis", "",
        f"- Failure Rate: {m.failure_rate:.1%}",
        f"- Latency CV: {m.latency_stats.stddev / max(m.latency_stats.mean, 0.001):.3f}",
        f"- Token CV: {m.token_stats.stddev / max(m.token_stats.mean, 1):.3f}",
        "", "---", "",

        # Section 7 — Scoring Breakdown
        "## 7. Scoring Breakdown", "",
        "| Subscore | Value | Weight |", "|---|---|---|",
    ]

    for name, val in s.subscores.items():
        w = s.weights.get(name, 0)
        lines.append(f"| {name.replace('_', ' ').title()} | {val:.1f} | {w:.0%} |")

    lines += ["", "---", "",

        # Section 8 — Certification Verdict
        "## 8. Certification Verdict", "",
        f"### {s.level_emoji} {s.level}  —  {s.overall:.1f}/100", "",
        "", "---", "",

        # Section 9 — Improvement Recommendations
        "## 9. Improvement Recommendations", "",
    ]

    if r.suggestions:
        for sg in r.suggestions:
            lines.append(f"- **[{sg.severity.upper()}] {sg.title}**: {sg.detail}")
    else:
        lines.append("*No improvement recommendations — all metrics are within acceptable thresholds.*")

    lines += ["", "---", "",

        # Section 10 — Integrity Hash
        "## 10. Integrity Hash", "",
        f"`{ihash}`", "",
        "", "---", "",

        # Section 11 — Understanding the Numbers
        "## 11. Understanding the Numbers", "",
        "> **What do the statistics above mean?** Here is a detailed "
        "explanation for non-technical readers.", "",
    ]

    for item in _build_detailed_explanation(r):
        lines.append(f"### {item['term']}")
        lines.append(f"")
        lines.append(f"**Value:** {item['value']}")
        lines.append(f"")
        lines.append(f"{item['explanation']}")
        lines.append(f"")

    lines += [
        "---", "",

        # Section 12 — Plain Language Summary
        "## 12. Report Summary", "",
        "> **What does this report mean?** Here is a simple summary "
        "anyone can understand.", "",
    ]

    for item in _build_plain_summary(r):
        lines.append(f"### {item['icon']}  {item['heading']}")
        lines.append(f"")
        lines.append(f"{item['text']}")
        lines.append(f"")

    # ── Digital Signature ──────────────────────────────────────────────── #
    lines += [
        "---", "",
        "## \u270d\ufe0f Digital Signature", "",
        '<div align="center">', "",
        "| Field | Value |", "|:---|:---|",
        f"| **Certificate No** | `{cert_number}` |",
        f"| **Issued By** | {_VERSION} \u2014 Automated Certification Engine |",
        f"| **Issue Date** | {ts} |",
        f"| **Integrity Hash** | `{ihash}` |", "",
        "---", "",
        "*This certificate was digitally signed and generated automatically*  ",
        f"*by the {_VERSION} Certification Engine.*  ",
        "*No human intervention was involved in the evaluation process.*", "",
        "</div>", "",
    ]

    lines += [
        "---", "",
        f"*Generated at {ts} by {_VERSION}*",
    ]

    return "\n".join(lines)


# ── Certification: HTML ───────────────────────────────────────────────────── #

def _cert_html(r: "EvaluationReport", ihash: str, cert_number: str) -> str:
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    s = r.score
    m = r.metrics
    sess = r.session

    level_colors = {"Platinum": "#00d4ff", "Gold": "#f1c40f", "Silver": "#bdc3c7",
                    "Conditional Pass": "#e67e22", "Fail": "#e74c3c"}
    lcolor = level_colors.get(s.level, "#95a5a6")

    suggestions_html = ""
    if r.suggestions:
        for sg in r.suggestions:
            sc = {"critical": "#e74c3c", "high": "#e67e22", "medium": "#f1c40f", "low": "#95a5a6"}
            c = sc.get(sg.severity, "#95a5a6")
            suggestions_html += f'<li><span class="badge" style="background:{c}">{sg.severity.upper()}</span> <strong>{sg.title}</strong>: {sg.detail}</li>'
    else:
        suggestions_html = "<li>No improvement recommendations needed.</li>"

    subscores_html = ""
    for name, val in s.subscores.items():
        sc = "#27ae60" if val >= 80 else "#f39c12" if val >= 60 else "#e74c3c"
        w = s.weights.get(name, 0)
        subscores_html += f'<tr><td>{name.replace("_", " ").title()}</td><td><span class="badge" style="background:{sc}">{val:.1f}</span></td><td>{w:.0%}</td></tr>'

    hal_section = ""
    if m.hallucination_stats.count > 0:
        h = m.hallucination_stats
        hal_section = f"""
        <table><tr><th>Metric</th><th>Value</th></tr>
        <tr><td>Mean Risk</td><td>{h.mean:.1%}</td></tr>
        <tr><td>Max Risk</td><td>{h.max_val:.1%}</td></tr>
        <tr><td>Executions Analysed</td><td>{h.count}</td></tr></table>"""
    else:
        hal_section = "<p><em>No hallucination analysis data.</em></p>"

    # ── SVG Certification Stamp ───────────────────────────────────────── #
    stamp_svg = f"""
    <div style="position:absolute;top:10px;right:20px;z-index:10;">
        <svg width="180" height="180" viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg">
            <!-- White filled background -->
            <circle cx="90" cy="90" r="82" fill="#ffffff" stroke="#0a4a23" stroke-width="3.5"/>
            <circle cx="90" cy="90" r="66" fill="none" stroke="#0a4a23" stroke-width="1.8"/>
            <!-- Cardinal dots between rings -->
            <circle cx="90" cy="6"   r="3" fill="#0a4a23"/>
            <circle cx="90" cy="174" r="3" fill="#0a4a23"/>
            <circle cx="6"  cy="90"  r="3" fill="#0a4a23"/>
            <circle cx="174" cy="90" r="3" fill="#0a4a23"/>
            <defs>
                <path id="topArc" d="M 16,90 A 74,74 0 0,0 164,90" fill="none"/>
                <path id="bottomArc" d="M 16,90 A 74,74 0 0,1 164,90" fill="none"/>
            </defs>
            <text font-family="Arial,Helvetica,sans-serif" font-size="12.5" font-weight="bold" fill="#0a4a23" letter-spacing="4.5">
                <textPath href="#topArc" startOffset="50%" text-anchor="middle">CERTIFIED</textPath>
            </text>
            <text font-family="Arial,Helvetica,sans-serif" font-size="11" font-weight="bold" fill="#0a4a23" letter-spacing="3.5">
                <textPath href="#bottomArc" startOffset="50%" text-anchor="middle">LLM AUDITOR</textPath>
            </text>
            <text x="90" y="87" text-anchor="middle" font-family="Arial,Helvetica,sans-serif" font-size="16" font-weight="bold" fill="#0a4a23">{s.level.upper()}</text>
            <line x1="52" y1="92" x2="128" y2="92" stroke="#0a4a23" stroke-width="0.6"/>
            <text x="90" y="106" text-anchor="middle" font-family="Arial,Helvetica,sans-serif" font-size="8.5" fill="#0a4a23">{cert_number}</text>
        </svg>
    </div>"""

    # ── Digital Signature Block ───────────────────────────────────────── #
    signature_html = f"""
    <div class="section" style="background:#fafafa;border-top:2px solid #0d5e2e;text-align:center;">
        <h2 style="color:#0d5e2e;">\u270d\ufe0f Digital Signature</h2>
        <table style="width:auto;margin:0 auto;font-size:13px;">
            <tr><td style="text-align:right;font-weight:600;padding:6px 16px;">Certificate No</td><td style="padding:6px 16px;"><code>{cert_number}</code></td></tr>
            <tr><td style="text-align:right;font-weight:600;padding:6px 16px;">Issued By</td><td style="padding:6px 16px;">{_VERSION} \u2014 Automated Certification Engine</td></tr>
            <tr><td style="text-align:right;font-weight:600;padding:6px 16px;">Issue Date</td><td style="padding:6px 16px;">{ts}</td></tr>
            <tr><td style="text-align:right;font-weight:600;padding:6px 16px;">Integrity Hash</td><td style="padding:6px 16px;font-size:10px;"><code>{ihash}</code></td></tr>
        </table>
        <hr style="border:none;border-top:1px solid #ddd;margin:20px 40px;"/>
        <p style="font-style:italic;font-size:12px;color:#7f8c8d;line-height:1.8;">
            This certificate was digitally signed and generated automatically<br/>
            by the {_VERSION} Certification Engine.<br/>
            No human intervention was involved in the evaluation process.
        </p>
    </div>"""

    # ── Mode label ─────────────────────────────────────────────────────── #
    mode_raw = getattr(sess, "mode", "simulated") or "simulated"
    is_live = mode_raw.lower() in ("live", "api", "real")
    mode_label = "With API Call" if is_live else "Without API Call"
    mode_color = "#27ae60" if is_live else "#3498db"
    mode_badge_html = (
        f'<span style="display:inline-block;padding:4px 14px;border-radius:14px;'
        f'font-size:12px;font-weight:700;color:#fff;background:{mode_color};">'
        f'{mode_label}</span>'
    )

    body = stamp_svg + f"""
    <div class="section"><h2>1. Application Identity</h2>
        <table><tr><th>Field</th><th>Value</th></tr>
        <tr><td>Application</td><td>{sess.app_name}</td></tr>
        <tr><td>Version</td><td>{sess.version}</td></tr>
        <tr><td>Evaluation Mode</td><td>{mode_badge_html}</td></tr>
        <tr><td>Duration</td><td>{sess.duration_seconds:.1f}s</td></tr>
        <tr><td>Total Executions</td><td>{m.total_runs}</td></tr>
        <tr><td>Models</td><td>{', '.join(m.models_used) or 'N/A'}</td></tr>
        </table></div>

    <div class="section"><h2>2. Evaluation Summary</h2>
        <div class="summary-box" style="text-align:center;">
            <div style="font-size:32px;color:{lcolor};">{s.level_emoji} {s.level}</div>
            <div style="font-size:24px;font-weight:bold;">{s.overall:.1f}/100</div>
        </div></div>

    <div class="section"><h2>3. Metrics &amp; Distributions</h2>
        <table><tr><th>Metric</th><th>Mean</th><th>StdDev</th><th>Min</th><th>Max</th></tr>
        <tr><td>Total Tokens</td><td>{m.token_stats.mean:.0f}</td><td>{m.token_stats.stddev:.0f}</td><td>{m.token_stats.min_val:.0f}</td><td>{m.token_stats.max_val:.0f}</td></tr>
        <tr><td>Cost (USD)</td><td>${m.cost_stats.mean:.6f}</td><td>${m.cost_stats.stddev:.6f}</td><td>${m.cost_stats.min_val:.6f}</td><td>${m.cost_stats.max_val:.6f}</td></tr>
        <tr><td>Latency (s)</td><td>{m.latency_stats.mean:.3f}</td><td>{m.latency_stats.stddev:.3f}</td><td>{m.latency_stats.min_val:.3f}</td><td>{m.latency_stats.max_val:.3f}</td></tr>
        </table></div>

    <div class="section"><h2>4. Hallucination Analysis</h2>{hal_section}</div>

    <div class="section"><h2>5. Governance Compliance</h2>
        <table><tr><th>Metric</th><th>Value</th></tr>
        <tr><td>Guard Violations</td><td>{m.guard_violations}</td></tr>
        <tr><td>Budget Violations</td><td>{m.budget_violations}</td></tr>
        <tr><td>Total Warnings</td><td>{m.total_warnings}</td></tr></table></div>

    <div class="section"><h2>6. Stability Analysis</h2>
        <table><tr><th>Metric</th><th>Value</th></tr>
        <tr><td>Failure Rate</td><td>{m.failure_rate:.1%}</td></tr>
        <tr><td>Latency CV</td><td>{m.latency_stats.stddev / max(m.latency_stats.mean, 0.001):.3f}</td></tr>
        <tr><td>Token CV</td><td>{m.token_stats.stddev / max(m.token_stats.mean, 1):.3f}</td></tr></table></div>

    <div class="section"><h2>7. Scoring Breakdown</h2>
        <table><tr><th>Subscore</th><th>Value</th><th>Weight</th></tr>
        {subscores_html}</table></div>

    <div class="section"><h2>8. Certification Verdict</h2>
        <div class="summary-box" style="text-align:center;font-size:20px;">
            <span style="color:{lcolor};font-weight:bold;">{s.level_emoji} {s.level} — {s.overall:.1f}/100</span>
        </div></div>

    <div class="section"><h2>9. Improvement Recommendations</h2>
        <ul class="notes-list">{suggestions_html}</ul></div>

    <div class="section"><h2>10. Integrity Hash</h2>
        <p><code style="font-size:11px;word-break:break-all;">{ihash}</code></p></div>

    <div class="section" style="background:#f9f9ff;border-left:4px solid #2c3e50;">
        <h2 style="color:#2c3e50;">11. Understanding the Numbers</h2>
        <p style="font-size:13px;color:#555;margin-bottom:18px;">What do the statistics above mean? Here is a detailed explanation for non-technical readers.</p>
        {''.join(f'''
        <div style="margin-bottom:18px;padding:14px 18px;background:#fff;border-radius:6px;border:1px solid #eaecf0;">
            <div style="font-weight:700;font-size:14px;color:#2c3e50;margin-bottom:4px;">{item["term"]}</div>
            <div style="font-size:12px;color:#7f8c8d;margin-bottom:6px;font-family:monospace;">{item["value"]}</div>
            <div style="font-size:13px;color:#444;line-height:1.7;">{item["explanation"]}</div>
        </div>''' for item in _build_detailed_explanation(r))}
    </div>

    <div class="section" style="background:#f8fdf8;">
        <h2 style="color:#0d5e2e;">12. Report Summary — What Does This Mean?</h2>
        <p style="font-size:13px;color:#555;margin-bottom:18px;">Here is a simple summary anyone can understand.</p>
        {''.join(f'''
        <div style="display:flex;align-items:flex-start;margin-bottom:14px;">
            <span style="font-size:22px;margin-right:14px;flex-shrink:0;">{item["icon"]}</span>
            <div>
                <div style="font-weight:700;font-size:14px;color:#2c3e50;margin-bottom:3px;">{item["heading"]}</div>
                <div style="font-size:13px;color:#555;line-height:1.6;">{item["text"]}</div>
            </div>
        </div>''' for item in _build_plain_summary(r))}
    </div>
    """ + signature_html

    return _html_shell(
        title="GenAI Application Certification Report",
        subtitle=f"{_VERSION} | {ts}",
        body=body,
        footer=f"Integrity Hash: {ihash} | Certificate: {cert_number} | {ts} | {_VERSION}",
        header_bg="#0d5e2e",
    )


# ── Certification: PDF ────────────────────────────────────────────────────── #

def _cert_pdf(r: "EvaluationReport", ihash: str, cert_number: str, path: str) -> None:
    rl = _import_reportlab()
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    s = r.score
    m = r.metrics
    sess = r.session

    doc, styles, story, cm = _pdf_base(path, "GenAI Certification Report", ts,
                                        header_color="#0d5e2e")

    # Mode label
    mode_raw = getattr(sess, "mode", "simulated") or "simulated"
    is_live = mode_raw.lower() in ("live", "api", "real")
    mode_label = "With API Call" if is_live else "Without API Call"

    # 1. Application Identity
    story += _pdf_section("1. Application Identity", styles)
    story.append(_pdf_table([
        ["Field", "Value"],
        ["Application", sess.app_name],
        ["Version", sess.version],
        ["Evaluation Mode", mode_label],
        ["Duration", f"{sess.duration_seconds:.1f}s"],
        ["Total Executions", str(m.total_runs)],
        ["Models", ", ".join(m.models_used) or "N/A"],
    ], cm))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 2. Evaluation Summary
    story += _pdf_section("2. Evaluation Summary", styles)
    story.append(rl["Paragraph"](
        f"<font size='14'><b>{s.level} — {s.overall:.1f}/100</b></font>",
        styles["summary"]))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 3. Metrics & Distributions
    story += _pdf_section("3. Metrics & Distributions", styles)
    story.append(_pdf_table([
        ["Metric", "Mean", "StdDev", "Min", "Max"],
        ["Total Tokens", f"{m.token_stats.mean:.0f}", f"{m.token_stats.stddev:.0f}",
         f"{m.token_stats.min_val:.0f}", f"{m.token_stats.max_val:.0f}"],
        ["Cost (USD)", f"${m.cost_stats.mean:.6f}", f"${m.cost_stats.stddev:.6f}",
         f"${m.cost_stats.min_val:.6f}", f"${m.cost_stats.max_val:.6f}"],
        ["Latency (s)", f"{m.latency_stats.mean:.3f}", f"{m.latency_stats.stddev:.3f}",
         f"{m.latency_stats.min_val:.3f}", f"{m.latency_stats.max_val:.3f}"],
    ], cm))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 4. Hallucination Analysis
    story += _pdf_section("4. Hallucination Analysis", styles)
    if m.hallucination_stats.count > 0:
        h = m.hallucination_stats
        story.append(_pdf_table([
            ["Metric", "Value"],
            ["Mean Risk", f"{h.mean:.1%}"],
            ["Max Risk", f"{h.max_val:.1%}"],
            ["Executions Analysed", str(h.count)],
        ], cm))
    else:
        story.append(rl["Paragraph"]("No hallucination analysis data.", styles["body"]))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 5. Governance Compliance
    story += _pdf_section("5. Governance Compliance", styles)
    story.append(_pdf_table([
        ["Metric", "Value"],
        ["Guard Violations", str(m.guard_violations)],
        ["Budget Violations", str(m.budget_violations)],
        ["Total Warnings", str(m.total_warnings)],
    ], cm))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 6. Stability Analysis
    story += _pdf_section("6. Stability Analysis", styles)
    story.append(_pdf_table([
        ["Metric", "Value"],
        ["Failure Rate", f"{m.failure_rate:.1%}"],
        ["Latency CV", f"{m.latency_stats.stddev / max(m.latency_stats.mean, 0.001):.3f}"],
        ["Token CV", f"{m.token_stats.stddev / max(m.token_stats.mean, 1):.3f}"],
    ], cm))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 7. Scoring Breakdown
    story += _pdf_section("7. Scoring Breakdown", styles)
    rows = [["Subscore", "Value", "Weight"]]
    for name, val in s.subscores.items():
        w = s.weights.get(name, 0)
        rows.append([name.replace("_", " ").title(), f"{val:.1f}", f"{w:.0%}"])
    story.append(_pdf_table(rows, cm))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 8. Certification Verdict
    story += _pdf_section("8. Certification Verdict", styles)
    story.append(rl["Paragraph"](
        f"<font size='16'><b>{s.level} — {s.overall:.1f}/100</b></font>",
        styles["summary"]))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    # 9. Improvement Recommendations
    story += _pdf_section("9. Improvement Recommendations", styles)
    if r.suggestions:
        for sg in r.suggestions:
            story.append(rl["Paragraph"](
                f"\u2022 <b>[{sg.severity.upper()}] {sg.title}:</b> {sg.detail}",
                styles["body"]))
    else:
        story.append(rl["Paragraph"]("No recommendations \u2014 all metrics acceptable.", styles["body"]))
    story.append(rl["Spacer"](1, 4 * rl["mm"]))

    # 10. Integrity Hash
    story += _pdf_section("10. Integrity Hash", styles)
    story.append(rl["Paragraph"](
        f"<font size='7' color='#7f8c8d'><b>SHA-256:</b> {ihash}</font>",
        styles["body"]))
    story.append(rl["Spacer"](1, 6 * rl["mm"]))

    # 11. Understanding the Numbers
    story += _pdf_section("11. Understanding the Numbers", styles)
    story.append(rl["Paragraph"](
        "<font size='8' color='#555555'><i>"
        "What do the statistics above mean? Here is a detailed explanation "
        "for non-technical readers."
        "</i></font>",
        styles["body"]))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    detail_term_style = rl["ParagraphStyle"](
        "detail_term",
        fontName="Helvetica-Bold",
        fontSize=10,
        textColor=rl["colors"].HexColor("#2c3e50"),
        leading=14,
        spaceBefore=8,
        spaceAfter=2,
        leftIndent=4,
    )
    detail_value_style = rl["ParagraphStyle"](
        "detail_value",
        fontName="Courier",
        fontSize=7.5,
        textColor=cm["MID_GREY"],
        leading=11,
        spaceAfter=2,
        leftIndent=24,
    )
    detail_explain_style = rl["ParagraphStyle"](
        "detail_explain",
        fontName="Helvetica",
        fontSize=9,
        textColor=cm["BLACK"],
        leading=15,
        spaceAfter=10,
        leftIndent=24,
    )

    for item in _build_detailed_explanation(r):
        story.append(rl["Paragraph"](item["term"], detail_term_style))
        story.append(rl["Paragraph"](item["value"], detail_value_style))
        story.append(rl["Paragraph"](item["explanation"], detail_explain_style))

    story.append(rl["Spacer"](1, 4 * rl["mm"]))

    # 12. Plain Language Summary
    story += _pdf_section("12. Report Summary", styles)
    story.append(rl["Paragraph"](
        "<font size='8' color='#555555'><i>"
        "What does this report mean? Here is a simple summary anyone can understand."
        "</i></font>",
        styles["body"]))
    story.append(rl["Spacer"](1, 3 * rl["mm"]))

    summary_style = rl["ParagraphStyle"](
        "plain_summary",
        fontName="Helvetica",
        fontSize=9,
        textColor=cm["BLACK"],
        leading=15,
        spaceAfter=8,
        leftIndent=24,
    )
    heading_style = rl["ParagraphStyle"](
        "plain_heading",
        fontName="Helvetica-Bold",
        fontSize=10,
        textColor=rl["colors"].HexColor("#0d5e2e"),
        leading=14,
        spaceBefore=6,
        spaceAfter=2,
        leftIndent=4,
    )

    for item in _build_plain_summary(r):
        story.append(rl["Paragraph"](
            f"{item['icon']}  {item['heading']}",
            heading_style,
        ))
        story.append(rl["Paragraph"](item["text"], summary_style))

    # ── Digital Signature Section ──────────────────────────────────────── #
    story += _pdf_section("Digital Signature", styles)
    sig_style = rl["ParagraphStyle"](
        "sig_detail",
        fontName="Helvetica",
        fontSize=8,
        textColor=cm["MID_GREY"],
        leading=13,
        spaceAfter=3,
    )
    story.append(rl["Paragraph"](f"<b>Certificate No:</b>  {cert_number}", sig_style))
    story.append(rl["Paragraph"](
        f"<b>Issued By:</b>  {_VERSION} \u2014 Automated Certification Engine", sig_style))
    story.append(rl["Paragraph"](f"<b>Issue Date:</b>  {ts}", sig_style))
    story.append(rl["Paragraph"](
        f"<b>Integrity Hash:</b>  <font size='6'>{ihash}</font>", sig_style))
    story.append(rl["Spacer"](1, 6 * rl["mm"]))
    story.append(rl["Paragraph"](
        "<i>This certificate was digitally signed and generated automatically "
        f"by the {_VERSION} Certification Engine. "
        "No human intervention was involved in the evaluation process.</i>",
        sig_style))

    title_text = "GenAI Certification Report"
    doc.build(story,
              onFirstPage=_pdf_page_handler(ts, title_text, header_color="#0d5e2e",
                                            cert_number=cert_number, level=s.level),
              onLaterPages=_pdf_page_handler(ts, title_text, header_color="#0d5e2e"))


# ═══════════════════════════════════════════════════════════════════════════════
#  SHARED HELPERS
# ═══════════════════════════════════════════════════════════════════════════════

# ═══════════════════════════════════════════════════════════════════════════════
#  PLAIN-LANGUAGE SUMMARY GENERATOR
# ═══════════════════════════════════════════════════════════════════════════════

def _build_plain_summary(r: "EvaluationReport") -> list[dict]:
    """
    Generate a list of plain-language summary bullets from evaluation data.

    Each item is a dict: {"icon": str, "heading": str, "text": str}
    Written so any non-technical reader can understand what happened.
    """
    s = r.score
    m = r.metrics
    sess = r.session
    items: list[dict] = []

    # ── What was tested ───────────────────────────────────────────────────── #
    items.append({
        "icon": "\U0001f4cb",  # clipboard
        "heading": "What Was Tested",
        "text": (
            f"\"{sess.app_name}\" (version {sess.version}) was tested "
            f"{m.total_runs} time{'s' if m.total_runs != 1 else ''} "
            f"using the {', '.join(m.models_used) or 'AI'} model."
        ),
    })

    # ── Overall verdict ───────────────────────────────────────────────────── #
    if s.overall >= 90:
        verdict_text = (
            f"The application received a top-tier \"{s.level}\" certification "
            f"with a score of {s.overall:.0f} out of 100. "
            "This means it performed excellently across all quality checks."
        )
    elif s.overall >= 80:
        verdict_text = (
            f"The application earned a \"{s.level}\" certification "
            f"({s.overall:.0f}/100). It performed well overall with only "
            "minor areas for improvement."
        )
    elif s.overall >= 70:
        verdict_text = (
            f"The application received a \"{s.level}\" certification "
            f"({s.overall:.0f}/100). Performance is acceptable but several "
            "areas need attention before production use."
        )
    elif s.overall >= 60:
        verdict_text = (
            f"The application received a \"{s.level}\" rating "
            f"({s.overall:.0f}/100). Significant improvements are needed "
            "before it can be fully trusted in production."
        )
    else:
        verdict_text = (
            f"The application did not pass certification "
            f"({s.overall:.0f}/100). Major issues were found that must be "
            "resolved before production deployment."
        )
    items.append({"icon": s.level_emoji, "heading": "Overall Result", "text": verdict_text})

    # ── Reliability / Stability ───────────────────────────────────────────── #
    stab = s.subscores.get("stability", 0)
    if stab >= 90:
        stab_text = "The AI responded consistently every time it was called. Response times and output lengths were steady and predictable."
    elif stab >= 70:
        stab_text = "Responses were mostly consistent, though some variation was observed in speed or output length. Generally dependable."
    else:
        stab_text = "Responses varied noticeably between calls. Speed and output length were unpredictable, which may affect user experience."
    fail_note = ""
    if m.failure_rate > 0:
        fail_note = f" {m.failure_rate:.0%} of calls failed to complete."
    items.append({"icon": "\u26a1", "heading": "Reliability", "text": stab_text + fail_note})

    # ── Accuracy / Truthfulness ───────────────────────────────────────────── #
    fact = s.subscores.get("factual_reliability", 0)
    hal_mean = m.hallucination_stats.mean if m.hallucination_stats.count > 0 else 0
    if fact >= 90:
        fact_text = "The AI's answers showed very low risk of containing made-up or inaccurate information."
    elif fact >= 70:
        fact_text = "The AI's answers were mostly reliable, but some responses contained statements that could not be fully verified."
    else:
        fact_text = "There is a noticeable risk that the AI may produce inaccurate or fabricated information. Extra fact-checking is recommended."
    if m.hallucination_stats.count > 0:
        fact_text += f" Average accuracy risk level: {hal_mean:.0%}."
    items.append({"icon": "\U0001f3af", "heading": "Accuracy", "text": fact_text})

    # ── Safety & Rules ────────────────────────────────────────────────────── #
    gov = s.subscores.get("governance_compliance", 0)
    if gov >= 95 and m.guard_violations == 0 and m.budget_violations == 0:
        gov_text = "The AI followed all safety rules and budget limits perfectly. No violations were detected."
    elif gov >= 70:
        parts = []
        if m.guard_violations > 0:
            parts.append(f"{m.guard_violations} quality gate violation{'s' if m.guard_violations != 1 else ''}")
        if m.budget_violations > 0:
            parts.append(f"{m.budget_violations} budget limit breach{'es' if m.budget_violations != 1 else ''}")
        gov_text = "Minor safety or budget issues were detected: " + ", ".join(parts) + ". These should be reviewed."
    else:
        gov_text = "Multiple safety or budget violations occurred. The system's guardrails may need to be reconfigured."
    items.append({"icon": "\U0001f6e1", "heading": "Safety & Rules", "text": gov_text})

    # ── Cost ──────────────────────────────────────────────────────────────── #
    total_cost = m.cost_stats.total
    avg_cost = m.cost_stats.mean
    if total_cost == 0:
        cost_text = "No cost was incurred during testing (free tier or zero-cost model)."
    elif avg_cost < 0.001:
        cost_text = f"Total cost for all {m.total_runs} calls was ${total_cost:.4f} USD — very economical."
    elif avg_cost < 0.01:
        cost_text = f"Total cost: ${total_cost:.4f} USD (average ${avg_cost:.4f} per call). Cost is reasonable."
    else:
        cost_text = f"Total cost: ${total_cost:.4f} USD (average ${avg_cost:.4f} per call). Consider optimising prompts or using a cheaper model to reduce expenses."
    items.append({"icon": "\U0001f4b0", "heading": "Cost", "text": cost_text})

    # ── Suggestions (simplified) ──────────────────────────────────────────── #
    if r.suggestions:
        critical = [sg for sg in r.suggestions if sg.severity in ("critical", "high")]
        if critical:
            titles = ", ".join(sg.title.lower() for sg in critical[:3])
            items.append({
                "icon": "\u26a0\ufe0f",
                "heading": "Action Needed",
                "text": f"The evaluation found {len(critical)} important issue{'s' if len(critical) != 1 else ''} to address: {titles}.",
            })
    else:
        items.append({
            "icon": "\u2705",
            "heading": "No Issues Found",
            "text": "All quality checks passed. No immediate actions are required.",
        })

    return items


def _build_detailed_explanation(r: "EvaluationReport") -> list[dict]:
    """
    Generate plain-language explanations of every statistical metric used
    in this report.  Each item: {"term", "value", "explanation"}.
    Written for non-technical stakeholders.
    """
    m = r.metrics
    s = r.score
    items: list[dict] = []

    # Mean
    items.append({
        "term": "Mean (Average)",
        "value": (
            f"Token Mean: {m.token_stats.mean:.0f}  |  "
            f"Cost Mean: ${m.cost_stats.mean:.6f}  |  "
            f"Latency Mean: {m.latency_stats.mean:.3f}s"
        ),
        "explanation": (
            "The mean is the simple average across all test runs. "
            "For example, if the AI was called 3 times and used 100, 200 "
            "and 300 tokens, the mean would be 200. A stable mean "
            "indicates the AI behaves consistently between calls."
        ),
    })

    # Standard Deviation
    items.append({
        "term": "Standard Deviation (StdDev)",
        "value": (
            f"Token StdDev: {m.token_stats.stddev:.0f}  |  "
            f"Cost StdDev: ${m.cost_stats.stddev:.6f}  |  "
            f"Latency StdDev: {m.latency_stats.stddev:.3f}s"
        ),
        "explanation": (
            "Standard deviation measures how much individual results "
            "spread out from the average. A low number means the AI "
            "behaves consistently every time. A high number means results "
            "fluctuate widely — which can make the application "
            "unpredictable for end users."
        ),
    })

    # CV
    lat_cv = m.latency_stats.stddev / max(m.latency_stats.mean, 0.001)
    tok_cv = m.token_stats.stddev / max(m.token_stats.mean, 1)
    items.append({
        "term": "Coefficient of Variation (CV)",
        "value": f"Latency CV: {lat_cv:.3f}  |  Token CV: {tok_cv:.3f}",
        "explanation": (
            "CV is the ratio of standard deviation to the mean. It "
            "tells you how variable results are relative to their average. "
            "A CV below 0.3 means stable, predictable performance. "
            "Above 0.5 indicates high variability that may need attention. "
            "Above 1.0 is a red flag — the AI is very inconsistent."
        ),
    })

    # Min / Max
    items.append({
        "term": "Min / Max Range",
        "value": (
            f"Tokens: {m.token_stats.min_val:.0f}–{m.token_stats.max_val:.0f}  |  "
            f"Latency: {m.latency_stats.min_val:.3f}s–{m.latency_stats.max_val:.3f}s  |  "
            f"Cost: ${m.cost_stats.min_val:.6f}–${m.cost_stats.max_val:.6f}"
        ),
        "explanation": (
            "Min and Max show the smallest and largest values observed "
            "during testing. A narrow gap (min close to max) means "
            "predictable performance. A wide gap may indicate the AI "
            "responds very differently depending on the input."
        ),
    })

    # Hallucination Risk
    if m.hallucination_stats.count > 0:
        items.append({
            "term": "Hallucination Risk Score",
            "value": (
                f"Mean Risk: {m.hallucination_stats.mean:.1%}  |  "
                f"Max Risk: {m.hallucination_stats.max_val:.1%}"
            ),
            "explanation": (
                "Hallucination risk measures the likelihood that the AI "
                "invented facts or produced inaccurate information. "
                "0% = no detected risk, 100% = extremely high risk. "
                "Below 15% is considered LOW risk. The system analyses "
                "factual claim density, hedging language, absolute "
                "statements and self-contradictions to compute this."
            ),
        })

    # Confidence
    items.append({
        "term": "Confidence Score",
        "value": f"Mean Confidence: {m.confidence_stats.mean:.0f}%",
        "explanation": (
            "Confidence estimates how trustworthy each AI response is, "
            "based on response length, cost and execution time. "
            "85%+ = healthy and complete. "
            "70–84% = minor anomalies, review advised. "
            "Below 70% = the response may be too short, too costly or "
            "took an unusually long time."
        ),
    })

    # Failure Rate
    items.append({
        "term": "Failure Rate",
        "value": f"{m.failure_rate:.1%}",
        "explanation": (
            "The percentage of AI calls that failed or produced errors. "
            "0% means every call succeeded. Even a small failure rate "
            "(above 2%) should be investigated — it may indicate "
            "unreliable API connections, invalid inputs or model "
            "limitations."
        ),
    })

    # Risk Distribution
    rd = m.risk_distribution
    total = max(sum(rd.values()), 1)
    items.append({
        "term": "Risk Distribution",
        "value": (
            f"LOW: {rd.get('LOW', 0)} ({rd.get('LOW', 0)/total:.0%})  |  "
            f"MEDIUM: {rd.get('MEDIUM', 0)} ({rd.get('MEDIUM', 0)/total:.0%})  |  "
            f"HIGH: {rd.get('HIGH', 0)} ({rd.get('HIGH', 0)/total:.0%})"
        ),
        "explanation": (
            "Each AI call is classified as LOW, MEDIUM or HIGH risk "
            "based on its cost and token usage. A healthy application "
            "should have most calls in the LOW category. MEDIUM calls "
            "deserve monitoring. HIGH-risk calls should be investigated."
        ),
    })

    # Overall Score
    items.append({
        "term": "Overall Certification Score",
        "value": f"{s.overall:.1f}/100 — {s.level}",
        "explanation": (
            f"The final score is a weighted combination of five quality "
            f"areas: Stability ({s.weights.get('stability', 0):.0%}), "
            f"Factual Reliability ({s.weights.get('factual_reliability', 0):.0%}), "
            f"Governance Compliance ({s.weights.get('governance_compliance', 0):.0%}), "
            f"Cost Predictability ({s.weights.get('cost_predictability', 0):.0%}), "
            f"and Risk Profile ({s.weights.get('risk_profile', 0):.0%}). "
            f"Each area is scored 0–100 and combined using these weights. "
            f"Platinum ≥ 90, Gold ≥ 80, Silver ≥ 70, Conditional Pass ≥ 60."
        ),
    })

    return items


def _hash(data: dict) -> str:
    payload = json.dumps(data, sort_keys=True, default=str).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()


def _write(path: str, content: str) -> None:
    with open(path, "w", encoding="utf-8", errors="replace") as f:
        f.write(content)


def _html_shell(title: str, subtitle: str, body: str, footer: str = "",
                header_bg: str = "#1a253c") -> str:
    """Shared HTML wrapper with professional styling."""
    return textwrap.dedent(f"""\
    <!DOCTYPE html>
    <html lang="en"><head>
    <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>{title}</title>
    <style>
    body{{font-family:'Segoe UI',Arial,sans-serif;background:#f4f6f9;color:#2c3e50;margin:0;padding:40px}}
    .report{{max-width:860px;margin:0 auto;background:#fff;border-radius:8px;box-shadow:0 2px 16px rgba(0,0,0,.1);overflow:hidden;position:relative}}
    .header{{background:{header_bg};color:#fff;padding:32px 40px 24px}}
    .header h1{{margin:0 0 8px;font-size:22px;letter-spacing:1px}}
    .header .subtitle{{font-size:13px;color:#a0b0c8}}
    .section{{padding:28px 40px;border-bottom:1px solid #eaecf0}}
    .section:last-child{{border-bottom:none}}
    .section h2{{font-size:13px;text-transform:uppercase;letter-spacing:1.5px;color:#7f8c8d;margin:0 0 16px}}
    .summary-box{{background:#f0f7ff;border-left:4px solid #2980b9;padding:16px 20px;border-radius:4px;font-size:14px;line-height:1.7}}
    table{{width:100%;border-collapse:collapse;font-size:14px}}
    th{{text-align:left;padding:10px 14px;background:#f4f6f9;color:#7f8c8d;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.8px}}
    td{{padding:10px 14px;border-bottom:1px solid #f0f0f0}}
    tr:last-child td{{border-bottom:none}}
    .badge{{display:inline-block;padding:3px 10px;border-radius:12px;font-size:12px;font-weight:700;color:#fff}}
    .notes-list{{list-style:none;padding:0;margin:0}}
    .notes-list li{{padding:6px 0;font-size:14px;border-bottom:1px solid #f0f0f0}}
    .notes-list li:last-child{{border-bottom:none}}
    .footer{{background:#f4f6f9;padding:20px 40px;font-size:11px;color:#95a5a6;text-align:center}}
    code{{font-family:'Courier New',monospace;background:#f0f0f0;padding:2px 6px;border-radius:3px;font-size:12px;word-break:break-all}}
    </style></head><body>
    <div class="report">
    <div class="header"><h1>{title}</h1><div class="subtitle">{subtitle}</div></div>
    {body}
    <div class="footer"><code>{footer}</code></div>
    </div></body></html>
    """)


# ── Shared PDF helpers ─────────────────────────────────────────────────────── #

def _import_reportlab() -> dict:
    """Import reportlab components. Returns a dict for easy access."""
    try:
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4
        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
        from reportlab.lib.units import mm
        from reportlab.platypus import (
            SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
            HRFlowable, KeepTogether,
        )
        from reportlab.lib.enums import TA_CENTER, TA_LEFT
    except ImportError as exc:
        raise ImportError(
            "reportlab is required for PDF export. Install: pip install reportlab>=4.0.0"
        ) from exc

    return {
        "colors": colors, "A4": A4, "mm": mm,
        "SimpleDocTemplate": SimpleDocTemplate, "Paragraph": Paragraph,
        "Spacer": Spacer, "Table": Table, "TableStyle": TableStyle,
        "HRFlowable": HRFlowable, "KeepTogether": KeepTogether,
        "ParagraphStyle": ParagraphStyle, "getSampleStyleSheet": getSampleStyleSheet,
        "TA_CENTER": TA_CENTER, "TA_LEFT": TA_LEFT,
    }


def _pdf_base(path: str, title: str, ts: str, header_color: str = "#1a253c"):
    """Create a PDF doc, styles, empty story, and color map."""
    rl = _import_reportlab()
    colors = rl["colors"]

    cm = {
        "DARK": colors.HexColor(header_color),
        "MID_GREY": colors.HexColor("#7f8c8d"),
        "LIGHT_GREY": colors.HexColor("#eaecf0"),
        "BG_GREY": colors.HexColor("#f4f6f9"),
        "GREEN": colors.HexColor("#27ae60"),
        "YELLOW": colors.HexColor("#f39c12"),
        "RED": colors.HexColor("#e74c3c"),
        "WHITE": colors.white,
        "BLACK": colors.HexColor("#2c3e50"),
    }

    mm = rl["mm"]
    margin = 18 * mm
    doc = rl["SimpleDocTemplate"](
        path, pagesize=rl["A4"],
        leftMargin=margin, rightMargin=margin,
        topMargin=32 * mm, bottomMargin=22 * mm,
        title=title, author=_VERSION,
    )

    ParagraphStyle = rl["ParagraphStyle"]
    styles = {
        "body": ParagraphStyle("body", fontName="Helvetica", fontSize=9,
                               textColor=cm["BLACK"], leading=14, spaceAfter=4),
        "section_title": ParagraphStyle("sect", fontName="Helvetica-Bold", fontSize=8,
                                        textColor=cm["MID_GREY"], leading=12,
                                        spaceBefore=14, spaceAfter=6),
        "summary": ParagraphStyle("summ", fontName="Helvetica-Oblique", fontSize=9,
                                  textColor=cm["BLACK"], leading=15, spaceAfter=4,
                                  leftIndent=8, rightIndent=8),
    }

    return doc, styles, [], cm


def _pdf_section(title: str, styles: dict) -> list:
    rl = _import_reportlab()
    return [
        rl["HRFlowable"](width="100%", thickness=0.5,
                          color=rl["colors"].HexColor("#eaecf0"),
                          spaceAfter=2, spaceBefore=10),
        rl["Paragraph"](title, styles["section_title"]),
    ]


def _pdf_table(rows: list, cm: dict) -> Any:
    rl = _import_reportlab()
    mm = rl["mm"]

    t = rl["Table"](rows, colWidths=None)
    t.setStyle(rl["TableStyle"]([
        ("BACKGROUND",   (0, 0), (-1, 0), cm["BG_GREY"]),
        ("TEXTCOLOR",    (0, 0), (-1, 0), cm["MID_GREY"]),
        ("FONTNAME",     (0, 0), (-1, 0), "Helvetica-Bold"),
        ("FONTSIZE",     (0, 0), (-1, 0), 7.5),
        ("FONTNAME",     (0, 1), (-1, -1), "Helvetica"),
        ("FONTSIZE",     (0, 1), (-1, -1), 9),
        ("TEXTCOLOR",    (0, 1), (-1, -1), cm["BLACK"]),
        ("ROWBACKGROUNDS", (0, 1), (-1, -1), [cm["WHITE"], cm["BG_GREY"]]),
        ("GRID",         (0, 0), (-1, -1), 0.3, cm["LIGHT_GREY"]),
        ("TOPPADDING",   (0, 0), (-1, -1), 5),
        ("BOTTOMPADDING",(0, 0), (-1, -1), 5),
        ("LEFTPADDING",  (0, 0), (-1, -1), 8),
    ]))
    return t


def _pdf_page_handler(ts: str, title: str, header_color: str = "#1a253c",
                      cert_number: str | None = None, level: str | None = None):
    """Return a function for reportlab page events (header/footer drawing)."""
    def _handler(canvas, doc):
        from reportlab.lib.pagesizes import A4
        from reportlab.lib.units import mm
        from reportlab.lib import colors

        PAGE_W, PAGE_H = A4
        margin = 18 * mm

        DARK = colors.HexColor(header_color)
        BG_GREY = colors.HexColor("#f4f6f9")
        MID_GREY = colors.HexColor("#7f8c8d")
        LIGHT_GREY = colors.HexColor("#eaecf0")

        canvas.saveState()

        # Header bar
        canvas.setFillColor(DARK)
        canvas.rect(0, PAGE_H - 28 * mm, PAGE_W, 28 * mm, fill=1, stroke=0)
        canvas.setFillColor(colors.white)
        canvas.setFont("Helvetica-Bold", 13)
        canvas.drawCentredString(PAGE_W / 2, PAGE_H - 14 * mm, title)
        canvas.setFont("Helvetica", 8)
        canvas.setFillColor(colors.HexColor("#a0b0c8"))
        canvas.drawCentredString(PAGE_W / 2, PAGE_H - 21 * mm, f"{_VERSION}  |  {ts}")

        # Certification stamp (first page only)
        if cert_number and level:
            _draw_pdf_stamp(canvas, PAGE_W, PAGE_H, mm, cert_number, level)

        # Footer
        canvas.setFillColor(BG_GREY)
        canvas.rect(0, 0, PAGE_W, 18 * mm, fill=1, stroke=0)
        canvas.setFillColor(MID_GREY)
        canvas.setFont("Helvetica", 7)
        canvas.drawString(margin, 11 * mm, _VERSION)
        canvas.drawRightString(PAGE_W - margin, 11 * mm, f"Page {doc.page}")

        canvas.restoreState()

    return _handler


def _draw_pdf_stamp(canvas, PAGE_W, PAGE_H, mm, cert_number, level):
    """Draw a circular certification stamp on the PDF header area."""
    from reportlab.lib import colors

    GREEN = colors.HexColor("#0a4a23")
    WHITE = colors.white

    # Position: right side of header — enlarged for A4 readability
    cx = PAGE_W - 30 * mm
    cy = PAGE_H - 14 * mm
    r = 18 * mm

    canvas.saveState()

    # White filled background circle
    canvas.setFillColor(WHITE)
    canvas.setStrokeColor(GREEN)
    canvas.setLineWidth(2.8)
    canvas.circle(cx, cy, r, stroke=1, fill=1)

    # Inner circle
    canvas.setStrokeColor(GREEN)
    canvas.setLineWidth(1.2)
    canvas.circle(cx, cy, r * 0.80, stroke=1, fill=0)

    # Decorative dots at cardinal points between rings
    dot_r = r * 0.90
    canvas.setFillColor(GREEN)
    for angle_deg in [0, 90, 180, 270]:
        angle_rad = math.radians(angle_deg)
        dx = cx + dot_r * math.cos(angle_rad)
        dy = cy + dot_r * math.sin(angle_rad)
        canvas.circle(dx, dy, 0.9 * mm, stroke=0, fill=1)

    # Top arc text: CERTIFIED
    _draw_arc_text_on_canvas(
        canvas, cx, cy, r * 0.88, "CERTIFIED",
        center_angle_deg=90, char_gap_deg=14,
        facing="out", font="Helvetica-Bold", size=7.8, color=GREEN)

    # Bottom arc text: LLM AUDITOR
    _draw_arc_text_on_canvas(
        canvas, cx, cy, r * 0.88, "LLM AUDITOR",
        center_angle_deg=270, char_gap_deg=10.5,
        facing="in", font="Helvetica-Bold", size=6.8, color=GREEN)

    # Center: certification level
    canvas.setFillColor(GREEN)
    canvas.setFont("Helvetica-Bold", 9)
    canvas.drawCentredString(cx, cy + 2.5, level.upper())

    # Decorative line under level
    line_w = r * 0.44
    canvas.setStrokeColor(GREEN)
    canvas.setLineWidth(0.5)
    canvas.line(cx - line_w, cy - 1.5, cx + line_w, cy - 1.5)

    # Certificate number
    canvas.setFont("Helvetica", 5.5)
    canvas.drawCentredString(cx, cy - 6.5, cert_number)

    canvas.restoreState()


def _draw_arc_text_on_canvas(canvas, cx, cy, radius, text, center_angle_deg,
                              char_gap_deg, facing, font, size, color):
    """Draw text along a circular arc on reportlab canvas."""
    n = len(text)
    total_gap = (n - 1) * char_gap_deg

    canvas.saveState()
    canvas.setFillColor(color)

    for i, ch in enumerate(text):
        if facing == "out":
            # Top arc: text reads left to right, characters face outward
            angle_deg = center_angle_deg + total_gap / 2 - i * char_gap_deg
            rot = angle_deg - 90
        else:
            # Bottom arc: text reads left to right, characters face inward
            angle_deg = center_angle_deg - total_gap / 2 + i * char_gap_deg
            rot = angle_deg + 90

        angle_rad = math.radians(angle_deg)
        x = cx + radius * math.cos(angle_rad)
        y = cy + radius * math.sin(angle_rad)

        canvas.saveState()
        canvas.translate(x, y)
        canvas.rotate(rot)
        canvas.setFont(font, size)
        canvas.drawCentredString(0, 0, ch)
        canvas.restoreState()

    canvas.restoreState()
