"""Scan history management — store, retrieve, and analyze scan results over time.

Persists scan results as JSON files in a configurable directory,
enabling trend tracking and regression detection.
"""

from __future__ import annotations

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


DEFAULT_HISTORY_DIR = ".mcpaudit-history"


def _ensure_history_dir(history_dir: Path) -> Path:
    """Create history directory if it doesn't exist."""
    history_dir.mkdir(parents=True, exist_ok=True)
    return history_dir


def save_scan(
    summary_dict: dict[str, Any],
    history_dir: Path | None = None,
    label: str = "",
) -> Path:
    """Save a scan result to the history directory.

    Args:
        summary_dict: ScanSummary.to_dict() output.
        history_dir: Directory to store history. Defaults to .mcpaudit-history/.
        label: Optional label for this scan (e.g., "pre-release", "ci-main").

    Returns:
        Path to the saved history file.
    """
    history_dir = _ensure_history_dir(history_dir or Path(DEFAULT_HISTORY_DIR))
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    entry: dict[str, Any] = {
        "timestamp": timestamp,
        "label": label,
        "scan": summary_dict,
    }
    filename = f"scan-{timestamp}.json"
    filepath = history_dir / filename
    filepath.write_text(json.dumps(entry, indent=2), encoding="utf-8")
    return filepath


def load_history(
    history_dir: Path | None = None,
    limit: int = 0,
) -> list[dict[str, Any]]:
    """Load all scan history entries, sorted by timestamp (oldest first).

    Args:
        history_dir: Directory with history files. Defaults to .mcpaudit-history/.
        limit: Max entries to return (0 = all).

    Returns:
        List of history entries (dicts).
    """
    history_dir = history_dir or Path(DEFAULT_HISTORY_DIR)
    if not history_dir.exists():
        return []

    entries: list[dict[str, Any]] = []
    for f in sorted(history_dir.glob("scan-*.json")):
        try:
            data = json.loads(f.read_text(encoding="utf-8"))
            entries.append(data)
        except (json.JSONDecodeError, KeyError):
            continue

    if limit > 0:
        entries = entries[-limit:]
    return entries


def compute_trends(entries: list[dict[str, Any]]) -> dict[str, Any]:
    """Compute trend analysis from history entries.

    Args:
        entries: List of history entries from load_history().

    Returns:
        Trend analysis dict with grades, findings, and deltas.
    """
    if not entries:
        return {"entries": 0, "trend": "no_data"}

    points: list[dict[str, Any]] = []
    for entry in entries:
        scan = entry.get("scan", {})
        summary = scan.get("summary", {})
        points.append({
            "timestamp": entry.get("timestamp", ""),
            "label": entry.get("label", ""),
            "grade": summary.get("overall_grade", "?"),
            "findings": summary.get("total_findings", 0),
            "files": summary.get("files_scanned", 0),
            "critical": summary.get("severity_counts", {}).get("CRITICAL", 0),
            "high": summary.get("severity_counts", {}).get("HIGH", 0),
            "medium": summary.get("severity_counts", {}).get("MEDIUM", 0),
            "low": summary.get("severity_counts", {}).get("LOW", 0),
        })

    latest = points[-1]
    previous = points[-2] if len(points) > 1 else None

    grade_order = {"A+": 0, "A": 1, "B": 2, "C": 3, "D": 4, "F": 5}
    if previous:
        lat_g = grade_order.get(latest["grade"], 5)
        prev_g = grade_order.get(previous["grade"], 5)
        if lat_g < prev_g:
            trend = "improving"
        elif lat_g > prev_g:
            trend = "degrading"
        else:
            trend = "stable"
        findings_delta = latest["findings"] - previous["findings"]
    else:
        trend = "baseline"
        findings_delta = 0

    return {
        "entries": len(points),
        "trend": trend,
        "latest": latest,
        "previous": previous,
        "findings_delta": findings_delta,
        "points": points,
    }


def format_history_text(entries: list[dict[str, Any]]) -> str:
    """Format history as human-readable terminal output.

    Args:
        entries: List of history entries.

    Returns:
        Formatted text string.
    """
    if not entries:
        return "No scan history found. Run mcpaudit with --save-history to start tracking."

    trends = compute_trends(entries)
    lines: list[str] = []
    lines.append(f"\n{'=' * 60}")
    lines.append("  MCP Security Audit — History")
    lines.append(f"{'=' * 60}")
    lines.append(f"  Total scans: {trends['entries']}")
    lines.append(f"  Trend: {trends['trend'].upper()}")

    if trends.get("latest"):
        lat = trends["latest"]
        lines.append(f"  Latest grade: {lat['grade']}")
        lines.append(f"  Latest findings: {lat['findings']}")
        if trends["findings_delta"] != 0:
            sign = "+" if trends["findings_delta"] > 0 else ""
            lines.append(f"  Delta: {sign}{trends['findings_delta']} findings")
    lines.append(f"{'=' * 60}\n")

    lines.append(f"  {'Timestamp':<22} {'Grade':<8} {'Findings':<10} {'Crit':<6} {'High':<6} {'Label'}")
    lines.append(f"  {'─' * 22} {'─' * 8} {'─' * 10} {'─' * 6} {'─' * 6} {'─' * 20}")

    for point in trends.get("points", []):
        lines.append(
            f"  {point['timestamp']:<22} "
            f"{point['grade']:<8} "
            f"{point['findings']:<10} "
            f"{point['critical']:<6} "
            f"{point['high']:<6} "
            f"{point.get('label', '')}"
        )

    lines.append("")
    return "\n".join(lines)


def generate_dashboard_html(entries: list[dict[str, Any]]) -> str:
    """Generate an HTML dashboard with scan history and trend charts.

    Args:
        entries: List of history entries.

    Returns:
        Complete HTML document string.
    """
    trends = compute_trends(entries)
    points = trends.get("points", [])

    # Build data for chart
    timestamps_js = json.dumps([p["timestamp"][:10] for p in points])
    findings_js = json.dumps([p["findings"] for p in points])
    critical_js = json.dumps([p["critical"] for p in points])
    high_js = json.dumps([p["high"] for p in points])
    medium_js = json.dumps([p["medium"] for p in points])
    grades_js = json.dumps([p["grade"] for p in points])

    latest = trends.get("latest", {})
    trend_label = trends.get("trend", "no_data").upper()
    trend_color = {
        "IMPROVING": "#22c55e",
        "STABLE": "#3b82f6",
        "DEGRADING": "#ef4444",
        "BASELINE": "#8b5cf6",
    }.get(trend_label, "#6b7280")

    grade_color = {
        "A+": "#22c55e", "A": "#22c55e", "B": "#eab308",
        "C": "#f97316", "D": "#ef4444", "F": "#dc2626",
    }.get(latest.get("grade", "?"), "#6b7280")

    # Build rows for the table
    table_rows = ""
    for p in reversed(points):
        gc = {
            "A+": "#22c55e", "A": "#22c55e", "B": "#eab308",
            "C": "#f97316", "D": "#ef4444", "F": "#dc2626",
        }.get(p["grade"], "#6b7280")
        table_rows += f"""<tr>
            <td>{p['timestamp']}</td>
            <td style="color:{gc};font-weight:bold">{p['grade']}</td>
            <td>{p['findings']}</td>
            <td style="color:#ef4444">{p['critical']}</td>
            <td style="color:#f97316">{p['high']}</td>
            <td style="color:#eab308">{p['medium']}</td>
            <td style="color:#3b82f6">{p['low']}</td>
            <td>{p['files']}</td>
            <td>{p.get('label','')}</td>
        </tr>"""

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mcpaudit Dashboard</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
       background: #0f172a; color: #e2e8f0; padding: 24px; }}
h1 {{ font-size: 1.5rem; margin-bottom: 8px; }}
.subtitle {{ color: #94a3b8; margin-bottom: 24px; }}
.cards {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
          gap: 16px; margin-bottom: 32px; }}
.card {{ background: #1e293b; border-radius: 12px; padding: 20px; }}
.card-label {{ font-size: 0.75rem; text-transform: uppercase; color: #94a3b8;
              letter-spacing: 0.05em; }}
.card-value {{ font-size: 2rem; font-weight: 700; margin-top: 4px; }}
.chart-container {{ background: #1e293b; border-radius: 12px; padding: 20px;
                    margin-bottom: 32px; }}
canvas {{ width: 100% !important; height: 300px !important; }}
table {{ width: 100%; border-collapse: collapse; background: #1e293b;
         border-radius: 12px; overflow: hidden; }}
th {{ text-align: left; padding: 12px 16px; background: #334155;
      font-size: 0.8rem; text-transform: uppercase; color: #94a3b8; }}
td {{ padding: 10px 16px; border-bottom: 1px solid #334155; font-size: 0.9rem; }}
tr:hover {{ background: #1e3a5f; }}
.footer {{ text-align: center; color: #475569; margin-top: 32px; font-size: 0.8rem; }}
</style>
</head>
<body>
<h1>mcpaudit Security Dashboard</h1>
<p class="subtitle">Historical scan results and trend analysis</p>

<div class="cards">
    <div class="card">
        <div class="card-label">Current Grade</div>
        <div class="card-value" style="color:{grade_color}">{latest.get('grade','?')}</div>
    </div>
    <div class="card">
        <div class="card-label">Total Scans</div>
        <div class="card-value">{trends.get('entries', 0)}</div>
    </div>
    <div class="card">
        <div class="card-label">Trend</div>
        <div class="card-value" style="color:{trend_color}">{trend_label}</div>
    </div>
    <div class="card">
        <div class="card-label">Latest Findings</div>
        <div class="card-value">{latest.get('findings', 0)}</div>
    </div>
    <div class="card">
        <div class="card-label">Critical</div>
        <div class="card-value" style="color:#ef4444">{latest.get('critical', 0)}</div>
    </div>
    <div class="card">
        <div class="card-label">Delta</div>
        <div class="card-value">{"+" if trends.get('findings_delta',0)>0 else ""}{trends.get('findings_delta',0)}</div>
    </div>
</div>

<div class="chart-container">
    <h3 style="margin-bottom:16px">Findings Over Time</h3>
    <canvas id="findingsChart"></canvas>
</div>

<h3 style="margin-bottom:12px">Scan History</h3>
<table>
<thead><tr>
    <th>Timestamp</th><th>Grade</th><th>Findings</th>
    <th>Critical</th><th>High</th><th>Medium</th><th>Low</th>
    <th>Files</th><th>Label</th>
</tr></thead>
<tbody>{table_rows}</tbody>
</table>

<p class="footer">Generated by mcpaudit v0.5.0 | Carlos Miret</p>

<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<script>
const ctx = document.getElementById('findingsChart').getContext('2d');
new Chart(ctx, {{
    type: 'line',
    data: {{
        labels: {timestamps_js},
        datasets: [
            {{ label: 'Total', data: {findings_js}, borderColor: '#3b82f6',
               backgroundColor: 'rgba(59,130,246,0.1)', fill: true, tension: 0.3 }},
            {{ label: 'Critical', data: {critical_js}, borderColor: '#ef4444',
               borderDash: [5,5], tension: 0.3 }},
            {{ label: 'High', data: {high_js}, borderColor: '#f97316',
               borderDash: [5,5], tension: 0.3 }},
            {{ label: 'Medium', data: {medium_js}, borderColor: '#eab308',
               borderDash: [5,5], tension: 0.3 }}
        ]
    }},
    options: {{
        responsive: true,
        plugins: {{ legend: {{ labels: {{ color: '#94a3b8' }} }} }},
        scales: {{
            x: {{ ticks: {{ color: '#94a3b8' }}, grid: {{ color: '#334155' }} }},
            y: {{ ticks: {{ color: '#94a3b8' }}, grid: {{ color: '#334155' }},
                  beginAtZero: true }}
        }}
    }}
}});
</script>
</body>
</html>"""
