"""Baseline management for suppressing known findings.

A baseline is a JSON file containing fingerprints of accepted findings.
When scanning with a baseline, matching findings are excluded from results,
allowing CI/CD pipelines to only fail on NEW security issues.

Usage:
    mcpaudit path/ --create-baseline .mcpaudit-baseline.json
    mcpaudit path/ --baseline .mcpaudit-baseline.json
"""

from __future__ import annotations

import hashlib
import json
from pathlib import Path

from .models import Finding, ScanSummary


def _fingerprint(finding: Finding) -> str:
    """Generate a stable fingerprint for a finding.

    Uses rule_id + basename of file + trimmed code snippet.
    This survives line number changes as long as the code doesn't change.
    """
    parts = [
        finding.rule_id,
        Path(finding.file).name if finding.file else "",
        finding.code_snippet.strip(),
    ]
    raw = "|".join(parts)
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16]


def create_baseline(summary: ScanSummary) -> str:
    """Create a baseline JSON string from scan results.

    Returns:
        JSON string containing all finding fingerprints.
    """
    entries: list[dict[str, str]] = []
    for result in summary.results:
        for finding in result.findings:
            entries.append({
                "fingerprint": _fingerprint(finding),
                "rule_id": finding.rule_id,
                "file": Path(finding.file).name if finding.file else "",
                "title": finding.title,
            })
    data = {
        "version": 1,
        "count": len(entries),
        "findings": entries,
    }
    return json.dumps(data, indent=2, ensure_ascii=False)


def load_baseline(path: Path) -> set[str]:
    """Load baseline fingerprints from a JSON file.

    Returns:
        Set of fingerprint strings.
    """
    text = path.read_text(encoding="utf-8")
    data = json.loads(text)
    return {e["fingerprint"] for e in data.get("findings", [])}


def apply_baseline(summary: ScanSummary, fingerprints: set[str]) -> int:
    """Remove baselined findings from scan results.

    Returns:
        Number of findings suppressed.
    """
    suppressed = 0
    for result in summary.results:
        original = result.findings[:]
        result.findings = [
            f for f in result.findings
            if _fingerprint(f) not in fingerprints
        ]
        suppressed += len(original) - len(result.findings)
    return suppressed
