use anyhow::Result;
use std::path::Path;
use tree_sitter::{Node, Tree};
use uuid::Uuid;

use crate::types::{Language, Location, MigrationImpact, Severity, Vulnerability, VulnerabilityType, ImpactType};
use crate::analyzers::languages::{
    LanguageAnalyzer,
    javascript::JavaScriptAnalyzer,
    python::PythonAnalyzer,
    go::GoAnalyzer,
    java::JavaAnalyzer,
    rust::RustAnalyzer,
    cpp::CppAnalyzer,
    csharp::CSharpAnalyzer,
    php::PhpAnalyzer,
    ruby::RubyAnalyzer,
    shell::ShellAnalyzer,
};

/// AST-based analyzer using tree-sitter with improved detection logic
pub struct AstAnalyzer;

impl AstAnalyzer {
    pub fn new() -> Self {
        Self
    }

    /// Analyze a parsed AST for vulnerabilities
    pub fn analyze(
        &self,
        tree: &Tree,
        source: &str,
        file_path: &Path,
        language: Language,
    ) -> Result<Vec<Vulnerability>> {
        let mut vulnerabilities = Vec::new();
        let root = tree.root_node();

        self.traverse_node(root, source, file_path, language, &mut vulnerabilities);

        Ok(vulnerabilities)
    }

    fn traverse_node(
        &self,
        node: Node,
        source: &str,
        file_path: &Path,
        language: Language,
        vulnerabilities: &mut Vec<Vulnerability>,
    ) {
        // CRITICAL FIX #1: Skip comment nodes entirely
        if self.is_comment_node(&node) {
            return;
        }

        // Check current node for vulnerabilities
        if let Some(vuln) = self.check_node(&node, source, file_path, language) {
            vulnerabilities.push(vuln);
        }

        // Recurse into children
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            self.traverse_node(child, source, file_path, language, vulnerabilities);
        }
    }

    /// CRITICAL FIX #1: Check if a node is a comment node
    fn is_comment_node(&self, node: &Node) -> bool {
        let kind = node.kind();
        matches!(
            kind,
            "comment" | "line_comment" | "block_comment" | 
            "doc_comment" | "documentation_comment" |
            // Language-specific comment nodes
            "hash_comment" |  // Python, Shell
            "javadoc_comment" |  // Java
            "xml_doc_comment" |  // C#
            "shebang" |  // Script files
            "pragma" // Some languages
        )
    }

    fn check_node(
        &self,
        node: &Node,
        source: &str,
        file_path: &Path,
        language: Language,
    ) -> Option<Vulnerability> {
        match language {
            Language::JavaScript | Language::TypeScript => {
                JavaScriptAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Python => {
                PythonAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Go => {
                GoAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Java => {
                JavaAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Rust => {
                RustAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::C | Language::Cpp => {
                CppAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::CSharp => {
                CSharpAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Php => {
                PhpAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Ruby => {
                RubyAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Shell => {
                ShellAnalyzer::new().check_node(node, source, file_path, self)
            }
            Language::Groovy => {
                // Groovy reuses Java logic
                JavaAnalyzer::new().check_node(node, source, file_path, self)
            }
            // These languages use regex-based scanning (no tree-sitter parser on crates.io)
            Language::Kotlin | Language::Swift | Language::Dart | 
            Language::Scala | Language::ObjectiveC | Language::Perl | Language::Lua => None,
            _ => None,
        }
    }

    /// CRITICAL FIX #9: Detect quantum-unsafe key sizes
    pub fn check_key_size_literal(
        &self,
        node_text: &str,
        node: &Node,
        file_path: &Path,
        source: &str,
    ) -> Option<Vulnerability> {
        // Check if this is a common quantum-unsafe key size
        let key_sizes = ["1024", "2048", "3072", "4096", "256", "384", "521"];
        
        if !key_sizes.contains(&node_text) {
            return None;
        }
        
        // Get parent context to see if this is crypto-related
        let parent = node.parent()?;
        let parent_text = parent.utf8_text(source.as_bytes()).ok()?;
        let lower_parent = parent_text.to_lowercase();
        
        // Check if parent context suggests this is a key size
        let key_indicators = [
            "key", "modulus", "bits", "size", "length",
            "rsa", "ecdsa", "ecdh", "ec", "dsa", "dh",
            "generatekey", "keygen", "keypair"
        ];
        
        let is_key_related = key_indicators.iter().any(|indicator| lower_parent.contains(indicator));
        
        if !is_key_related {
            return None;
        }
        
        // Determine vulnerability type based on key size
        match node_text {
            "1024" | "2048" | "3072" | "4096" => {
                Some(self.create_vulnerability(
                    node,
                    file_path,
                    VulnerabilityType::Rsa2048,
                    Severity::Critical,
                    &format!("Quantum-unsafe RSA key size detected: {} bits", node_text),
                    "RSA of any size is vulnerable to quantum attacks. Migrate to CRYSTALS-Kyber/Dilithium",
                    source,
                ))
            }
            "256" | "384" | "521" => {
                Some(self.create_vulnerability(
                    node,
                    file_path,
                    VulnerabilityType::Ecdsa,
                    Severity::Critical,
                    &format!("Quantum-unsafe EC curve size detected: P-{}", node_text),
                    "Elliptic curves are vulnerable to quantum attacks. Migrate to post-quantum cryptography",
                    source,
                ))
            }
            _ => None,
        }
    }

    /// CRITICAL FIX #2: Improved string literal checking with better context awareness
    pub fn check_string_literal(
        &self,
        text: &str,
        node: &Node,
        file_path: &Path,
        source: &str,
        original_node: &Node,
    ) -> Option<Vulnerability> {
        let lower = text.to_lowercase();

        // CRITICAL FIX #12: Check for hardcoded keys first
        if self.is_hardcoded_key(text) {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::HardcodedKey,
                Severity::Critical,
                "Hardcoded cryptographic key detected",
                "Remove hardcoded keys and use secure key management",
                source,
            ));
        }

        // CRITICAL FIX #2: Check if this string is in a documentation/informational context
        if self.is_informational_string(text) {
            return None;
        }

        // CRITICAL FIX #2: Check if parent context suggests this is actual crypto usage
        let parent = original_node.parent()?;
        let parent_text = parent.utf8_text(source.as_bytes()).ok()?;
        let is_crypto_context = self.is_crypto_usage_context(parent_text);

        // Only flag algorithm identifiers if they're in a crypto usage context
        if !is_crypto_context {
            return None;
        }

        // Check for algorithm identifiers in strings
        if lower.contains("rsa-oaep") || lower.contains("rsassa") || lower.contains("rsa-pss") ||
           lower == "\"rsa\"" || lower == "'rsa'" {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::Rsa2048,
                Severity::Critical,
                "RSA algorithm identifier in string",
                "Update to PQC algorithm identifiers when available",
                source,
            ));
        }

        if lower.contains("ecdsa") || lower.contains("ecdh") || lower.contains("secp256") || 
           lower.contains("secp384") || lower.contains("prime256") || lower.contains("p-256") ||
           lower.contains("p-384") || lower.contains("p-521") {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::Ecdsa,
                Severity::Critical,
                "EC algorithm identifier in string",
                "Update to PQC algorithm identifiers",
                source,
            ));
        }

        if lower == "\"des\"" || lower == "'des'" || lower.contains("des-cbc") || 
           lower.contains("des-ecb") || lower.contains("3des") {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::Des,
                Severity::Critical,
                "DES algorithm identifier detected",
                "Replace with AES-256-GCM",
                source,
            ));
        }

        if lower.contains("rc4") || lower.contains("arcfour") {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::Rc4,
                Severity::Critical,
                "RC4 cipher identifier detected",
                "Replace with AES-256-GCM or ChaCha20-Poly1305",
                source,
            ));
        }

        if lower.contains("sslv2") || lower.contains("sslv3") || lower == "\"tlsv1\"" || 
           lower == "'tlsv1'" || lower.contains("tlsv1.0") || lower.contains("tls 1.0") {
            return Some(self.create_vulnerability(
                original_node,
                file_path,
                VulnerabilityType::InsecureTls,
                Severity::High,
                "Insecure TLS/SSL version detected",
                "Use TLS 1.3",
                source,
            ));
        }

        None
    }

    /// CRITICAL FIX #12: Detect hardcoded cryptographic keys
    fn is_hardcoded_key(&self, text: &str) -> bool {
        // PEM format keys
        if text.contains("-----BEGIN") && (
            text.contains("PRIVATE KEY") || 
            text.contains("RSA PRIVATE KEY") ||
            text.contains("EC PRIVATE KEY") ||
            text.contains("DSA PRIVATE KEY") ||
            text.contains("ENCRYPTED PRIVATE KEY")
        ) {
            return true;
        }

        // Base64-encoded keys (heuristic: long base64 strings)
        let cleaned = text.replace(&['"', '\'', ' ', '\n', '\r', '\t'][..], "");
        if cleaned.len() > 200 && cleaned.chars().all(|c| {
            c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '='
        }) {
            return true;
        }

        // Hex-encoded keys (heuristic: long hex strings)
        if cleaned.len() > 64 && cleaned.starts_with("0x") && 
           cleaned[2..].chars().all(|c| c.is_ascii_hexdigit()) {
            return true;
        }

        false
    }

    /// CRITICAL FIX #2: Determine if a string is informational/documentation
    fn is_informational_string(&self, text: &str) -> bool {
        let lower = text.to_lowercase();
        
        // Documentation indicators
        let documentation_indicators = [
            " is ", " are ", " was ", " were ", " has ", " have ", " had ",
            " provides ", " enables ", " uses ", " using ",
            " should ", " must ", " can ", " will ", " would ", " could ",
            " replaces ", " replaced ", " replacing ",
            " deprecated ", " broken ", " insecure ", " vulnerable ", " weak ",
            " migrate ", " migration ", " upgrade ", " upgrading ",
            " named after ", " stands for ", " means ",
            " shorter ", " longer ", " faster ", " slower ", " better ", " worse ",
            " quantum ", " post-quantum ", " pqc ",
            " than ", "compared to", "instead of", "rather than",
            "known as", "referred to", "also called", "such as",
            "example:", "e.g.", "i.e.", "note:", "warning:", "todo:",
        ];
        
        for indicator in documentation_indicators {
            if lower.contains(indicator) {
                return true;
            }
        }
        
        // Check if it's a comment-like string (starts with common comment patterns)
        if lower.trim_start().starts_with("//") || 
           lower.trim_start().starts_with("#") ||
           lower.trim_start().starts_with("/*") ||
           lower.trim_start().starts_with("*") {
            return true;
        }
        
        false
    }

    /// CRITICAL FIX #2: Determine if parent context suggests crypto usage
    fn is_crypto_usage_context(&self, parent_text: &str) -> bool {
        let lower = parent_text.to_lowercase();
        
        // Function call contexts
        let crypto_function_indicators = [
            "getinstance", "createcipher", "createhash", "createsign",
            "generatekey", "keypair", "cipher", "encrypt", "decrypt",
            "sign", "verify", "hash", "digest", "algorithm",
            "crypto.", "subtle.", "keyagreement", "signature",
            ".new(", ".create(", ".generate(",
        ];
        
        for indicator in crypto_function_indicators {
            if lower.contains(indicator) {
                return true;
            }
        }
        
        // Configuration contexts
        if lower.contains("algorithm") || lower.contains("cipher") || 
           lower.contains("curve") || lower.contains("scheme") {
            return true;
        }
        
        false
    }

    pub fn get_callee_name(&self, node: &Node, source: &str) -> Option<String> {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            if child.kind() == "member_expression" || child.kind() == "identifier" {
                return child.utf8_text(source.as_bytes()).ok().map(|s| s.to_string());
            }
        }
        None
    }

    pub fn get_call_args_text(&self, node: &Node, source: &str) -> String {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            if child.kind() == "arguments" {
                return child.utf8_text(source.as_bytes()).unwrap_or("").to_string();
            }
        }
        String::new()
    }

    pub fn get_python_func_name(&self, node: &Node, source: &str) -> Option<String> {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            if child.kind() == "attribute" || child.kind() == "identifier" {
                return child.utf8_text(source.as_bytes()).ok().map(|s| s.to_string());
            }
        }
        None
    }

    pub fn create_vulnerability(
        &self,
        node: &Node,
        file_path: &Path,
        vuln_type: VulnerabilityType,
        severity: Severity,
        description: &str,
        recommendation: &str,
        source: &str,
    ) -> Vulnerability {
        let start = node.start_position();
        let end = node.end_position();

        let location = Location {
            file: file_path.to_path_buf(),
            line: start.row + 1,
            column: start.column + 1,
            end_line: Some(end.row + 1),
            end_column: Some(end.column + 1),
        };

        Vulnerability {
            id: Uuid::new_v4(),
            vuln_type,
            severity,
            description: description.to_string(),
            location,
            code_snippet: self.extract_snippet(source, start.row + 1),
            recommendation: recommendation.to_string(),
            migration_impacts: vec![MigrationImpact {
                impact_type: ImpactType::ArchitecturalRefactor,
                description: "Migration to post-quantum cryptography required".to_string(),
                affected_components: vec![],
                estimated_effort: "Medium".to_string(),
                recommendation: recommendation.to_string(),
            }],
            cwe_id: None,
            is_dependency: false,
            dependency_chain: None,
            finding_type: crate::types::FindingType::DirectUsage,
            library_name: None,
        }
    }

    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());

        let snippet: Vec<String> = lines[start..end]
            .iter()
            .enumerate()
            .map(|(i, l)| format!("{:4} | {}", start + i + 1, l))
            .collect();

        Some(snippet.join("\n"))
    }
}