"""Summary report — aggregate stats across multiple scan results.

Provides top-10 most common findings, trend analysis against previous
scans (from history), and an auto-generated executive summary paragraph.

Used via CLI: mcpaudit summary --history-dir .mcpaudit/
"""

from __future__ import annotations

import json
from collections import Counter
from dataclasses import dataclass, field
from typing import Any

from .models import ScanResult, ScanSummary, Severity


# ---------------------------------------------------------------------------
# Data models
# ---------------------------------------------------------------------------

@dataclass
class ExecutiveSummary:
    """Auto-generated executive summary paragraph."""

    paragraph: str
    risk_level: str  # "CRITICAL", "HIGH", "MODERATE", "LOW", "CLEAN"


@dataclass
class TrendInfo:
    """Trend comparison against previous scan."""

    direction: str     # "improving", "degrading", "stable", "baseline"
    findings_delta: int
    previous_findings: int
    previous_grade: str


@dataclass
class SummaryReport:
    """Aggregate summary report."""

    total_files: int = 0
    total_lines: int = 0
    total_findings: int = 0
    severity_counts: dict[str, int] = field(default_factory=dict)
    overall_grade: str = "A+"
    top_findings: list[tuple[str, int]] = field(default_factory=list)
    executive_summary: ExecutiveSummary = field(
        default_factory=lambda: ExecutiveSummary(paragraph="", risk_level="CLEAN")
    )
    trend: TrendInfo | None = None

    def to_dict(self) -> dict[str, Any]:
        result: dict[str, Any] = {
            "total_files": self.total_files,
            "total_lines": self.total_lines,
            "total_findings": self.total_findings,
            "severity_counts": self.severity_counts,
            "overall_grade": self.overall_grade,
            "top_findings": [
                {"rule_id": rule_id, "count": count}
                for rule_id, count in self.top_findings
            ],
            "executive_summary": {
                "paragraph": self.executive_summary.paragraph,
                "risk_level": self.executive_summary.risk_level,
            },
        }
        if self.trend is not None:
            result["trend"] = {
                "direction": self.trend.direction,
                "findings_delta": self.trend.findings_delta,
                "previous_findings": self.trend.previous_findings,
                "previous_grade": self.trend.previous_grade,
            }
        return result


# ---------------------------------------------------------------------------
# Top findings
# ---------------------------------------------------------------------------

def top_findings(
    results: list[ScanResult],
    limit: int = 10,
) -> list[tuple[str, int]]:
    """Compute the most common findings by rule ID.

    Args:
        results: List of scan results to aggregate.
        limit: Maximum number of top findings to return.

    Returns:
        List of (rule_id, count) tuples, sorted by count descending.
    """
    counter: Counter[str] = Counter()
    for result in results:
        for finding in result.findings:
            counter[finding.rule_id] += 1
    return counter.most_common(limit)


# ---------------------------------------------------------------------------
# Executive summary generation
# ---------------------------------------------------------------------------

def _generate_executive_summary(
    total_files: int,
    total_findings: int,
    severity_counts: dict[str, int],
    overall_grade: str,
) -> ExecutiveSummary:
    """Generate an executive summary paragraph from scan stats.

    Args:
        total_files: Number of files scanned.
        total_findings: Total number of findings.
        severity_counts: Finding counts by severity.
        overall_grade: Overall security grade.

    Returns:
        ExecutiveSummary with paragraph and risk level.
    """
    critical = severity_counts.get("CRITICAL", 0)
    high = severity_counts.get("HIGH", 0)
    medium = severity_counts.get("MEDIUM", 0)

    # Determine risk level
    if critical > 0:
        risk_level = "CRITICAL"
    elif high > 0:
        risk_level = "HIGH"
    elif medium > 0:
        risk_level = "MODERATE"
    elif total_findings > 0:
        risk_level = "LOW"
    else:
        risk_level = "CLEAN"

    # Build paragraph
    parts: list[str] = []

    if total_findings == 0:
        parts.append(
            f"Scanned {total_files} file(s) with no security findings. "
            f"Overall grade: {overall_grade}."
        )
    else:
        parts.append(
            f"Scanned {total_files} file(s) and identified "
            f"{total_findings} security finding(s). "
            f"Overall grade: {overall_grade}."
        )

    if critical > 0:
        parts.append(
            f" {critical} critical issue(s) require immediate attention."
        )
    if high > 0:
        parts.append(
            f" {high} high-severity issue(s) should be addressed promptly."
        )
    if total_findings == 0:
        parts.append(" No issues detected — the codebase meets security standards.")

    paragraph = "".join(parts)

    return ExecutiveSummary(
        paragraph=paragraph,
        risk_level=risk_level,
    )


# ---------------------------------------------------------------------------
# Trend analysis
# ---------------------------------------------------------------------------

def _compute_trend(
    current_findings: int,
    history: list[dict[str, Any]],
) -> TrendInfo | None:
    """Compute trend by comparing current scan with last history entry.

    Args:
        current_findings: Total findings in current scan.
        history: List of history entries (from history.load_history).

    Returns:
        TrendInfo if history is available, None otherwise.
    """
    if not history:
        return None

    last = history[-1]
    scan_data = last.get("scan", {})
    summary_data = scan_data.get("summary", {})
    prev_findings = summary_data.get("total_findings", 0)
    prev_grade = summary_data.get("overall_grade", "?")

    delta = current_findings - prev_findings
    if delta < 0:
        direction = "improving"
    elif delta > 0:
        direction = "degrading"
    else:
        direction = "stable"

    return TrendInfo(
        direction=direction,
        findings_delta=delta,
        previous_findings=prev_findings,
        previous_grade=prev_grade,
    )


# ---------------------------------------------------------------------------
# Build summary
# ---------------------------------------------------------------------------

def build_summary(
    results: list[ScanResult],
    history: list[dict[str, Any]] | None = None,
) -> SummaryReport:
    """Build a summary report from scan results.

    Args:
        results: List of ScanResult objects.
        history: Optional history entries for trend comparison.

    Returns:
        SummaryReport with all aggregate stats.
    """
    total_files = len(results)
    total_lines = sum(r.lines_scanned for r in results)
    total_findings_count = sum(len(r.findings) for r in results)

    # Severity counts
    sev_counts: dict[str, int] = {sev.value: 0 for sev in Severity}
    for result in results:
        for finding in result.findings:
            sev_counts[finding.severity.value] += 1

    # Overall grade (worst across all)
    summary_obj = ScanSummary(results=results)
    overall_grade = summary_obj.overall_grade

    # Top findings
    top = top_findings(results)

    # Executive summary
    exec_summary = _generate_executive_summary(
        total_files, total_findings_count, sev_counts, overall_grade,
    )

    # Trend
    trend = None
    if history:
        trend = _compute_trend(total_findings_count, history)

    return SummaryReport(
        total_files=total_files,
        total_lines=total_lines,
        total_findings=total_findings_count,
        severity_counts=sev_counts,
        overall_grade=overall_grade,
        top_findings=top,
        executive_summary=exec_summary,
        trend=trend,
    )


# ---------------------------------------------------------------------------
# Formatters
# ---------------------------------------------------------------------------

def format_summary_report_text(report: SummaryReport) -> str:
    """Format a summary report as human-readable text.

    Args:
        report: SummaryReport to format.

    Returns:
        Formatted text string.
    """
    lines: list[str] = []
    lines.append(f"\n{'=' * 60}")
    lines.append("  mcpaudit — Summary Report")
    lines.append(f"{'=' * 60}")

    if report.total_files == 0:
        lines.append("  No scan results to summarize.")
        lines.append(f"{'=' * 60}\n")
        return "\n".join(lines)

    lines.append(f"  Files scanned:   {report.total_files}")
    lines.append(f"  Lines scanned:   {report.total_lines:,}")
    lines.append(f"  Total findings:  {report.total_findings}")
    lines.append(f"  Overall grade:   {report.overall_grade}")
    lines.append(f"{'=' * 60}")

    # Severity breakdown
    lines.append("\n  Severity Breakdown:")
    for sev in Severity:
        count = report.severity_counts.get(sev.value, 0)
        if count > 0:
            lines.append(f"    {sev.value:<10} {count}")

    # Top findings
    if report.top_findings:
        lines.append(f"\n  Top Findings:")
        for rule_id, count in report.top_findings:
            lines.append(f"    {rule_id:<16} {count} occurrence(s)")

    # Trend
    if report.trend is not None:
        lines.append(f"\n  Trend: {report.trend.direction.upper()}")
        sign = "+" if report.trend.findings_delta > 0 else ""
        lines.append(
            f"    Delta: {sign}{report.trend.findings_delta} findings "
            f"(prev: {report.trend.previous_findings}, "
            f"grade: {report.trend.previous_grade})"
        )

    # Executive summary
    lines.append(f"\n  Executive Summary:")
    lines.append(f"    {report.executive_summary.paragraph}")

    lines.append(f"\n{'=' * 60}\n")
    return "\n".join(lines)


def format_summary_report_json(report: SummaryReport) -> str:
    """Format a summary report as JSON.

    Args:
        report: SummaryReport to format.

    Returns:
        JSON string.
    """
    return json.dumps(report.to_dict(), indent=2, ensure_ascii=False)
