//! Haskell-specific quantum vulnerability detection

use std::path::Path;
use regex::Regex;
use uuid::Uuid;

use crate::types::{Location, Severity, Vulnerability, VulnerabilityType};

pub struct HaskellAnalyzer {
    patterns: Vec<HaskellPattern>,
}

struct HaskellPattern {
    regex: Regex,
    vuln_type: VulnerabilityType,
    severity: Severity,
    description: &'static str,
    recommendation: &'static str,
    cwe_id: Option<&'static str>,
}

impl HaskellAnalyzer {
    pub fn new() -> Self {
        Self { patterns: Self::compile_patterns() }
    }

    fn compile_patterns() -> Vec<HaskellPattern> {
        let pattern_defs: Vec<(&str, VulnerabilityType, Severity, &'static str, &'static str, Option<&'static str>)> = vec![
            // RSA patterns (cryptonite, HsOpenSSL)
            (r"(?i)Crypto\.PubKey\.RSA|RSA\.generate|RSA\.sign|RSA\.verify", VulnerabilityType::Rsa2048, Severity::Critical, "Haskell RSA cryptography detected", "Migrate to post-quantum alternatives", Some("CWE-327")),
            (r"(?i)generateRSAKey|rsaKeyPair|RSAKeyPair", VulnerabilityType::Rsa2048, Severity::Critical, "Haskell RSA key generation", "Use PQC key exchange", Some("CWE-327")),
            (r"(?i)OpenSSL\.RSA|rsaSign|rsaVerify|rsaEncrypt|rsaDecrypt", VulnerabilityType::Rsa2048, Severity::Critical, "Haskell OpenSSL RSA", "Migrate to PQC", Some("CWE-327")),
            
            // ECDSA/ECDH patterns
            (r"(?i)Crypto\.PubKey\.ECC|Crypto\.PubKey\.ECDSA|ECDSA\.sign|ECDSA\.verify", VulnerabilityType::Ecdsa, Severity::Critical, "Haskell ECDSA detected", "Use Dilithium", Some("CWE-327")),
            (r"(?i)Crypto\.ECC|ECC\.generate|generateECDSAKey", VulnerabilityType::Ecdsa, Severity::Critical, "Haskell ECC detected", "Migrate to PQC", Some("CWE-327")),
            (r"(?i)Curve_P256|Curve_P384|Curve_P521|SEC_p256k1|SEC_p384r1|SEC_p521r1", VulnerabilityType::Ecdsa, Severity::Critical, "Haskell EC curve detected", "All classical curves are quantum-vulnerable", Some("CWE-327")),
            (r"(?i)ecdh|ecdhShared|getShared", VulnerabilityType::Ecdh, Severity::Critical, "Haskell ECDH detected", "Use Kyber KEM", Some("CWE-327")),
            
            // Edwards curves
            (r"(?i)Curve25519|Ed25519|X25519|Ed448|X448", VulnerabilityType::Ecdsa, Severity::Critical, "Haskell Edwards curve detected", "Quantum-vulnerable", Some("CWE-327")),
            
            // DSA patterns
            (r"(?i)Crypto\.PubKey\.DSA|DSA\.generate|DSA\.sign|DSA\.verify", VulnerabilityType::Dsa, Severity::Critical, "Haskell DSA detected", "Use Dilithium", Some("CWE-327")),
            (r"(?i)generateDSAKey|dsaSign|dsaVerify", VulnerabilityType::Dsa, Severity::Critical, "Haskell DSA operations", "Migrate to PQC", Some("CWE-327")),
            
            // DH patterns
            (r"(?i)Crypto\.PubKey\.DH|DH\.generateParams|DH\.generatePrivate", VulnerabilityType::DhKeyExchange, Severity::Critical, "Haskell Diffie-Hellman detected", "Use Kyber KEM", Some("CWE-327")),
            (r"(?i)dhKeyPair|dhShared|generateDHParams", VulnerabilityType::DhKeyExchange, Severity::Critical, "Haskell DH operations", "Migrate to PQC", Some("CWE-327")),
            
            // Weak hashes
            (r"(?i)Crypto\.Hash\.MD5|MD5\.hash|hashWith\s+MD5|md5\s+", VulnerabilityType::Md5, Severity::High, "Haskell MD5 detected", "Use SHA256", Some("CWE-328")),
            (r"(?i)Crypto\.Hash\.SHA1|SHA1\.hash|hashWith\s+SHA1|sha1\s+", VulnerabilityType::Sha1, Severity::High, "Haskell SHA-1 detected", "Use SHA256", Some("CWE-328")),
            (r"(?i)Data\.Digest\.Pure\.MD5|pureMD5", VulnerabilityType::Md5, Severity::High, "Haskell pure MD5 detected", "Use SHA256", Some("CWE-328")),
            (r"(?i)hmacMD5|HMAC\s+MD5", VulnerabilityType::Md5, Severity::High, "Haskell HMAC-MD5", "Use HMAC-SHA256", Some("CWE-328")),
            (r"(?i)hmacSHA1|HMAC\s+SHA1", VulnerabilityType::Sha1, Severity::High, "Haskell HMAC-SHA1", "Use HMAC-SHA256", Some("CWE-328")),
            
            // Weak ciphers
            (r"(?i)Crypto\.Cipher\.DES|DES\.encrypt|DES\.decrypt|des_cbc", VulnerabilityType::Des, Severity::Critical, "Haskell DES detected", "Use AES-GCM", Some("CWE-327")),
            (r"(?i)TripleDES|DES_EDE|des3", VulnerabilityType::TripleDes, Severity::High, "Haskell 3DES detected", "Use AES-GCM", Some("CWE-327")),
            (r"(?i)Crypto\.Cipher\.RC4|RC4\.encrypt|rc4", VulnerabilityType::Rc4, Severity::Critical, "Haskell RC4 detected", "Use AES-GCM", Some("CWE-327")),
            (r"(?i)Crypto\.Cipher\.Blowfish|blowfish", VulnerabilityType::Blowfish, Severity::Medium, "Haskell Blowfish detected", "Use AES-GCM", Some("CWE-327")),
            
            // Weak random
            (r"(?i)System\.Random|mkStdGen|randomR\s*\(|random\s*::", VulnerabilityType::WeakRandom, Severity::High, "Haskell System.Random detected", "Use Crypto.Random", Some("CWE-338")),
            (r"(?i)getStdGen|newStdGen|setStdGen", VulnerabilityType::WeakRandom, Severity::High, "Haskell StdGen detected", "Use Crypto.Random.getRandomBytes", Some("CWE-338")),
            
            // Insecure TLS
            (r"(?i)SSL3|TLS10|TLS11|sslVersion2|sslVersion3", VulnerabilityType::InsecureTls, Severity::High, "Haskell insecure TLS version", "Use TLS 1.2+", Some("CWE-326")),
            
            // Import patterns
            (r"(?i)import\s+Crypto\.PubKey\.RSA", VulnerabilityType::Rsa2048, Severity::Medium, "Haskell RSA import", "Plan PQC migration", Some("CWE-327")),
            (r"(?i)import\s+Crypto\.PubKey\.ECC|import\s+Crypto\.PubKey\.ECDSA", VulnerabilityType::Ecdsa, Severity::Medium, "Haskell EC import", "Plan PQC migration", Some("CWE-327")),
            (r"(?i)import\s+Crypto\.PubKey\.DSA", VulnerabilityType::Dsa, Severity::Medium, "Haskell DSA import", "Plan PQC migration", Some("CWE-327")),
            (r"(?i)import\s+Crypto\.Hash\.MD5", VulnerabilityType::Md5, Severity::Medium, "Haskell MD5 import", "Use SHA256", Some("CWE-328")),
            (r"(?i)import\s+Crypto\.Hash\.SHA1", VulnerabilityType::Sha1, Severity::Medium, "Haskell SHA1 import", "Use SHA256", Some("CWE-328")),
        ];

        pattern_defs.into_iter().filter_map(|(pattern, vuln_type, severity, desc, rec, cwe)| {
            Regex::new(pattern).ok().map(|regex| HaskellPattern { regex, vuln_type, severity, description: desc, recommendation: rec, cwe_id: cwe })
        }).collect()
    }

    pub fn analyze(&self, source: &str, file_path: &Path) -> Vec<Vulnerability> {
        let mut vulns = Vec::new();
        for pattern in &self.patterns {
            for mat in pattern.regex.find_iter(source) {
                if self.is_in_comment(source, mat.start()) { continue; }
                let (line, column) = self.offset_to_position(source, mat.start());
                vulns.push(Vulnerability {
                    id: Uuid::new_v4(), vuln_type: pattern.vuln_type.clone(), severity: pattern.severity,
                    location: Location { file: file_path.to_path_buf(), line, column, end_line: None, end_column: None },
                    description: pattern.description.to_string(), code_snippet: self.extract_snippet(source, line),
                    recommendation: pattern.recommendation.to_string(), migration_impacts: vec![],
                    cwe_id: pattern.cwe_id.map(|s| s.to_string()), is_dependency: false, dependency_chain: None, finding_type: crate::types::FindingType::DirectUsage, library_name: None,
                });
            }
        }
        vulns
    }

    fn is_in_comment(&self, source: &str, offset: usize) -> bool {
        let before = &source[..offset];
        // Haskell single-line comment: --
        if let Some(line_start) = before.rfind('\n') {
            if before[line_start..].contains("--") { return true; }
        }
        // Haskell block comment: {- -}
        let last_open = before.rfind("{-");
        let last_close = before.rfind("-}");
        matches!((last_open, last_close), (Some(o), Some(c)) if o > c) || matches!((last_open, last_close), (Some(_), None))
    }

    fn offset_to_position(&self, source: &str, offset: usize) -> (usize, usize) {
        let (mut line, mut col) = (1, 1);
        for (i, c) in source.char_indices() {
            if i >= offset { break; }
            if c == '\n' { line += 1; col = 1; } else { col += 1; }
        }
        (line, col)
    }

    fn extract_snippet(&self, source: &str, line: usize) -> Option<String> {
        let lines: Vec<&str> = source.lines().collect();
        if line == 0 || line > lines.len() { return None; }
        let start = line.saturating_sub(2);
        let end = (line + 1).min(lines.len());
        Some(lines[start..end].iter().enumerate().map(|(i, l)| format!("{:4} | {}", start + i + 1, l)).collect::<Vec<_>>().join("\n"))
    }
}
