"""Tests for baseline management."""

import json
import tempfile
from pathlib import Path

from mcpaudit.baseline import (
    _fingerprint,
    apply_baseline,
    create_baseline,
    load_baseline,
)
from mcpaudit.models import Finding, ScanResult, ScanSummary, Severity


def _make_finding(rule_id: str = "CMD-001", line: int = 10,
                  code: str = "subprocess.run(cmd, shell=True)",
                  file: str = "server.py") -> Finding:
    return Finding(
        rule_id=rule_id,
        severity=Severity.CRITICAL,
        title="Test finding",
        description="Test description",
        file=file,
        line=line,
        code_snippet=code,
    )


def _make_summary(findings: list[Finding]) -> ScanSummary:
    result = ScanResult(file="server.py", findings=findings, lines_scanned=100)
    return ScanSummary(results=[result])


class TestFingerprint:
    def test_same_finding_same_fingerprint(self):
        f1 = _make_finding()
        f2 = _make_finding()
        assert _fingerprint(f1) == _fingerprint(f2)

    def test_different_rule_different_fingerprint(self):
        f1 = _make_finding(rule_id="CMD-001")
        f2 = _make_finding(rule_id="SQL-001")
        assert _fingerprint(f1) != _fingerprint(f2)

    def test_different_code_different_fingerprint(self):
        f1 = _make_finding(code="subprocess.run(cmd)")
        f2 = _make_finding(code="subprocess.call(cmd)")
        assert _fingerprint(f1) != _fingerprint(f2)

    def test_line_change_same_fingerprint(self):
        """Line number changes should NOT affect fingerprint."""
        f1 = _make_finding(line=10)
        f2 = _make_finding(line=42)
        assert _fingerprint(f1) == _fingerprint(f2)

    def test_whitespace_stripped(self):
        f1 = _make_finding(code="  subprocess.run(cmd)  ")
        f2 = _make_finding(code="subprocess.run(cmd)")
        assert _fingerprint(f1) == _fingerprint(f2)

    def test_different_file_different_fingerprint(self):
        f1 = _make_finding(file="server.py")
        f2 = _make_finding(file="handler.py")
        assert _fingerprint(f1) != _fingerprint(f2)

    def test_full_path_uses_basename(self):
        """Full paths should be reduced to basename."""
        f1 = _make_finding(file="/home/user/project/server.py")
        f2 = _make_finding(file="/opt/other/server.py")
        assert _fingerprint(f1) == _fingerprint(f2)

    def test_fingerprint_is_hex_string(self):
        fp = _fingerprint(_make_finding())
        assert len(fp) == 16
        assert all(c in "0123456789abcdef" for c in fp)


class TestCreateBaseline:
    def test_empty_summary(self):
        summary = ScanSummary()
        output = create_baseline(summary)
        data = json.loads(output)
        assert data["version"] == 1
        assert data["count"] == 0
        assert data["findings"] == []

    def test_single_finding(self):
        summary = _make_summary([_make_finding()])
        output = create_baseline(summary)
        data = json.loads(output)
        assert data["count"] == 1
        assert len(data["findings"]) == 1
        entry = data["findings"][0]
        assert "fingerprint" in entry
        assert entry["rule_id"] == "CMD-001"

    def test_multiple_findings(self):
        findings = [
            _make_finding(rule_id="CMD-001"),
            _make_finding(rule_id="SQL-001", code="cursor.execute(query)"),
            _make_finding(rule_id="PATH-001", code="open(user_path)"),
        ]
        summary = _make_summary(findings)
        output = create_baseline(summary)
        data = json.loads(output)
        assert data["count"] == 3

    def test_json_is_valid(self):
        summary = _make_summary([_make_finding()])
        output = create_baseline(summary)
        json.loads(output)


class TestLoadBaseline:
    def test_load_empty_baseline(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"version": 1, "count": 0, "findings": []}, f)
            f.flush()
            fingerprints = load_baseline(Path(f.name))
        assert fingerprints == set()

    def test_load_with_fingerprints(self):
        data = {
            "version": 1,
            "count": 2,
            "findings": [
                {"fingerprint": "abc123", "rule_id": "CMD-001", "file": "s.py", "title": "t"},
                {"fingerprint": "def456", "rule_id": "SQL-001", "file": "s.py", "title": "t"},
            ],
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(data, f)
            f.flush()
            fingerprints = load_baseline(Path(f.name))
        assert fingerprints == {"abc123", "def456"}


class TestApplyBaseline:
    def test_no_baseline_no_change(self):
        findings = [_make_finding()]
        summary = _make_summary(findings)
        suppressed = apply_baseline(summary, set())
        assert suppressed == 0
        assert len(summary.results[0].findings) == 1

    def test_suppress_matching_finding(self):
        finding = _make_finding()
        summary = _make_summary([finding])
        fp = _fingerprint(finding)
        suppressed = apply_baseline(summary, {fp})
        assert suppressed == 1
        assert len(summary.results[0].findings) == 0

    def test_keep_non_matching_finding(self):
        findings = [
            _make_finding(rule_id="CMD-001"),
            _make_finding(rule_id="SQL-001", code="cursor.execute(query)"),
        ]
        summary = _make_summary(findings)
        fp = _fingerprint(findings[0])
        suppressed = apply_baseline(summary, {fp})
        assert suppressed == 1
        assert len(summary.results[0].findings) == 1
        assert summary.results[0].findings[0].rule_id == "SQL-001"

    def test_roundtrip_create_and_apply(self):
        """Create baseline from findings, then apply it."""
        findings = [
            _make_finding(rule_id="CMD-001"),
            _make_finding(rule_id="SQL-001", code="query(x)"),
        ]
        summary1 = _make_summary(findings)
        baseline_json = create_baseline(summary1)

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            f.write(baseline_json)
            f.flush()
            fingerprints = load_baseline(Path(f.name))

        summary2 = _make_summary(list(findings))
        suppressed = apply_baseline(summary2, fingerprints)
        assert suppressed == 2
        assert len(summary2.results[0].findings) == 0

    def test_new_findings_not_suppressed(self):
        """Baseline only suppresses old findings, new ones pass through."""
        old_finding = _make_finding(rule_id="CMD-001")
        summary_old = _make_summary([old_finding])
        baseline_json = create_baseline(summary_old)

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            f.write(baseline_json)
            f.flush()
            fingerprints = load_baseline(Path(f.name))

        new_finding = _make_finding(rule_id="SSRF-001", code="requests.get(url)")
        summary_new = _make_summary([old_finding, new_finding])
        suppressed = apply_baseline(summary_new, fingerprints)
        assert suppressed == 1
        assert len(summary_new.results[0].findings) == 1
        assert summary_new.results[0].findings[0].rule_id == "SSRF-001"
