from __future__ import annotations

from typing import Any

from dsc.scanner.models import DetectorMetadata, Finding, Severity
from dsc.vulnerabilities.catalog import get_vulnerability


_HYBRID_IDS = frozenset({
    "CWE-20", "CWE-78", "CWE-79", "CWE-89", "CWE-306", "CWE-502",
})

_SPECIALIZED_IDS = frozenset({
    "CWE-77", "CWE-90", "CWE-208", "CWE-209", "CWE-269", "CWE-327",
    "CWE-521", "CWE-532", "CWE-639", "CWE-669", "CWE-755", "CWE-787",
    "CWE-798", "CWE-918", "CWE-943", "CWE-1104", "CWE-1321", "CWE-1336",
    "CWE-1395", "DOCKERFILE-SECURITY", "IAC-MISCONFIG",
})


def _classify(rule_id: str) -> tuple[str, str, float]:
    """Return (family, precision_tier, default_confidence) for a rule.

    This provides offline fallback classification. When Firestore data includes
    precision_tier and default_confidence fields, those take precedence (see
    catalog.py Firestore loading). These fallback values match the tier
    assignments in the scanner quality layer spec.
    """
    if rule_id in _HYBRID_IDS:
        return ("hybrid", "A", 0.95)
    if rule_id in _SPECIALIZED_IDS:
        return ("specialized", "A", 0.95)
    if rule_id == "SECLLM-BRIDGE":
        return ("bridge", "C", 0.50)
    return ("unknown", "B", 0.75)


def metadata_from_catalog(rule_id: str) -> DetectorMetadata:
    rule = get_vulnerability(rule_id)
    family, tier, confidence = _classify(rule.id)
    return DetectorMetadata(
        id=rule.id,
        name=rule.name,
        cwe=rule.cwe,
        description=rule.description,
        severity_default=rule.severity_default,
        tags=list(rule.tags),
        languages=list(rule.languages),
        vulnerable_example=rule.vulnerable_example,
        secure_example=rule.secure_example,
        fix_suggestion=rule.fix_suggestion,
        references=list(rule.references),
        family=family,
        precision_tier=tier,
        default_confidence=confidence,
    )


def finding_from_catalog(
    rule_id: str,
    *,
    file_path: str,
    line_start: int,
    line_end: int | None = None,
    column: int = 1,
    message: str,
    snippet: str | None = None,
    metadata: dict[str, Any] | None = None,
    severity: Severity | None = None,
    fix_suggestion: str | None = None,
) -> Finding:
    rule = get_vulnerability(rule_id)
    return Finding(
        rule_id=rule.id,
        cwe=rule.cwe,
        severity=severity or rule.severity_default,
        file_path=file_path,
        line_start=line_start,
        line_end=line_start if line_end is None else line_end,
        column=column,
        message=message,
        fix_suggestion=rule.fix_suggestion if fix_suggestion is None else fix_suggestion,
        snippet=snippet,
        metadata=dict(metadata or {}),
    )
