"""Report generation: markdown and JSON output."""

from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path

from govai.models import (
    ConfidenceSource,
    InventoryReport,
    RiskTier,
)

# Order for risk tiers in the report
_TIER_ORDER = [
    RiskTier.UNACCEPTABLE,
    RiskTier.HIGH,
    RiskTier.LIMITED,
    RiskTier.MINIMAL,
    RiskTier.UNCLEAR,
]

_DISCLAIMER = (
    "This report is generated by govai and is a starting point for EU "
    "AI Act compliance review. It is not a legal opinion. Classifications "
    "marked 'inferred' should be verified. govai is open source: "
    "github.com/kevin-chacon/govai-eu"
)

RISK_TIER_DEFINITIONS = {
    "UNACCEPTABLE": {
        "label": "UNACCEPTABLE RISK \u2014 Prohibited",
        "plain": (
            "This AI system is prohibited under the EU AI Act "
            "and must be discontinued."
        ),
        "examples": (
            "Social scoring by public authorities, real-time biometric "
            "surveillance in public spaces, subliminal manipulation."
        ),
        "action": "Discontinue use immediately. Seek legal advice.",
    },
    "HIGH": {
        "label": "HIGH RISK",
        "plain": (
            "This AI system makes or significantly influences decisions "
            "that affect people's rights, employment, access to services, "
            "or safety. Strict obligations apply before deployment."
        ),
        "examples": (
            "CV screening, hiring/firing decisions, credit scoring, "
            "autonomous customer routing, medical diagnosis aids."
        ),
        "action": (
            "Requires: conformity assessment, technical documentation "
            "(Article 11), risk management system (Article 9), human "
            "oversight protocol (Article 14), audit logging, and "
            "transparency notices."
        ),
    },
    "LIMITED": {
        "label": "LIMITED RISK",
        "plain": (
            "This AI system has transparency obligations. Users must be "
            "informed they are interacting with or affected by AI."
        ),
        "examples": (
            "Chatbots, AI-generated content, emotion recognition systems."
        ),
        "action": (
            "Add clear AI disclosure notices wherever users interact "
            "with or are affected by this system."
        ),
    },
    "MINIMAL": {
        "label": "MINIMAL RISK",
        "plain": (
            "This AI system has no specific EU AI Act obligations "
            "beyond general good practice."
        ),
        "examples": (
            "Spam filters, general writing assistants, search features, "
            "recommendation engines in most contexts."
        ),
        "action": (
            "No mandatory action required. Keep this report as "
            "evidence of due diligence."
        ),
    },
    "UNCLEAR": {
        "label": "UNCLEAR \u2014 Requires Verification",
        "plain": (
            "This system's risk tier could not be determined from "
            "available information. It depends on how the system "
            "is configured or deployed."
        ),
        "examples": (
            "Custom deployments, workflow automation with AI steps, "
            "platforms where risk depends on specific use case."
        ),
        "action": (
            "Treat as HIGH RISK until verified. Contact the tool "
            "owner or vendor to confirm the decision-making scope."
        ),
    },
}


def next_steps_section(report: InventoryReport) -> str:
    """Generate a plain-language next steps section based on risk tiers found.

    Returns markdown string.
    """
    lines: list[str] = []
    summary = report.risk_summary

    unacceptable_count = summary.get(RiskTier.UNACCEPTABLE, 0)
    high_count = summary.get(RiskTier.HIGH, 0)
    unclear_count = summary.get(RiskTier.UNCLEAR, 0)
    limited_count = summary.get(RiskTier.LIMITED, 0)

    has_severe = unacceptable_count > 0 or high_count > 0 or unclear_count > 0

    if unacceptable_count > 0:
        lines.append("## \u26d4 Next Steps \u2014 Immediate Action Required")
        lines.append("")
        lines.append(
            f"You have {unacceptable_count} prohibited AI system(s). "
            "These must be discontinued immediately. The EU AI Act "
            "prohibits their use with no grace period."
        )
        lines.append("")
        lines.append(
            "**Action:** Stop using these systems. Seek qualified legal "
            "advice before considering any alternative deployment."
        )

    if high_count > 0:
        if not lines:  # No UNACCEPTABLE header yet
            lines.append(
                "## \u26a0 Next Steps \u2014 Compliance Work Required"
            )
            lines.append("")
        else:
            lines.append("")
        lines.append(
            f"You have {high_count} high-risk AI system(s). Each requires "
            "the following before it can lawfully operate:"
        )
        lines.append("")
        lines.append(
            "- **Technical documentation** (Article 11) \u2014 document the "
            "system's purpose, architecture, training data, and performance"
        )
        lines.append(
            "- **Risk management system** (Article 9) \u2014 identify, analyse, "
            "and mitigate risks throughout the system lifecycle"
        )
        lines.append(
            "- **Human oversight mechanism** (Article 14) \u2014 ensure humans "
            "can monitor, intervene, and override the system"
        )
        lines.append(
            "- **Audit logging** \u2014 maintain logs of autonomous decisions"
        )
        lines.append(
            "- **Transparency notice** \u2014 inform affected persons that AI "
            "is involved in decisions that affect them"
        )
        lines.append("")
        lines.append(
            "**Enforcement deadline:** August 2026 for most high-risk systems."
        )
        lines.append("")
        lines.append(
            "**Recommended:** engage a qualified EU AI Act compliance advisor. "
            "The EU AI Act Service Desk provides free guidance: "
            "ai-act-service-desk.ec.europa.eu"
        )

    if unclear_count > 0:
        lines.append("")
        lines.append(
            f"**{unclear_count} system(s) with unclear risk tier:**"
        )
        lines.append(
            "Treat each as HIGH RISK until verified. Contact the tool owner "
            "or vendor to confirm what autonomous decisions the system makes "
            "and what data it processes. The notes section of each tool entry "
            "indicates what information is needed."
        )

    if limited_count > 0:
        lines.append("")
        lines.append(
            f"**{limited_count} limited-risk system(s):**"
        )
        lines.append(
            "Add AI disclosure notices wherever users interact with or are "
            "affected by these systems. Users must be informed that AI "
            "is involved."
        )

    if not has_severe:
        lines.clear()
        lines.append("## \u2713 Next Steps")
        lines.append("")
        lines.append(
            "No high-risk AI systems were identified in this inventory."
        )
        lines.append("")
        lines.append(
            "**Keep this report** as documented evidence of due diligence. "
            "Re-run govai-eu when you add new software tools or significantly "
            "change how existing tools are used."
        )
        lines.append("")
        lines.append(
            "Note: this report only covers tools on your input list. "
            "Employee-used AI tools not on the list are not assessed."
        )

        if limited_count > 0:
            lines.append("")
            lines.append(
                f"**{limited_count} limited-risk system(s):**"
            )
            lines.append(
                "Add AI disclosure notices wherever users interact with or "
                "are affected by these systems. Users must be informed that "
                "AI is involved."
            )

    # Always append footer
    lines.append("")
    lines.append("---")
    lines.append(
        "*This report is a starting point, not a compliance certificate. "
        "For official guidance see: ai-act-service-desk.ec.europa.eu*"
    )

    return "\n".join(lines)


def to_markdown(report: InventoryReport) -> str:
    """Generate a markdown report from an InventoryReport."""
    lines: list[str] = []

    # Title
    lines.append("# govai — AI System Inventory Report")
    lines.append("")

    # Metadata
    generated = report.generated_at.strftime("%Y-%m-%d %H:%M UTC")
    lines.append(f"**Generated:** {generated}  ")
    lines.append(f"**Input file:** {report.input_file}  ")
    lines.append(f"**Tools analysed:** {report.tool_count}")
    lines.append("")

    # Risk summary table
    lines.append("## Risk Summary")
    lines.append("")
    lines.append("| Risk Tier | Count | Meaning |")
    lines.append("|-----------|-------|---------|")
    for tier in _TIER_ORDER:
        count = report.risk_summary.get(tier, 0)
        if count > 0:
            defn = RISK_TIER_DEFINITIONS.get(tier.value, {})
            meaning = defn.get("plain", "")
            lines.append(f"| {tier.value} | {count} | {meaning} |")
    lines.append("")

    # Next Steps
    lines.append(next_steps_section(report))
    lines.append("")

    # Risk Tier Reference
    present_tiers = [
        tier for tier in _TIER_ORDER
        if report.risk_summary.get(tier, 0) > 0
    ]
    if present_tiers:
        lines.append("## Risk Tier Reference")
        lines.append("")
        for tier in present_tiers:
            defn = RISK_TIER_DEFINITIONS.get(tier.value, {})
            lines.append(f"### {defn.get('label', tier.value)}")
            lines.append(defn.get("plain", ""))
            lines.append("")
            lines.append(f"**Examples:** {defn.get('examples', '')}")
            lines.append("")
            lines.append(f"**What to do:** {defn.get('action', '')}")
            lines.append("")


    # Per-tool sections grouped by risk tier
    for tier in _TIER_ORDER:
        tier_results = [r for r in report.results if r.risk_tier == tier]
        if not tier_results:
            continue

        for result in tier_results:
            vendor_str = f" ({result.vendor})" if result.vendor else ""
            lines.append(f"## {tier.value} — {result.tool_name}{vendor_str}")
            lines.append("")

            # Confidence with explanation
            if result.confidence_source == ConfidenceSource.REGISTRY_MATCH:
                conf_text = (
                    f"**Confidence:** {result.confidence_source.value} "
                    "*(found in govai-eu registry)*"
                )
            else:
                conf_text = (
                    f"**Confidence:** {result.confidence_source.value} "
                    "*(classified by LLM — verify before relying on this)*"
                )
            lines.append(conf_text)
            lines.append("")

            # AI Features
            if result.ai_features:
                lines.append("**AI Features:**")
                for feat in result.ai_features:
                    lines.append(f"- {feat}")
                lines.append("")

            # Decision Scope
            scope = result.decision_scope or "No autonomous decisions identified"
            lines.append(f"**Decision Scope:** {scope}")
            lines.append("")

            # Data Processed
            if result.data_categories:
                lines.append("**Data Processed:**")
                for cat in result.data_categories:
                    lines.append(f"- {cat}")
                lines.append("")

            # Obligations
            if result.obligations:
                lines.append("**EU AI Act Obligations:**")
                for ob in result.obligations:
                    lines.append(f"- {ob}")
                lines.append("")

            # Missing Documentation
            if result.missing_docs:
                lines.append("**Missing Documentation:**")
                for doc in result.missing_docs:
                    lines.append(f"- {doc}")
                lines.append("")

            # Notes
            if result.notes:
                lines.append(f"**Notes:** {result.notes}")
                lines.append("")

            lines.append("---")
            lines.append("")

    # Footer disclaimer
    lines.append("")
    lines.append(f"*{_DISCLAIMER}*")
    lines.append("")

    return "\n".join(lines)


def to_json(report: InventoryReport) -> str:
    """Serialise InventoryReport to JSON with enum string values."""
    return report.model_dump_json(indent=2)


_BADGE_COLOURS = {
    "UNACCEPTABLE": ("#dc2626", "#ffffff"),
    "HIGH": ("#ea580c", "#ffffff"),
    "LIMITED": ("#ca8a04", "#ffffff"),
    "MINIMAL": ("#16a34a", "#ffffff"),
    "UNCLEAR": ("#6b7280", "#ffffff"),
}

_BORDER_COLOURS = {
    "UNACCEPTABLE": "#dc2626",
    "HIGH": "#ea580c",
    "UNCLEAR": "#ea580c",
    "LIMITED": "#ca8a04",
    "MINIMAL": "#16a34a",
}


def _badge_html(tier_value: str) -> str:
    bg, fg = _BADGE_COLOURS.get(tier_value, ("#6b7280", "#ffffff"))
    return (
        f'<span style="display:inline-block;padding:2px 10px;'
        f'border-radius:3px;font-size:12px;font-weight:600;'
        f'letter-spacing:0.04em;background:{bg};color:{fg};'
        f'">{tier_value}</span>'
    )


def _md_to_html_inline(text: str) -> str:
    """Convert basic markdown bold **x** to <strong>x</strong>."""
    import re
    text = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text)
    return text


def _next_steps_html(report: InventoryReport) -> str:
    """Generate the next steps section as HTML."""
    md_text = next_steps_section(report)
    lines = md_text.split("\n")
    html_parts: list[str] = []
    in_list = False

    for line in lines:
        if line.startswith("## "):
            html_parts.append(f"<h3>{_md_to_html_inline(line[3:])}</h3>")
        elif line.startswith("- "):
            if not in_list:
                html_parts.append("<ul>")
                in_list = True
            html_parts.append(f"<li>{_md_to_html_inline(line[2:])}</li>")
        elif line.startswith("---"):
            continue  # Skip horizontal rule separators
        elif line.startswith("*") and line.endswith("*"):
            html_parts.append(
                f"<p style='color:#6b7280;font-size:13px;'>"
                f"<em>{line.strip('*')}</em></p>"
            )
        elif line.strip() == "":
            if in_list:
                html_parts.append("</ul>")
                in_list = False
        else:
            if in_list:
                html_parts.append("</ul>")
                in_list = False
            html_parts.append(f"<p>{_md_to_html_inline(line)}</p>")

    if in_list:
        html_parts.append("</ul>")

    return "\n".join(html_parts)


def to_html(report: InventoryReport) -> str:
    """Generate a complete standalone HTML report.

    All CSS is in a <style> block. No external dependencies.
    Opens offline. Prints cleanly.
    """
    date_str = report.generated_at.strftime("%Y-%m-%d %H:%M UTC")
    present_tiers = [
        tier for tier in _TIER_ORDER
        if report.risk_summary.get(tier, 0) > 0
    ]

    # Determine border colour for next steps box
    highest = "MINIMAL"
    for tier in _TIER_ORDER:
        if report.risk_summary.get(tier, 0) > 0:
            highest = tier.value
            break
    border_colour = _BORDER_COLOURS.get(highest, "#16a34a")

    # --- Build parts ---
    # Summary cards
    cards_html = []
    for tier in _TIER_ORDER:
        count = report.risk_summary.get(tier, 0)
        if count > 0:
            cards_html.append(
                f'<div class="card">'
                f'<div class="card-count">{count}</div>'
                f'{_badge_html(tier.value)}'
                f'</div>'
            )
    cards_row = "\n".join(cards_html)

    # Next steps box
    next_steps = _next_steps_html(report)

    # Risk Tier Reference (collapsible)
    ref_items: list[str] = []
    for tier in present_tiers:
        defn = RISK_TIER_DEFINITIONS.get(tier.value, {})
        ref_items.append(
            f'<div style="margin-bottom:16px;">'
            f'{_badge_html(tier.value)} '
            f'<strong>{defn.get("label", tier.value)}</strong>'
            f'<p>{defn.get("plain", "")}</p>'
            f'<p><strong>Examples:</strong> {defn.get("examples", "")}</p>'
            f'<p><strong>What to do:</strong> {defn.get("action", "")}</p>'
            f'</div>'
        )
    ref_html = "\n".join(ref_items)

    # Per-tool sections
    tools_html: list[str] = []
    for tier in _TIER_ORDER:
        tier_results = [r for r in report.results if r.risk_tier == tier]
        if not tier_results:
            continue

        tools_html.append(
            f'<h2>{_badge_html(tier.value)} '
            f'{RISK_TIER_DEFINITIONS.get(tier.value, {}).get("label", tier.value)}'
            f'</h2>'
        )

        for result in tier_results:
            vendor_str = f" ({result.vendor})" if result.vendor else ""
            is_open = tier in (RiskTier.UNACCEPTABLE, RiskTier.HIGH)
            open_attr = " open" if is_open else ""

            # Confidence badge
            if result.confidence_source == ConfidenceSource.REGISTRY_MATCH:
                conf_badge = (
                    '<span style="font-size:12px;color:#6b7280;">'
                    'registry match</span>'
                )
            else:
                conf_badge = (
                    '<span style="font-size:12px;color:#ea580c;">'
                    'inferred (LLM)</span>'
                )

            inner: list[str] = []

            # Confidence explanation
            if result.confidence_source == ConfidenceSource.REGISTRY_MATCH:
                inner.append(
                    '<p><strong>Confidence:</strong> Found in govai-eu '
                    'registry.</p>'
                )
            else:
                inner.append(
                    '<p><strong>Confidence:</strong> Classified by LLM '
                    '\u2014 verify before relying on this.</p>'
                )

            if result.ai_features:
                inner.append('<p><strong>AI Features:</strong></p><ul>')
                for feat in result.ai_features:
                    inner.append(f'<li>{feat}</li>')
                inner.append('</ul>')

            scope = result.decision_scope or "No autonomous decisions identified"
            inner.append(
                f'<p><strong>Decision Scope:</strong> {scope}</p>'
            )

            if result.data_categories:
                inner.append('<p><strong>Data Processed:</strong></p><ul>')
                for cat in result.data_categories:
                    inner.append(f'<li>{cat}</li>')
                inner.append('</ul>')

            if result.obligations:
                inner.append(
                    '<p><strong>EU AI Act Obligations:</strong></p><ul>'
                )
                for ob in result.obligations:
                    inner.append(f'<li>{ob}</li>')
                inner.append('</ul>')

            if result.missing_docs:
                inner.append(
                    '<p><strong>Missing Documentation:</strong></p><ul>'
                )
                for doc in result.missing_docs:
                    inner.append(f'<li>{doc}</li>')
                inner.append('</ul>')

            if result.notes:
                inner.append(
                    f'<p><strong>Notes:</strong> {result.notes}</p>'
                )

            inner_html = "\n".join(inner)
            tools_html.append(
                f'<details{open_attr}>'
                f'<summary>{result.tool_name}{vendor_str} {conf_badge}'
                f'</summary>'
                f'<div class="tool-body">{inner_html}</div>'
                f'</details>'
            )

    tools_section = "\n".join(tools_html)

    html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>govai-eu Report \u2014 {date_str}</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
    Arial, sans-serif;
  font-size: 15px;
  line-height: 1.6;
  color: #111111;
  background: #ffffff;
}}
.container {{
  max-width: 900px;
  margin: 0 auto;
  padding: 40px 32px;
}}
h1 {{ font-size: 24px; margin-bottom: 8px; }}
h2 {{ font-size: 20px; margin: 32px 0 16px; }}
h3 {{ font-size: 18px; margin: 16px 0 8px; }}
.meta {{ font-size: 13px; color: #6b7280; margin-bottom: 24px; }}
.cards {{
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 24px 0;
}}
.card {{
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 16px 24px;
  text-align: center;
  min-width: 100px;
}}
.card-count {{
  font-size: 32px;
  font-weight: 700;
  margin-bottom: 4px;
}}
.next-steps {{
  border-left: 4px solid {border_colour};
  background: #f9fafb;
  padding: 20px 24px;
  margin: 24px 0;
  border-radius: 0 8px 8px 0;
}}
.next-steps h3 {{ margin-top: 0; }}
.next-steps ul {{ padding-left: 24px; margin: 8px 0; }}
.next-steps p {{ margin: 8px 0; }}
details {{
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  margin: 8px 0;
  padding: 0;
}}
summary {{
  padding: 12px 16px;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}}
summary::-webkit-details-marker {{ display: none; }}
summary::before {{
  content: "\u25b6";
  display: inline-block;
  margin-right: 8px;
  font-size: 11px;
  transition: transform 0.2s;
}}
details[open] > summary::before {{ transform: rotate(90deg); }}
.tool-body {{
  padding: 0 16px 16px;
  border-top: 1px solid #e5e7eb;
}}
.tool-body p {{ margin: 8px 0; }}
.tool-body ul {{ padding-left: 24px; margin: 4px 0; }}
.footer {{
  margin-top: 48px;
  padding-top: 16px;
  border-top: 1px solid #e5e7eb;
  font-size: 13px;
  color: #6b7280;
}}
@media print {{
  details {{ display: block !important; }}
  details > summary {{ display: none; }}
  .tool-body {{ border-top: none; padding-top: 8px; }}
}}
</style>
</head>
<body>
<div class="container">

<h1>govai-eu \u2014 AI System Inventory Report</h1>
<div class="meta">
  Generated: {date_str} | Input: {report.input_file}
  | Tools analysed: {report.tool_count}
</div>

<div class="cards">
{cards_row}
</div>

<div class="next-steps">
{next_steps}
</div>

<details>
<summary>Risk Tier Reference \u2014 what these categories mean</summary>
<div class="tool-body">
{ref_html}
</div>
</details>

{tools_section}

<div class="footer">
<p>{_DISCLAIMER}</p>
</div>

</div>
</body>
</html>
"""
    return html


def write_reports(
    report: InventoryReport, output_dir: Path, format: str
) -> list[Path]:
    """Write report files and return the list of paths written.

    Args:
        report: The inventory report to write.
        output_dir: Directory to write files to.
        format: "markdown", "json", "html", "both", or "all".

    Returns:
        List of file paths that were written.
    """
    output_dir.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    stem = f"govai_report_{timestamp}"
    written: list[Path] = []

    if format in ("markdown", "both", "all"):
        md_path = output_dir / f"{stem}.md"
        md_path.write_text(to_markdown(report), encoding="utf-8")
        written.append(md_path)

    if format in ("json", "both", "all"):
        json_path = output_dir / f"{stem}.json"
        json_path.write_text(to_json(report), encoding="utf-8")
        written.append(json_path)

    if format in ("html", "all"):
        html_path = output_dir / f"{stem}.html"
        html_path.write_text(to_html(report), encoding="utf-8")
        written.append(html_path)

    return written
