//! R-specific quantum vulnerability detection

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

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

pub struct RAnalyzer {
    patterns: Vec<RPattern>,
}

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

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

    fn compile_patterns() -> Vec<RPattern> {
        let pattern_defs: Vec<(&str, VulnerabilityType, Severity, &'static str, &'static str, Option<&'static str>)> = vec![
            // RSA patterns (openssl, sodium packages)
            (r"(?i)rsa_keygen|rsa_encrypt|rsa_decrypt|rsa_sign|rsa_verify", VulnerabilityType::Rsa2048, Severity::Critical, "R RSA operations detected", "Migrate to PQC", Some("CWE-327")),
            (r"(?i)openssl::rsa|rsa_key_pair|generate_rsa", VulnerabilityType::Rsa2048, Severity::Critical, "R OpenSSL RSA", "Use PQC alternatives", Some("CWE-327")),
            (r#"(?i)read_key\s*\([^)]*rsa|write_key\s*\([^)]*rsa"#, VulnerabilityType::Rsa2048, Severity::Critical, "R RSA key I/O", "Migrate to PQC", Some("CWE-327")),
            
            // EC patterns
            (r"(?i)ec_keygen|ec_key_pair|ecdsa_sign|ecdsa_verify", VulnerabilityType::Ecdsa, Severity::Critical, "R ECDSA operations", "Use Dilithium", Some("CWE-327")),
            (r"(?i)ecdh_shared|ec_dh|openssl::ec", VulnerabilityType::Ecdh, Severity::Critical, "R ECDH detected", "Use Kyber", Some("CWE-327")),
            (r#"(?i)secp256k1|secp384r1|secp521r1|prime256v1|P-256|P-384|P-521"#, VulnerabilityType::Ecdsa, Severity::Critical, "R EC curve", "Quantum-vulnerable", Some("CWE-327")),
            (r"(?i)curve25519|ed25519|x25519", VulnerabilityType::Ecdsa, Severity::Critical, "R Edwards curve", "Quantum-vulnerable", Some("CWE-327")),
            
            // DSA patterns
            (r"(?i)dsa_keygen|dsa_sign|dsa_verify|openssl::dsa", VulnerabilityType::Dsa, Severity::Critical, "R DSA operations", "Use Dilithium", Some("CWE-327")),
            
            // DH patterns
            (r"(?i)dh_keygen|dh_compute|dh_shared|openssl::dh", VulnerabilityType::DhKeyExchange, Severity::Critical, "R Diffie-Hellman", "Use Kyber", Some("CWE-327")),
            
            // Weak hashes (digest package)
            (r#"(?i)digest\s*\([^,]*,\s*["']md5["']|md5\s*\(|digest::md5"#, VulnerabilityType::Md5, Severity::High, "R MD5 hash", "Use SHA256", Some("CWE-328")),
            (r#"(?i)digest\s*\([^,]*,\s*["']sha1["']|sha1\s*\(|digest::sha1"#, VulnerabilityType::Sha1, Severity::High, "R SHA-1 hash", "Use SHA256", Some("CWE-328")),
            (r#"(?i)hmac\s*\([^,]*,\s*["']md5["']"#, VulnerabilityType::Md5, Severity::High, "R HMAC-MD5", "Use HMAC-SHA256", Some("CWE-328")),
            (r#"(?i)hmac\s*\([^,]*,\s*["']sha1["']"#, VulnerabilityType::Sha1, Severity::High, "R HMAC-SHA1", "Use HMAC-SHA256", Some("CWE-328")),
            
            // Weak ciphers
            (r"(?i)des_encrypt|des_decrypt|openssl.*des|DES\s*\(", VulnerabilityType::Des, Severity::Critical, "R DES cipher", "Use AES", Some("CWE-327")),
            (r"(?i)des3|3des|triple.?des|desede", VulnerabilityType::TripleDes, Severity::High, "R 3DES cipher", "Use AES", Some("CWE-327")),
            (r"(?i)rc4_encrypt|rc4_decrypt|openssl.*rc4", VulnerabilityType::Rc4, Severity::Critical, "R RC4 cipher", "Use AES", Some("CWE-327")),
            (r"(?i)blowfish|openssl.*bf", VulnerabilityType::Blowfish, Severity::Medium, "R Blowfish cipher", "Use AES", Some("CWE-327")),
            
            // Weak random
            (r"(?i)\bsample\s*\(|\brunif\s*\(|\brnorm\s*\(|\brbinom\s*\(", VulnerabilityType::WeakRandom, Severity::High, "R random functions", "Use sodium::random", Some("CWE-338")),
            (r"(?i)set\.seed\s*\(|\.Random\.seed", VulnerabilityType::WeakRandom, Severity::High, "R set.seed detected", "Use cryptographic RNG", Some("CWE-338")),
            
            // Package imports/library calls
            (r"(?i)library\s*\(\s*openssl\s*\)|require\s*\(\s*openssl\s*\)", VulnerabilityType::Rsa2048, Severity::Low, "R openssl package", "Review crypto usage", Some("CWE-327")),
            (r"(?i)library\s*\(\s*digest\s*\)|require\s*\(\s*digest\s*\)", VulnerabilityType::Md5, Severity::Low, "R digest package", "Review hash usage", Some("CWE-328")),
            (r"(?i)library\s*\(\s*sodium\s*\)|require\s*\(\s*sodium\s*\)", VulnerabilityType::Ecdsa, Severity::Low, "R sodium package", "Review curve usage", Some("CWE-327")),
        ];

        pattern_defs.into_iter().filter_map(|(pattern, vuln_type, severity, desc, rec, cwe)| {
            Regex::new(pattern).ok().map(|regex| RPattern { 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];
        // R single-line comment: #
        if let Some(line_start) = before.rfind('\n') {
            if before[line_start..].contains('#') { return true; }
        }
        false
    }

    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"))
    }
}
