"""Tests for MCP Security Metrics Dashboard."""

from __future__ import annotations

import json

import pytest

from mcpaudit.metrics_dashboard import (
    DashboardOptions,
    DashboardResult,
    KPI,
    ScanEntry,
    _classify_rag,
    _compute_findings_trend,
    _compute_kpis,
    _compute_rate_trend,
    _compute_trend,
    _rag_badge,
    _sparkline_css,
    _trend_arrow,
    compute_kpis,
    format_dashboard_result_json,
    generate_metrics_dashboard,
)


# ---- ScanEntry ----

class TestScanEntry:
    def test_defaults(self):
        e = ScanEntry(timestamp="2024-01-01T00:00:00Z")
        assert e.findings_total == 0
        assert e.mttr_hours == 0.0
        assert e.compliance_pct == 0.0

    def test_all_fields(self):
        e = ScanEntry(
            timestamp="2024-01-01T00:00:00Z",
            findings_total=10,
            findings_new=3,
            findings_resolved=2,
            critical=1, high=2, medium=3, low=4,
            compliance_pct=85.5,
            mttr_hours=24.0,
        )
        assert e.findings_total == 10
        assert e.critical == 1
        assert e.compliance_pct == 85.5

    def test_to_dict(self):
        e = ScanEntry(timestamp="2024-01-01T00:00:00Z", findings_total=5)
        d = e.to_dict()
        assert d["timestamp"] == "2024-01-01T00:00:00Z"
        assert d["findings_total"] == 5

    def test_from_dict(self):
        e = ScanEntry.from_dict({"timestamp": "2024-01-01", "critical": 3})
        assert e.critical == 3
        assert e.timestamp == "2024-01-01"

    def test_from_dict_defaults(self):
        e = ScanEntry.from_dict({})
        assert e.timestamp == ""
        assert e.findings_total == 0

    def test_roundtrip(self):
        e = ScanEntry(timestamp="2024-01-15T10:00:00Z", findings_total=7, mttr_hours=12.5)
        d = e.to_dict()
        e2 = ScanEntry.from_dict(d)
        assert e2.findings_total == 7
        assert e2.mttr_hours == 12.5


# ---- KPI ----

class TestKPI:
    def test_defaults(self):
        k = KPI(name="test", value=42.0)
        assert k.unit == ""
        assert k.trend == "stable"
        assert k.rag == "green"
        assert k.sparkline == []

    def test_to_dict(self):
        k = KPI(name="metric", value=10.5, unit="%", trend="improving", rag="green")
        d = k.to_dict()
        assert d["name"] == "metric"
        assert d["value"] == 10.5
        assert d["unit"] == "%"

    def test_sparkline_in_dict(self):
        k = KPI(name="m", value=1, sparkline=[1.0, 2.0, 3.0])
        d = k.to_dict()
        assert len(d["sparkline"]) == 3


# ---- DashboardOptions ----

class TestDashboardOptions:
    def test_defaults(self):
        o = DashboardOptions()
        assert o.title == "Security Metrics Dashboard"
        assert o.refresh_interval == 300
        assert o.show_sparklines is True

    def test_custom(self):
        o = DashboardOptions(title="Custom", refresh_interval=60)
        assert o.title == "Custom"
        assert o.refresh_interval == 60

    def test_to_dict(self):
        o = DashboardOptions()
        d = o.to_dict()
        assert "title" in d
        assert "refresh_interval" in d


# ---- RAG Classification ----

class TestRAG:
    def test_green_by_default(self):
        assert _classify_rag("unknown_metric", 50.0) == "green"

    def test_total_findings_green(self):
        assert _classify_rag("total_findings", 10.0) == "green"

    def test_total_findings_amber(self):
        assert _classify_rag("total_findings", 30.0) == "amber"

    def test_total_findings_red(self):
        assert _classify_rag("total_findings", 60.0) == "red"

    def test_compliance_green(self):
        assert _classify_rag("compliance_pct", 90.0) == "green"

    def test_compliance_amber(self):
        assert _classify_rag("compliance_pct", 70.0) == "amber"

    def test_compliance_red(self):
        assert _classify_rag("compliance_pct", 40.0) == "red"

    def test_resolution_rate_green(self):
        assert _classify_rag("resolution_rate", 80.0) == "green"

    def test_resolution_rate_amber(self):
        assert _classify_rag("resolution_rate", 60.0) == "amber"

    def test_resolution_rate_red(self):
        assert _classify_rag("resolution_rate", 40.0) == "red"

    def test_mttr_green(self):
        assert _classify_rag("mttr_hours", 24.0) == "green"

    def test_mttr_amber(self):
        assert _classify_rag("mttr_hours", 72.0) == "amber"

    def test_mttr_red(self):
        assert _classify_rag("mttr_hours", 200.0) == "red"

    def test_custom_thresholds(self):
        custom = {"total_findings": (5.0, 10.0)}
        assert _classify_rag("total_findings", 12.0, custom) == "red"
        assert _classify_rag("total_findings", 7.0, custom) == "amber"
        assert _classify_rag("total_findings", 3.0, custom) == "green"


# ---- Trend Computation ----

class TestTrend:
    def test_stable_single_value(self):
        assert _compute_trend([5.0]) == "stable"

    def test_stable_empty(self):
        assert _compute_trend([]) == "stable"

    def test_increasing(self):
        assert _compute_trend([1.0, 2.0, 3.0, 10.0, 15.0, 20.0]) == "increasing"

    def test_decreasing(self):
        assert _compute_trend([20.0, 15.0, 10.0, 3.0, 2.0, 1.0]) == "decreasing"

    def test_findings_trend_improving(self):
        # Decreasing findings = improving
        assert _compute_findings_trend([20.0, 15.0, 10.0, 3.0, 2.0, 1.0]) == "improving"

    def test_findings_trend_declining(self):
        assert _compute_findings_trend([1.0, 2.0, 3.0, 10.0, 15.0, 20.0]) == "declining"

    def test_rate_trend_improving(self):
        assert _compute_rate_trend([50.0, 60.0, 70.0, 80.0, 90.0]) == "improving"

    def test_rate_trend_declining(self):
        assert _compute_rate_trend([90.0, 80.0, 70.0, 60.0, 50.0]) == "declining"


# ---- Sparkline ----

class TestSparkline:
    def test_empty_values(self):
        html = _sparkline_css([])
        assert "sparkline" in html

    def test_single_value(self):
        html = _sparkline_css([5.0])
        assert "spark-bar" in html

    def test_multiple_values(self):
        html = _sparkline_css([1.0, 5.0, 3.0, 8.0])
        assert html.count("spark-bar") == 4

    def test_all_zeros(self):
        html = _sparkline_css([0.0, 0.0, 0.0])
        assert "spark-bar" in html


# ---- Trend Arrow ----

class TestTrendArrow:
    def test_improving(self):
        html = _trend_arrow("improving")
        assert "trend-up" in html

    def test_declining(self):
        html = _trend_arrow("declining")
        assert "trend-down" in html

    def test_stable(self):
        html = _trend_arrow("stable")
        assert "trend-stable" in html

    def test_unknown(self):
        html = _trend_arrow("unknown")
        assert "trend-stable" in html


# ---- RAG Badge ----

class TestRAGBadge:
    def test_red(self):
        html = _rag_badge("red")
        assert "RED" in html
        assert "#dc2626" in html

    def test_amber(self):
        html = _rag_badge("amber")
        assert "AMBER" in html

    def test_green(self):
        html = _rag_badge("green")
        assert "GREEN" in html


# ---- KPI Computation ----

class TestComputeKPIs:
    def test_empty_scans(self):
        kpis = _compute_kpis([])
        assert kpis == []

    def test_single_scan(self):
        scans = [ScanEntry(timestamp="2024-01-01T00:00:00Z", findings_total=10, findings_new=3, findings_resolved=1, compliance_pct=80.0, mttr_hours=24.0)]
        kpis = _compute_kpis(scans)
        assert len(kpis) == 5
        names = [k.name for k in kpis]
        assert "Total Findings" in names
        assert "MTTR" in names
        assert "Resolution Rate" in names
        assert "New Findings/Week" in names
        assert "Compliance" in names

    def test_total_findings_value(self):
        scans = [ScanEntry(timestamp="2024-01-01", findings_total=42)]
        kpis = _compute_kpis(scans)
        total = [k for k in kpis if k.name == "Total Findings"][0]
        assert total.value == 42.0

    def test_resolution_rate_all_resolved(self):
        scans = [ScanEntry(timestamp="2024-01-01", findings_new=5, findings_resolved=5)]
        kpis = _compute_kpis(scans)
        rate = [k for k in kpis if k.name == "Resolution Rate"][0]
        assert rate.value == 100.0

    def test_resolution_rate_zero_new(self):
        scans = [ScanEntry(timestamp="2024-01-01", findings_new=0, findings_resolved=0)]
        kpis = _compute_kpis(scans)
        rate = [k for k in kpis if k.name == "Resolution Rate"][0]
        assert rate.value == 100.0


# ---- Public API: compute_kpis ----

class TestPublicComputeKPIs:
    def test_with_dicts(self):
        scans = [{"timestamp": "2024-01-01", "findings_total": 5}]
        kpis = compute_kpis(scans)
        assert len(kpis) == 5

    def test_with_scan_entries(self):
        scans = [ScanEntry(timestamp="2024-01-01", findings_total=3)]
        kpis = compute_kpis(scans)
        assert len(kpis) == 5


# ---- generate_metrics_dashboard ----

class TestGenerateMetricsDashboard:
    def test_empty_scans(self):
        html = generate_metrics_dashboard([])
        assert "<!DOCTYPE html>" in html
        assert "Security Metrics Dashboard" in html

    def test_basic_html_structure(self):
        scans = [{"timestamp": "2024-01-01", "findings_total": 10, "critical": 1}]
        html = generate_metrics_dashboard(scans)
        assert "<!DOCTYPE html>" in html
        assert "kpi-card" in html

    def test_custom_title(self):
        html = generate_metrics_dashboard(
            [{"timestamp": "2024-01-01"}],
            {"title": "My Dashboard"},
        )
        assert "My Dashboard" in html

    def test_auto_refresh(self):
        html = generate_metrics_dashboard(
            [{"timestamp": "2024-01-01"}],
            {"refresh_interval": 60},
        )
        assert 'content="60"' in html

    def test_no_auto_refresh(self):
        html = generate_metrics_dashboard(
            [{"timestamp": "2024-01-01"}],
            {"refresh_interval": 0},
        )
        assert "http-equiv" not in html

    def test_with_dashboard_options(self):
        opts = DashboardOptions(title="Test", refresh_interval=120)
        html = generate_metrics_dashboard(
            [ScanEntry(timestamp="2024-01-01", findings_total=5)],
            opts,
        )
        assert "Test" in html

    def test_with_multiple_scans(self):
        scans = [
            {"timestamp": "2024-01-01", "findings_total": 20, "findings_new": 5, "findings_resolved": 3, "compliance_pct": 70.0},
            {"timestamp": "2024-01-02", "findings_total": 18, "findings_new": 2, "findings_resolved": 4, "compliance_pct": 75.0},
            {"timestamp": "2024-01-03", "findings_total": 15, "findings_new": 1, "findings_resolved": 4, "compliance_pct": 80.0},
        ]
        html = generate_metrics_dashboard(scans)
        assert "Total Findings" in html
        assert "MTTR" in html

    def test_severity_breakdown(self):
        scans = [{"timestamp": "2024-01-01", "critical": 2, "high": 5, "medium": 10, "low": 3}]
        html = generate_metrics_dashboard(scans)
        assert "Critical" in html
        assert "High" in html

    def test_sparklines_disabled(self):
        scans = [{"timestamp": "2024-01-01", "findings_total": 10}]
        html = generate_metrics_dashboard(scans, {"show_sparklines": False})
        # Should still work, just no sparkline bars
        assert "<!DOCTYPE html>" in html

    def test_rag_badges_in_html(self):
        scans = [{"timestamp": "2024-01-01", "findings_total": 100}]
        html = generate_metrics_dashboard(scans)
        assert "rag-badge" in html

    def test_scan_history_table(self):
        scans = [
            {"timestamp": "2024-01-01", "findings_total": 10, "findings_new": 3, "findings_resolved": 2, "compliance_pct": 80.0},
        ]
        html = generate_metrics_dashboard(scans)
        assert "Recent Scans" in html

    def test_many_scans_limits_history(self):
        scans = [{"timestamp": f"2024-01-{i+1:02d}", "findings_total": i} for i in range(20)]
        html = generate_metrics_dashboard(scans)
        assert "<!DOCTYPE html>" in html


# ---- format_dashboard_result_json ----

class TestFormatJSON:
    def test_empty_kpis(self):
        result = format_dashboard_result_json([])
        data = json.loads(result)
        assert data["kpis"] == []

    def test_with_kpis(self):
        kpis = [KPI(name="test", value=42.0, unit="%")]
        result = format_dashboard_result_json(kpis)
        data = json.loads(result)
        assert len(data["kpis"]) == 1
        assert data["kpis"][0]["name"] == "test"

    def test_valid_json(self):
        kpis = [KPI(name="a", value=1.0), KPI(name="b", value=2.0)]
        result = format_dashboard_result_json(kpis)
        data = json.loads(result)
        assert len(data["kpis"]) == 2


# ---- DashboardResult ----

class TestDashboardResult:
    def test_auto_timestamp(self):
        r = DashboardResult(html="<html></html>")
        assert r.timestamp != ""

    def test_to_dict(self):
        r = DashboardResult(html="", scan_count=5)
        d = r.to_dict()
        assert d["scan_count"] == 5

    def test_with_kpis(self):
        kpis = [KPI(name="test", value=1.0)]
        r = DashboardResult(html="", kpis=kpis)
        d = r.to_dict()
        assert len(d["kpis"]) == 1
