"""
PQC fix strategy classification and generation.

Implements a tiered detection system for quantum-vulnerable algorithms
and determines whether findings require PQC migration guidance or
direct code patches.
"""

from enum import Enum
from typing import Any, Dict, List, Optional


class FixStrategy(Enum):
    """Strategy for fixing a security finding."""
    PQC_GUIDANCE = "pqc_guidance"       # Quantum-vulnerable: needs PQC migration guidance
    DIRECT_PATCH = "direct_patch"       # Standard vuln: can be patched directly


# Algorithms known to be quantum-vulnerable
QUANTUM_VULNERABLE_ALGORITHMS = {
    "rsa", "ecc", "ecdh", "ecdsa", "dh", "dsa",
    "md5", "sha1", "sha-1", "des", "3des", "triple-des",
    "rc4", "blowfish", "rsa-sig",
}

# CWEs related to broken/weak cryptography
CRYPTO_CWES = {
    "CWE-327",  # Use of a Broken or Risky Cryptographic Algorithm
    "CWE-328",  # Use of Weak Hash
    "CWE-326",  # Inadequate Encryption Strength
    "CWE-261",  # Weak Encoding for Password
}

# Vulnerability classes indicating crypto issues
CRYPTO_VULN_CLASSES = {
    "weak-crypto", "broken-crypto", "insecure-hash",
    "weak-cipher", "insecure-crypto",
}

# Pattern fragments in check_ids that indicate quantum-vulnerable crypto
CRYPTO_CHECK_PATTERNS = [
    "rsa", "ecdsa", "ecdh", "dsa", "md5", "sha1", "sha-1",
    "des", "3des", "rc4", "blowfish", "weak-hash", "weak-cipher",
    "insecure-hash", "broken-crypto", "weak-crypto",
]

# PQC algorithm replacement mapping
PQC_ALGORITHM_MAPPING: Dict[str, Dict[str, str]] = {
    "rsa": {
        "replacement": "ML-KEM (CRYSTALS-Kyber)",
        "severity": "CRITICAL",
        "rationale": "RSA is vulnerable to Shor's algorithm on quantum computers",
        "reference": "NIST FIPS 203",
    },
    "ecc": {
        "replacement": "ML-KEM (CRYSTALS-Kyber)",
        "severity": "CRITICAL",
        "rationale": "ECC is vulnerable to Shor's algorithm on quantum computers",
        "reference": "NIST FIPS 203",
    },
    "ecdh": {
        "replacement": "ML-KEM (CRYSTALS-Kyber)",
        "severity": "CRITICAL",
        "rationale": "ECDH key exchange is vulnerable to quantum attacks",
        "reference": "NIST FIPS 203",
    },
    "ecdsa": {
        "replacement": "ML-DSA (CRYSTALS-Dilithium)",
        "severity": "CRITICAL",
        "rationale": "ECDSA signatures are vulnerable to Shor's algorithm",
        "reference": "NIST FIPS 204",
    },
    "dh": {
        "replacement": "ML-KEM (CRYSTALS-Kyber)",
        "severity": "CRITICAL",
        "rationale": "Diffie-Hellman is vulnerable to Shor's algorithm",
        "reference": "NIST FIPS 203",
    },
    "dsa": {
        "replacement": "ML-DSA (CRYSTALS-Dilithium)",
        "severity": "CRITICAL",
        "rationale": "DSA is vulnerable to Shor's algorithm on quantum computers",
        "reference": "NIST FIPS 204",
    },
    "rsa-sig": {
        "replacement": "ML-DSA (CRYSTALS-Dilithium)",
        "severity": "CRITICAL",
        "rationale": "RSA signatures are vulnerable to Shor's algorithm",
        "reference": "NIST FIPS 204",
    },
    "md5": {
        "replacement": "SHA-3 (Keccak) or SHAKE256",
        "severity": "HIGH",
        "rationale": "MD5 is cryptographically broken and quantum-vulnerable via Grover's algorithm",
        "reference": "NIST SP 800-131A",
    },
    "sha1": {
        "replacement": "SHA-3 (Keccak) or SHAKE256",
        "severity": "HIGH",
        "rationale": "SHA-1 is cryptographically broken and quantum-vulnerable via Grover's algorithm",
        "reference": "NIST SP 800-131A",
    },
    "sha-1": {
        "replacement": "SHA-3 (Keccak) or SHAKE256",
        "severity": "HIGH",
        "rationale": "SHA-1 is cryptographically broken and quantum-vulnerable via Grover's algorithm",
        "reference": "NIST SP 800-131A",
    },
    "des": {
        "replacement": "AES-256 or ChaCha20-Poly1305",
        "severity": "HIGH",
        "rationale": "DES has insufficient key size (56-bit) and is quantum-vulnerable",
        "reference": "NIST SP 800-131A",
    },
    "3des": {
        "replacement": "AES-256 or ChaCha20-Poly1305",
        "severity": "MEDIUM",
        "rationale": "3DES is deprecated and has limited quantum resistance",
        "reference": "NIST SP 800-131A",
    },
    "rc4": {
        "replacement": "AES-256 or ChaCha20-Poly1305",
        "severity": "HIGH",
        "rationale": "RC4 has known biases and is quantum-vulnerable",
        "reference": "NIST SP 800-131A",
    },
    "blowfish": {
        "replacement": "AES-256 or ChaCha20-Poly1305",
        "severity": "MEDIUM",
        "rationale": "Blowfish has a 64-bit block size, vulnerable to birthday attacks",
        "reference": "NIST SP 800-131A",
    },
}


def determine_fix_strategy(finding: Dict[str, Any]) -> FixStrategy:
    """
    Determine whether a finding requires PQC migration guidance or a direct patch.

    Uses a tiered detection approach:
    1. Check metadata technology field for known quantum-vulnerable algorithms
    2. Check vulnerability_class for crypto-related classes
    3. Check CWE codes for crypto-related CWEs
    4. Pattern-match the check_id for crypto keywords

    Returns FixStrategy.PQC_GUIDANCE if quantum-vulnerable, else DIRECT_PATCH.
    """
    extra = finding.get("extra", {})
    metadata = extra.get("metadata", {})
    check_id = finding.get("check_id", "").lower()

    # Tier 1: Technology metadata
    technologies = metadata.get("technology", [])
    for tech in technologies:
        if tech.lower() in QUANTUM_VULNERABLE_ALGORITHMS:
            return FixStrategy.PQC_GUIDANCE

    # Tier 2: Vulnerability class
    vuln_classes = metadata.get("vulnerability_class", [])
    for vc in vuln_classes:
        if vc.lower() in CRYPTO_VULN_CLASSES:
            return FixStrategy.PQC_GUIDANCE

    # Tier 3: CWE codes
    cwes = metadata.get("cwe", [])
    for cwe in cwes:
        if cwe in CRYPTO_CWES:
            return FixStrategy.PQC_GUIDANCE

    # Tier 4: Pattern matching on check_id
    for pattern in CRYPTO_CHECK_PATTERNS:
        if pattern in check_id:
            return FixStrategy.PQC_GUIDANCE

    return FixStrategy.DIRECT_PATCH


def get_pqc_mapping_for_finding(finding: Dict[str, Any]) -> Dict[str, Dict[str, str]]:
    """
    Extract PQC algorithm replacement mappings for a finding.

    Scans the finding's metadata, message, and check_id for known
    quantum-vulnerable algorithm names, then returns the relevant
    PQC migration mapping entries.
    """
    result: Dict[str, Dict[str, str]] = {}

    extra = finding.get("extra", {})
    metadata = extra.get("metadata", {})
    message = extra.get("message", "").lower()
    check_id = finding.get("check_id", "").lower()

    # Collect all text to search
    search_text = f"{check_id} {message}"

    # Check technology metadata
    technologies = metadata.get("technology", [])
    for tech in technologies:
        tech_lower = tech.lower()
        if tech_lower in PQC_ALGORITHM_MAPPING:
            result[tech_lower] = PQC_ALGORITHM_MAPPING[tech_lower]

    # Check message and check_id for algorithm names
    for algo in PQC_ALGORITHM_MAPPING:
        if algo in search_text and algo not in result:
            result[algo] = PQC_ALGORITHM_MAPPING[algo]

    return result


def get_all_quantum_algorithms() -> List[str]:
    """Return a list of all known quantum-vulnerable algorithm identifiers."""
    return list(PQC_ALGORITHM_MAPPING.keys())
