"""Markdown threat report generator.

Produces comprehensive, human-readable reports combining analysis from
all four frameworks (STRIDE, DREAD, LINDDUN, PASTA) with CWE/MITRE
cross-references and prioritised mitigations.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import TYPE_CHECKING

from threatlens.models import (
    AttackNode,
    AttackTree,
    DreadScore,
    FrameworkCorrelation,
    GateType,
    PastaStageResult,
    PrivacyImpact,
    Threat,
)

if TYPE_CHECKING:
    from collections.abc import Sequence


def _severity_badge(severity: str) -> str:
    badges = {
        "critical": "🔴 CRITICAL",
        "high": "🟠 HIGH",
        "medium": "🟡 MEDIUM",
        "low": "🟢 LOW",
        "info": "🔵 INFO",
    }
    return badges.get(severity, severity.upper())


def _dread_bar(score: float) -> str:
    filled = round(score)
    return "█" * filled + "░" * (10 - filled)


def _render_attack_node(node: AttackNode, depth: int = 0) -> list[str]:
    indent = "  " * depth
    gate_label = f" [{node.gate.value}]" if node.gate != GateType.LEAF else ""
    prob = f" (p={node.likelihood:.0%})" if node.likelihood is not None else ""
    imp = f" [impact={node.impact}]" if node.impact is not None else ""
    lines = [f"{indent}- {node.label}{gate_label}{prob}{imp}"]
    for child in node.children:
        lines.extend(_render_attack_node(child, depth + 1))
    return lines


# ---------------------------------------------------------------------------
# Section renderers
# ---------------------------------------------------------------------------


def _render_header(system_name: str) -> str:
    ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    return (
        f"# Threat Analysis Report: {system_name}\n\n"
        f"*Generated by ThreatLens on {ts}*\n\n"
        f"---\n"
    )


def _render_executive_summary(threats: Sequence[Threat]) -> str:
    counts: dict[str, int] = {}
    for t in threats:
        counts[t.severity.value] = counts.get(t.severity.value, 0) + 1

    lines = ["## Executive Summary\n"]
    total = len(threats)
    lines.append(f"**{total} threat(s)** identified across the system.\n")
    lines.append("| Severity | Count |")
    lines.append("|----------|-------|")
    for sev in ["critical", "high", "medium", "low", "info"]:
        if sev in counts:
            lines.append(f"| {_severity_badge(sev)} | {counts[sev]} |")
    lines.append("")
    return "\n".join(lines)


def _render_stride_section(threats: Sequence[Threat]) -> str:
    lines = ["## STRIDE Threat Analysis\n"]
    for t in threats:
        cats = ", ".join(c.value.replace("_", " ").title() for c in t.stride_categories)
        lines.append(f"### {_severity_badge(t.severity.value)} {t.title}\n")
        lines.append(f"**ID:** `{t.id}`  ")
        lines.append(f"**Categories:** {cats}  ")
        lines.append(f"**Components:** {', '.join(t.affected_components) or 'N/A'}\n")
        lines.append(f"{t.description}\n")
        if t.mitigations:
            lines.append("**Mitigations:**\n")
            for m in t.mitigations:
                lines.append(f"- {m}")
            lines.append("")
    return "\n".join(lines)


def _render_dread_section(scored: Sequence[tuple[Threat, DreadScore]]) -> str:
    lines = ["## DREAD Risk Scoring\n"]
    lines.append("| Threat | D | R | E | A | D | Overall | Rating |")
    lines.append("|--------|---|---|---|---|---|---------|--------|")
    for threat, ds in scored:
        lines.append(
            f"| {threat.title} "
            f"| {ds.damage} | {ds.reproducibility} | {ds.exploitability} "
            f"| {ds.affected_users} | {ds.discoverability} "
            f"| **{ds.overall}** | {_severity_badge(ds.rating.value)} |"
        )
    lines.append("")

    if scored:
        lines.append("### Score Breakdown\n")
        for threat, ds in scored[:5]:
            lines.append(f"**{threat.title}** (overall: {ds.overall}/10)\n")
            lines.append(f"- Damage:         {_dread_bar(ds.damage)} {ds.damage}")
            lines.append(
                f"- Reproducibility: {_dread_bar(ds.reproducibility)}"
                f" {ds.reproducibility}"
            )
            lines.append(
                f"- Exploitability:  {_dread_bar(ds.exploitability)}"
                f" {ds.exploitability}"
            )
            lines.append(
                f"- Affected Users:  {_dread_bar(ds.affected_users)}"
                f" {ds.affected_users}"
            )
            lines.append(
                f"- Discoverability: {_dread_bar(ds.discoverability)}"
                f" {ds.discoverability}"
            )
            lines.append("")
    return "\n".join(lines)


def _render_linddun_section(impacts: Sequence[PrivacyImpact]) -> str:
    if not impacts:
        return (
            "## LINDDUN Privacy Analysis\n\nNo privacy-specific threats identified.\n"
        )

    lines = ["## LINDDUN Privacy Analysis\n"]
    for imp in impacts:
        cat_name = imp.category.value.replace("_", " ").title()
        lines.append(f"### {_severity_badge(imp.severity.value)} {cat_name}\n")
        lines.append(f"{imp.description}\n")
        lines.append(f"**Affected data types:** {', '.join(imp.affected_data_types)}\n")
        if imp.recommendations:
            lines.append("**Recommendations:**\n")
            for r in imp.recommendations:
                lines.append(f"- {r}")
            lines.append("")
    return "\n".join(lines)


def _render_pasta_section(stages: Sequence[PastaStageResult]) -> str:
    if not stages:
        return ""

    lines = ["## PASTA Process Analysis\n"]
    for stage in stages:
        lines.append(f"### Stage {stage.stage.value[0]}: {stage.title}\n")
        for finding in stage.findings:
            lines.append(f"- {finding}")
        lines.append("")
    return "\n".join(lines)


def _render_attack_trees_section(trees: Sequence[AttackTree]) -> str:
    if not trees:
        return ""

    lines = ["## Attack Trees\n"]
    for tree in trees:
        lines.append(f"### Target: {tree.target}\n")
        lines.append(f"_{tree.description}_\n")
        lines.append("```")
        lines.extend(_render_attack_node(tree.root))
        lines.append("```\n")
    return "\n".join(lines)


def _render_correlation_section(correlations: Sequence[FrameworkCorrelation]) -> str:
    if not correlations:
        return ""

    lines = ["## Cross-Framework Correlation Matrix\n"]
    lines.append("| Threat | STRIDE | DREAD | LINDDUN | CWE | MITRE ATT&CK |")
    lines.append("|--------|--------|-------|---------|-----|--------------|")
    for c in correlations:
        stride = ", ".join(s.value[:4].upper() for s in c.stride)
        dread_val = str(c.dread.overall) if c.dread else "N/A"
        linddun = ", ".join(cat.value[:4].upper() for cat in c.linddun)
        cwes = ", ".join(c.cwe_ids[:3]) + ("..." if len(c.cwe_ids) > 3 else "")
        mitre = ", ".join(t.split(" ")[0] for t in c.mitre_techniques[:2])
        if len(c.mitre_techniques) > 2:
            mitre += "..."
        lines.append(
            f"| {c.threat_title} | {stride} | {dread_val}"
            f" | {linddun} | {cwes} | {mitre} |"
        )
    lines.append("")
    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def generate_report(
    system_name: str,
    threats: Sequence[Threat],
    scored: Sequence[tuple[Threat, DreadScore]] | None = None,
    privacy_impacts: Sequence[PrivacyImpact] | None = None,
    pasta_stages: Sequence[PastaStageResult] | None = None,
    attack_trees: Sequence[AttackTree] | None = None,
    correlations: Sequence[FrameworkCorrelation] | None = None,
) -> str:
    """Generate a comprehensive markdown threat analysis report.

    Args:
        system_name: Name of the system being analysed.
        threats: Identified threats from STRIDE analysis.
        scored: Optional DREAD-scored threats.
        privacy_impacts: Optional LINDDUN privacy findings.
        pasta_stages: Optional PASTA stage results.
        attack_trees: Optional attack tree decompositions.
        correlations: Optional cross-framework correlations.

    Returns:
        Complete markdown report as a string.
    """
    sections = [
        _render_header(system_name),
        _render_executive_summary(threats),
        _render_stride_section(threats),
    ]

    if scored:
        sections.append(_render_dread_section(scored))

    if privacy_impacts is not None:
        sections.append(_render_linddun_section(privacy_impacts))

    if pasta_stages:
        sections.append(_render_pasta_section(pasta_stages))

    if attack_trees:
        sections.append(_render_attack_trees_section(attack_trees))

    if correlations:
        sections.append(_render_correlation_section(correlations))

    sections.append(
        "---\n\n*Report generated by "
        "[ThreatLens](https://github.com/manambharadwaj/"
        "threatlens) — Multi-framework threat "
        "intelligence by Manam Bharadwaj.*\n"
    )

    return "\n".join(sections)
