"""Secrets Analyzer — converts raw secret matches into typed Finding objects."""

from __future__ import annotations

from typing import Any

from sentinel.analyzers.base import BaseAnalyzer
from sentinel.models.findings import (
    ComplianceMapping,
    Evidence,
    Finding,
    ScanResult,
    ScanStats,
    Severity,
)

_SEVERITY_MAP = {
    "critical": Severity.CRITICAL,
    "high": Severity.HIGH,
    "medium": Severity.MEDIUM,
    "low": Severity.LOW,
    "info": Severity.INFO,
}


class SecretsAnalyzer(BaseAnalyzer):
    module_name = "secrets"

    def analyze(self, collected: dict[str, Any]) -> ScanResult:
        findings: list[Finding] = []
        seen: set[str] = set()

        all_matches = collected.get("matches", []) + collected.get("history_matches", [])

        for match in all_matches:
            key = f"{match['pattern_id']}:{match['file']}:{match.get('line', 0)}"
            if key in seen:
                continue
            seen.add(key)

            is_history = match.get("type") == "history"
            severity_str = match.get("severity", "medium")
            # Historical exposure is always at least HIGH — assume credential is compromised
            if is_history and severity_str in ("low", "info", "medium"):
                severity_str = "high"
            severity = _SEVERITY_MAP.get(severity_str, Severity.MEDIUM)

            context = match.get("snippet", "")
            if is_history:
                context = (
                    f"Commit {match.get('commit', '?')}: {match.get('commit_message', '')}\n"
                    + context
                )

            finding = Finding(
                id=f"secrets.{match['pattern_id']}.{abs(hash(key)) % 99999:05d}",
                title=(
                    f"{'[Git History] ' if is_history else ''}"
                    f"{match['pattern_name']} detected"
                ),
                description=(
                    f"A {match['pattern_name']} was detected in "
                    + (
                        f"git history (commit {match.get('commit', '?')}). "
                        "Even though this secret was removed from the current commit, it "
                        "remains in git history and is fully recoverable by anyone with "
                        "repository clone access."
                        if is_history
                        else f"{match['file']}."
                    )
                    + f" Redacted value: `{match.get('value_redacted', '****')}`."
                ),
                severity=severity,
                module=self.module_name,
                evidence=[Evidence(
                    file=match["file"],
                    line=match.get("line"),
                    snippet=match.get("value_redacted"),
                    context=context[:500] if context else None,
                )],
                remediation=(
                    "1. Rotate the exposed credential immediately — assume it is compromised.\n"
                    "2. Remove the secret from the codebase.\n"
                    + (
                        "3. Purge from git history: git-filter-repo --path <file> --invert-paths\n"
                        "4. Notify affected systems and audit access logs."
                        if is_history
                        else
                        "3. Store secrets in environment variables or a secrets manager.\n"
                        "4. Add this file pattern to .gitignore."
                    )
                ),
                compliance=ComplianceMapping(
                    soc2=["CC6.1"],
                    cis=["CIS-GitLab-2.3"],
                    owasp_cicd=["CICD-SEC-6"],
                    iso27001=["A.9.4.3"],
                ),
                tags=["secret", "history" if is_history else "filesystem"],
            )
            findings.append(finding)

        stats = ScanStats(
            files_scanned=collected.get("files_scanned", 0),
            rules_evaluated=len(all_matches),
            findings_count=len(findings),
        )
        return self._make_result(findings, stats)
