"""Security scanning types for OWASP vulnerability analysis."""

from enum import Enum

from pydantic import BaseModel, ConfigDict, Field


class Severity(str, Enum):
    """Vulnerability severity levels."""

    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


class Confidence(str, Enum):
    """Finding confidence levels."""

    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"


class FindingCategory(str, Enum):
    """Finding categories — OWASP Top 10, GIGW, and Accessibility."""

    # OWASP Top 10 2021
    A01_BROKEN_ACCESS_CONTROL = "A01"
    A02_CRYPTOGRAPHIC_FAILURES = "A02"
    A03_INJECTION = "A03"
    A04_INSECURE_DESIGN = "A04"
    A05_SECURITY_MISCONFIGURATION = "A05"
    A06_VULNERABLE_COMPONENTS = "A06"
    A07_AUTH_FAILURES = "A07"
    A08_DATA_INTEGRITY_FAILURES = "A08"
    A09_LOGGING_FAILURES = "A09"
    A10_SSRF = "A10"

    # GIGW Compliance
    GIGW_SEC = "GIGW-SEC"
    GIGW_A11Y = "GIGW-A11Y"
    GIGW_QUAL = "GIGW-QUAL"
    GIGW_LIFE = "GIGW-LIFE"
    GIGW_GOV = "GIGW-GOV"

    # Accessibility / WCAG
    WCAG_A = "WCAG-A"
    WCAG_AA = "WCAG-AA"
    WCAG_AAA = "WCAG-AAA"


# Keep backward-compatible alias
OWASPCategory = FindingCategory


# Display names keyed by short code
CATEGORY_DISPLAY_NAMES: dict[str, str] = {
    # OWASP Top 10
    "A01": "Broken Access Control",
    "A02": "Cryptographic Failures",
    "A03": "Injection",
    "A04": "Insecure Design",
    "A05": "Security Misconfiguration",
    "A06": "Vulnerable and Outdated Components",
    "A07": "Identification and Authentication Failures",
    "A08": "Software and Data Integrity Failures",
    "A09": "Security Logging and Monitoring Failures",
    "A10": "Server-Side Request Forgery (SSRF)",
    # GIGW
    "GIGW-SEC": "GIGW Code Security",
    "GIGW-A11Y": "GIGW Accessibility",
    "GIGW-QUAL": "GIGW Quality Standards",
    "GIGW-LIFE": "GIGW Lifecycle Management",
    "GIGW-GOV": "GIGW Government Mandates",
    # Accessibility
    "WCAG-A": "WCAG Level A",
    "WCAG-AA": "WCAG Level AA",
    "WCAG-AAA": "WCAG Level AAA",
}

# Backward-compatible alias
OWASP_DISPLAY_NAMES = CATEGORY_DISPLAY_NAMES


def owasp_verbose_prefix(short_code: str) -> str:
    """Return a verbose prefix like 'OWASP A01:2021 (Broken Access Control)' or 'GIGW-SEC (GIGW Code Security)'."""
    name = CATEGORY_DISPLAY_NAMES.get(short_code.upper(), "")
    if not name:
        return short_code
    code_upper = short_code.upper()
    if code_upper.startswith("A") and code_upper[1:].isdigit():
        return f"OWASP {code_upper}:2021 ({name})"
    elif code_upper.startswith("GIGW"):
        return f"{code_upper} ({name})"
    elif code_upper.startswith("WCAG"):
        return f"{code_upper} ({name})"
    return f"{code_upper} ({name})"


class SecurityFinding(BaseModel):
    """Represents a single security vulnerability finding."""

    model_config = ConfigDict(populate_by_name=True)

    id: str
    rule_id: str = Field(alias="ruleId")
    owasp_category: OWASPCategory | None = Field(default=None, alias="owaspCategory")
    cwe_id: str | None = Field(default=None, alias="cweId")
    severity: Severity
    confidence: Confidence
    title: str
    description: str
    file_path: str = Field(alias="filePath")
    line_start: int = Field(alias="lineStart")
    line_end: int | None = Field(default=None, alias="lineEnd")
    code_snippet: str | None = Field(default=None, alias="codeSnippet")
    recommendation: str | None = None
    reference_links: list[str] = Field(default_factory=list, alias="referenceLinks")


class CheckResult(BaseModel):
    """Result of evaluating a single compliance check (pass/fail/not_applicable)."""

    model_config = ConfigDict(populate_by_name=True)

    check_id: str = Field(alias="checkId")
    status: str  # "pass", "fail", "not_applicable"
    notes: str | None = None


class SeverityCounts(BaseModel):
    """Count of findings by severity."""

    low: int = 0
    medium: int = 0
    high: int = 0
    critical: int = 0


class ScanSummary(BaseModel):
    """Summary statistics for a security scan."""

    model_config = ConfigDict(populate_by_name=True)

    total_findings: int = Field(alias="totalFindings")
    by_severity: SeverityCounts = Field(alias="bySeverity")
    by_owasp_category: dict[str, int] = Field(default_factory=dict, alias="byOwaspCategory")
    files_scanned: int = Field(default=0, alias="filesScanned")


class SecurityScanResult(BaseModel):
    """Complete security scan result."""

    model_config = ConfigDict(populate_by_name=True)

    scan_id: str = Field(alias="scanId")
    target_path: str = Field(alias="targetPath")
    scan_timestamp: str = Field(alias="scanTimestamp")
    summary: ScanSummary
    findings: list[SecurityFinding] = Field(default_factory=list)
    errors: list[str] = Field(default_factory=list)


class StoredSecurityScan(BaseModel):
    """Stored security scan with metadata."""

    model_config = ConfigDict(populate_by_name=True)

    id: str
    target_path: str = Field(alias="targetPath")
    result: SecurityScanResult
    stored_at: str = Field(alias="storedAt")
    last_accessed: str = Field(alias="lastAccessed")
    status: str = "pending"
    selected_checks: list[str] = Field(default_factory=list, alias="selectedChecks")
    scan_profile: str | list[str] | None = Field(default=None, alias="scanProfile")
    file_list: list[str] = Field(default_factory=list, alias="fileList")
    check_results: list[CheckResult] = Field(default_factory=list, alias="checkResults")
    pushed_to_maeris: bool = Field(default=False, alias="pushedToMaeris")
    pushed_at: str | None = Field(default=None, alias="pushedAt")
