//! TLS version extractor.
//!
//! Detects usage of deprecated TLS versions (1.0, 1.1) per RFC 8996,
//! which deprecates TLS 1.0 and 1.1 for all use cases.

use regex::Regex;
use stemedb_core::types::ObjectValue;

use super::Extractor;
use crate::types::{ExtractedClaim, Language};

/// Extractor for deprecated TLS version usage.
///
/// Detects patterns indicating use of TLS 1.0 or TLS 1.1, which are
/// deprecated per RFC 8996. Modern applications should use TLS 1.2
/// or TLS 1.3 as minimum versions.
pub struct TlsVersionExtractor {
    // Rust patterns
    rust_tls10: Regex,
    rust_tls11: Regex,
    rust_min_version_hex: Regex,

    // Go patterns
    go_tls10: Regex,
    go_tls11: Regex,
    go_version_hex: Regex,

    // Python patterns
    python_tls10: Regex,
    python_tls11: Regex,
    python_sslv3: Regex,

    // JavaScript/Node patterns
    js_tls10: Regex,
    js_tls11: Regex,
    js_secure_protocol: Regex,

    // Config file patterns (YAML, TOML, JSON)
    config_min_version: Regex,

    // Semantic patterns (variable name suggests TLS + value is deprecated)
    semantic_tls_version: Regex,

    // Environment variable patterns
    env_tls_version: Regex,

    // Terraform HCL patterns
    terraform_tls: Regex,

    // Kubernetes camelCase patterns (YAML)
    k8s_tls: Regex,
}

impl Default for TlsVersionExtractor {
    fn default() -> Self {
        Self::new()
    }
}

impl TlsVersionExtractor {
    /// Create a new TLS version extractor with compiled regexes.
    ///
    /// # Panics
    /// Panics if any regex pattern is invalid. This is acceptable because:
    /// - Regex patterns are compile-time constants (static strings)
    /// - Invalid patterns indicate programmer error, not runtime conditions
    /// - Unit tests validate all patterns at test time
    #[allow(clippy::expect_used)]
    pub fn new() -> Self {
        Self {
            // Rust: rustls and native-tls TLS version patterns
            rust_tls10: Regex::new(r"(?:TLS10|TLS_1_0|TLS1_0|version::TLS10)").expect("valid regex"),
            rust_tls11: Regex::new(r"(?:TLS11|TLS_1_1|TLS1_1|version::TLS11)").expect("valid regex"),
            rust_min_version_hex: Regex::new(r"min_protocol_version.*0x030[01]")
                .expect("valid regex"),

            // Go: crypto/tls version constants
            go_tls10: Regex::new(r"(?:VersionTLS10|VersionSSL30)").expect("valid regex"),
            go_tls11: Regex::new(r"VersionTLS11").expect("valid regex"),
            go_version_hex: Regex::new(r"MinVersion\s*:\s*0x030[01]").expect("valid regex"),

            // Python: ssl module version constants
            python_tls10: Regex::new(r"(?:TLSv1(?:_0)?|PROTOCOL_TLSv1(?:_0)?)(?:\b|[^\d_])")
                .expect("valid regex"),
            python_tls11: Regex::new(r"(?:TLSv1_1|PROTOCOL_TLSv1_1)").expect("valid regex"),
            python_sslv3: Regex::new(r"(?:SSLv3|PROTOCOL_SSLv3|SSLv2|PROTOCOL_SSLv2)")
                .expect("valid regex"),

            // Node.js: tls options
            js_tls10: Regex::new(r#"minVersion\s*:\s*['"]TLSv1(?:\.0)?['"]"#)
                .expect("valid regex"),
            js_tls11: Regex::new(r#"minVersion\s*:\s*['"]TLSv1\.1['"]"#).expect("valid regex"),
            js_secure_protocol: Regex::new(
                r#"secureProtocol\s*:\s*['"](?:TLSv1_method|TLSv1_1_method|SSLv3_method)['"]"#,
            )
            .expect("valid regex"),

            // Config files: YAML, TOML, JSON patterns
            // Matches: min_version: "1.0", tls_min_version: TLSv1, minimum_tls_version: 1.1
            config_min_version: Regex::new(
                r#"(?i)(?:min_version|tls_min_version|minimum_tls_version|ssl_min_version)\s*[:=]\s*["']?(?:1\.[01]|TLSv?1(?:\.[01])?|SSL(?:v?3)?|TLS10|TLS11)["']?"#,
            )
            .expect("valid regex"),

            // Semantic: variable name contains tls/ssl and min/version in any order
            // Covers common patterns: TLS_MIN_VERSION, MIN_TLS_VERSION, SSL_VERSION, sslVersion, tlsMinVersion
            // Pattern 1: (tls|ssl) followed by min/version (e.g., TLS_MIN_VERSION, ssl_version)
            // Pattern 2: (min|version) followed by tls/ssl (e.g., MIN_TLS, VERSION_SSL)
            // Pattern 3: camelCase (e.g., sslVersion, tlsMinVersion, minTlsVersion)
            // Value must be terminated by quote, end of word, or end of line to avoid matching "TLS1_2" as "TLS1"
            // Allow optional type annotations (e.g., Rust `: &str`) between name and value
            semantic_tls_version: Regex::new(
                r#"(?i)\b(?:(?:tls|ssl)[_A-Z]*(?:min(?:imum)?|version)|(?:min(?:imum)?|version)[_A-Z]*(?:tls|ssl)|(?:tls|ssl)[A-Z][a-z]*(?:Version|Min)|(?:min|version)[A-Z][a-z]*(?:Tls|Ssl))\w*(?:\s*:\s*[^=]+)?\s*=\s*["']?(1\.[01]|TLSv?1(?:\.[01])?|SSL(?:v?[23])?)(?:["'\s;]|$)"#,
            )
            .expect("valid regex"),

            // Environment variables (NAME=value, with optional export)
            // Catches: TLS_MIN_VERSION=1.0, export SSL_VERSION=TLSv1
            env_tls_version: Regex::new(
                r#"(?i)^(?:export\s+)?(\w*(?:tls|ssl)\w*(?:_?(?:min|version))+\w*)\s*=\s*["']?(1\.[01]|TLSv?1(?:\.[01])?|SSL(?:v?[23])?)["']?\s*$"#,
            )
            .expect("valid regex"),

            // Terraform HCL patterns
            // Catches: min_tls_version = "TLS1_0", ssl_minimum_protocol_version = "TLSv1"
            // The value must be a complete deprecated version - use word boundary or quote/end
            terraform_tls: Regex::new(
                r#"(?i)(?:min(?:imum)?_)?(?:tls|ssl)(?:_(?:protocol_)?)?version\s*=\s*["']?(?:TLS_?1_?[01]|TLSv1(?:\.[01])?|1\.[01]|SSL(?:v?[23])?)(?:["'\s]|$)"#,
            )
            .expect("valid regex"),

            // Kubernetes camelCase patterns (YAML)
            // Catches: minTLSVersion: VersionTLS10, tlsMinVersion: "1.0"
            k8s_tls: Regex::new(
                r#"(?i)(?:min)?(?:tls|ssl)(?:Min)?(?:Version|Protocol)\s*:\s*["']?(?:VersionTLS1[01]|VersionSSL30|TLSv?1(?:\.[01])?|1\.[01]|SSL(?:v?[23])?)["']?"#,
            )
            .expect("valid regex"),
        }
    }

    fn check_pattern(
        &self,
        content: &str,
        pattern: &Regex,
        path_segments: &[String],
        file: &str,
        version: &str,
        description: &str,
    ) -> Vec<ExtractedClaim> {
        let mut claims = Vec::new();

        for (line_idx, line) in content.lines().enumerate() {
            if let Some(matched) = pattern.find(line) {
                let mut concept_path = path_segments.to_vec();
                concept_path.push("tls".to_string());
                concept_path.push("min_version".to_string());

                claims.push(ExtractedClaim {
                    concept_path: format!("code://{}", concept_path.join("/")),
                    predicate: "version".to_string(),
                    value: ObjectValue::Text(version.to_string()),
                    file: file.to_string(),
                    line: line_idx + 1,
                    matched_text: matched.as_str().to_string(),
                    confidence: 0.95,
                    description: description.to_string(),
                });
            }
        }

        claims
    }
}

impl Extractor for TlsVersionExtractor {
    fn name(&self) -> &str {
        "tls_version"
    }

    fn languages(&self) -> &[Language] {
        &[
            Language::Rust,
            Language::Go,
            Language::Python,
            Language::TypeScript,
            Language::JavaScript,
            Language::Yaml,
            Language::Toml,
            Language::Json,
            Language::Terraform,
            Language::Dotenv,
        ]
    }

    fn extract(
        &self,
        path_segments: &[String],
        content: &str,
        language: Language,
        file: &str,
    ) -> Vec<ExtractedClaim> {
        let mut claims = Vec::new();

        match language {
            Language::Rust => {
                claims.extend(self.check_pattern(
                    content,
                    &self.rust_tls10,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.rust_tls11,
                    path_segments,
                    file,
                    "1.1",
                    "TLS 1.1 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.rust_min_version_hex,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0/1.1 configured via hex version (RFC 8996 deprecated)",
                ));
            }
            Language::Go => {
                claims.extend(self.check_pattern(
                    content,
                    &self.go_tls10,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0 or SSL 3.0 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.go_tls11,
                    path_segments,
                    file,
                    "1.1",
                    "TLS 1.1 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.go_version_hex,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0/1.1 configured via hex version (RFC 8996 deprecated)",
                ));
            }
            Language::Python => {
                claims.extend(self.check_pattern(
                    content,
                    &self.python_tls10,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.python_tls11,
                    path_segments,
                    file,
                    "1.1",
                    "TLS 1.1 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.python_sslv3,
                    path_segments,
                    file,
                    "ssl3",
                    "SSL 2.0/3.0 is deprecated and insecure (RFC 8996)",
                ));
            }
            Language::TypeScript | Language::JavaScript => {
                claims.extend(self.check_pattern(
                    content,
                    &self.js_tls10,
                    path_segments,
                    file,
                    "1.0",
                    "TLS 1.0 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.js_tls11,
                    path_segments,
                    file,
                    "1.1",
                    "TLS 1.1 is deprecated (RFC 8996)",
                ));
                claims.extend(self.check_pattern(
                    content,
                    &self.js_secure_protocol,
                    path_segments,
                    file,
                    "1.0",
                    "Deprecated TLS/SSL protocol method (RFC 8996)",
                ));
            }
            Language::Yaml | Language::Toml | Language::Json => {
                claims.extend(self.check_pattern(
                    content,
                    &self.config_min_version,
                    path_segments,
                    file,
                    "deprecated",
                    "Deprecated TLS version in configuration (RFC 8996)",
                ));
                // Kubernetes camelCase patterns for YAML
                if language == Language::Yaml {
                    claims.extend(self.check_pattern(
                        content,
                        &self.k8s_tls,
                        path_segments,
                        file,
                        "deprecated",
                        "Kubernetes minTLSVersion set to deprecated value (RFC 8996)",
                    ));
                }
            }
            Language::Terraform => {
                claims.extend(self.check_pattern(
                    content,
                    &self.terraform_tls,
                    path_segments,
                    file,
                    "deprecated",
                    "Terraform TLS configuration uses deprecated version (RFC 8996)",
                ));
            }
            Language::Dotenv => {
                claims.extend(self.check_pattern(
                    content,
                    &self.env_tls_version,
                    path_segments,
                    file,
                    "deprecated",
                    "Environment variable sets deprecated TLS version (RFC 8996)",
                ));
            }
            _ => {}
        }

        // Apply semantic pattern to all languages (cross-language)
        // This catches patterns like: const TLS_MIN_VERSION = "1.0"
        claims.extend(self.check_pattern(
            content,
            &self.semantic_tls_version,
            path_segments,
            file,
            "deprecated",
            "Semantic: Variable name suggests TLS version, value is deprecated (RFC 8996)",
        ));

        claims
    }
}

#[cfg(test)]
#[path = "tls_version_tests.rs"]
mod tests;
