//! RFC normative statement parsers.
//!
//! Contains RFC-specific parsers for extracting normative statements
//! (MUST, SHALL, SHOULD per RFC 2119) from RFC documents.

use std::collections::HashMap;

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

/// A parsed normative statement from an RFC.
pub(super) struct NormativeStatement {
    /// Subject path (rfc://{number}/{topic}).
    pub subject: String,
    /// Predicate for the statement.
    pub predicate: String,
    /// Value extracted from the statement.
    pub value: ObjectValue,
    /// Human-readable description.
    pub description: String,
}

/// Parse normative statements from RFC text.
pub(super) fn parse_normative_statements(text: &str, rfc_num: u32) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // RFC-specific parsing based on content
    match rfc_num {
        7519 => statements.extend(parse_rfc7519_jwt(text)),
        6749 => statements.extend(parse_rfc6749_oauth(text)),
        6750 => statements.extend(parse_rfc6750_bearer(text)),
        8446 => statements.extend(parse_rfc8446_tls13(text)),
        7525 => statements.extend(parse_rfc7525_tls_practices(text)),
        6238 => statements.extend(parse_rfc6238_totp(text)),
        7617 => statements.extend(parse_rfc7617_basic_auth(text)),
        9110 => statements.extend(parse_rfc9110_http(text)),
        _ => {
            // Generic parsing for unknown RFCs
            statements.extend(parse_generic_rfc(text, rfc_num));
        }
    }

    statements
}

/// Parse RFC 7519 (JWT) normative statements.
fn parse_rfc7519_jwt(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Audience validation (Section 4.1.3)
    if contains_normative(text, "aud", "MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/audience_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "JWT audience claim MUST be validated (RFC 7519 Section 4.1.3)"
                .to_string(),
        });
    }

    // Expiry validation (Section 4.1.4)
    if contains_normative(text, "exp", "MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/expiry_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "JWT expiry claim MUST be validated (RFC 7519 Section 4.1.4)".to_string(),
        });
    }

    // Signature verification
    if text.contains("signature") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/signature_verification".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "JWT signatures MUST be verified (RFC 7519)".to_string(),
        });
    }

    // Algorithm restriction
    if text.contains("alg") && (text.contains("\"none\"") || text.contains("none algorithm")) {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/algorithm_restriction".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Text("explicit_list".to_string()),
            description: "JWT algorithm MUST be explicitly specified, 'none' algorithm forbidden"
                .to_string(),
        });
    }

    // Not Before validation (Section 4.1.5)
    if contains_normative(text, "nbf", "MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/nbf_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "JWT not-before claim MUST be validated (RFC 7519 Section 4.1.5)"
                .to_string(),
        });
    }

    // Issuer validation (Section 4.1.1)
    if contains_normative(text, "iss", "application-specific") {
        statements.push(NormativeStatement {
            subject: "rfc://7519/jwt/issuer_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "JWT issuer claim SHOULD be validated for application-specific purposes"
                .to_string(),
        });
    }

    statements
}

/// Parse RFC 6749 (OAuth 2.0) normative statements.
fn parse_rfc6749_oauth(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Redirect URI validation
    if text.contains("redirect_uri") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://6749/oauth/redirect_uri_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "OAuth redirect_uri MUST be validated exactly (RFC 6749)".to_string(),
        });
    }

    // State parameter
    if text.contains("state") && text.contains("SHOULD") {
        statements.push(NormativeStatement {
            subject: "rfc://6749/oauth/state_parameter".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "OAuth state parameter SHOULD be used for CSRF protection (RFC 6749)"
                .to_string(),
        });
    }

    // Scope validation
    if text.contains("scope") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://6749/oauth/scope_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "OAuth scope MUST be validated (RFC 6749)".to_string(),
        });
    }

    // HTTPS requirement
    if text.contains("TLS") || text.contains("HTTPS") {
        statements.push(NormativeStatement {
            subject: "rfc://6749/oauth/transport_security".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "OAuth endpoints MUST use TLS (RFC 6749)".to_string(),
        });
    }

    statements
}

/// Parse RFC 6750 (Bearer tokens) normative statements.
fn parse_rfc6750_bearer(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Transport security
    if text.contains("TLS") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://6750/bearer/transport_security".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "Bearer tokens MUST be transmitted over TLS (RFC 6750)".to_string(),
        });
    }

    // Token storage
    if text.contains("confidential") || text.contains("secure") {
        statements.push(NormativeStatement {
            subject: "rfc://6750/bearer/secure_storage".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "Bearer tokens MUST be stored securely (RFC 6750)".to_string(),
        });
    }

    statements
}

/// Parse RFC 8446 (TLS 1.3) normative statements.
fn parse_rfc8446_tls13(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Certificate verification
    if text.contains("certificate") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://8446/tls/cert_verification".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "TLS certificate chains MUST be verified (RFC 8446)".to_string(),
        });
    }

    // Cipher selection
    if text.contains("cipher") || text.contains("cipher_suite") {
        statements.push(NormativeStatement {
            subject: "rfc://8446/tls/cipher_selection".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Text("TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384".to_string()),
            description: "TLS 1.3 cipher suites (RFC 8446)".to_string(),
        });
    }

    // Protocol version
    statements.push(NormativeStatement {
        subject: "rfc://8446/tls/min_version".to_string(),
        predicate: "config_value".to_string(),
        value: ObjectValue::Text("TLS1.3".to_string()),
        description: "TLS 1.3 is the minimum recommended version (RFC 8446)".to_string(),
    });

    statements
}

/// Parse RFC 7525 (TLS best practices) normative statements.
fn parse_rfc7525_tls_practices(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Hostname verification
    if text.contains("hostname") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://7525/tls/hostname_verification".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "TLS hostname MUST be verified (RFC 7525)".to_string(),
        });
    }

    // Certificate revocation
    if text.contains("revocation") {
        statements.push(NormativeStatement {
            subject: "rfc://7525/tls/revocation_checking".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "TLS certificate revocation SHOULD be checked (RFC 7525)".to_string(),
        });
    }

    // Deprecated versions
    if text.contains("SSL") && text.contains("MUST NOT") {
        statements.push(NormativeStatement {
            subject: "rfc://7525/tls/deprecated_versions".to_string(),
            predicate: "disabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "SSLv2 and SSLv3 MUST NOT be used (RFC 7525)".to_string(),
        });
    }

    statements
}

/// Parse RFC 6238 (TOTP) normative statements.
fn parse_rfc6238_totp(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Time step
    if text.contains("30") && text.contains("time") {
        statements.push(NormativeStatement {
            subject: "rfc://6238/totp/time_step".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Number(30.0),
            description: "TOTP time step SHOULD be 30 seconds (RFC 6238)".to_string(),
        });
    }

    // Validation window
    if text.contains("window") || text.contains("tolerance") {
        statements.push(NormativeStatement {
            subject: "rfc://6238/totp/validation_window".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Number(1.0),
            description: "TOTP validation window SHOULD allow 1 step tolerance (RFC 6238)"
                .to_string(),
        });
    }

    // Key length
    if text.contains("key") && text.contains("160") {
        statements.push(NormativeStatement {
            subject: "rfc://6238/totp/key_length".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Number(160.0),
            description: "TOTP secret key SHOULD be at least 160 bits (RFC 6238)".to_string(),
        });
    }

    statements
}

/// Parse RFC 7617 (HTTP Basic Auth) normative statements.
fn parse_rfc7617_basic_auth(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Transport security
    if text.contains("TLS") || text.contains("confidential") {
        statements.push(NormativeStatement {
            subject: "rfc://7617/basic_auth/transport_security".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "HTTP Basic Auth MUST use TLS (RFC 7617)".to_string(),
        });
    }

    // UTF-8 encoding
    if text.contains("UTF-8") {
        statements.push(NormativeStatement {
            subject: "rfc://7617/basic_auth/encoding".to_string(),
            predicate: "config_value".to_string(),
            value: ObjectValue::Text("UTF-8".to_string()),
            description: "HTTP Basic Auth credentials SHOULD use UTF-8 (RFC 7617)".to_string(),
        });
    }

    statements
}

/// Parse RFC 9110 (HTTP Semantics) normative statements.
fn parse_rfc9110_http(text: &str) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();

    // Timeout handling
    if text.contains("timeout") {
        statements.push(NormativeStatement {
            subject: "rfc://9110/http/timeout_handling".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "HTTP timeouts SHOULD be configured (RFC 9110)".to_string(),
        });
    }

    // Host header
    if text.contains("Host") && text.contains("MUST") {
        statements.push(NormativeStatement {
            subject: "rfc://9110/http/host_header".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "HTTP/1.1 Host header MUST be present (RFC 9110)".to_string(),
        });
    }

    // Content-Length handling
    if text.contains("Content-Length") {
        statements.push(NormativeStatement {
            subject: "rfc://9110/http/content_length_validation".to_string(),
            predicate: "enabled".to_string(),
            value: ObjectValue::Boolean(true),
            description: "HTTP Content-Length SHOULD be validated (RFC 9110)".to_string(),
        });
    }

    statements
}

/// Generic RFC parsing for unknown RFCs.
fn parse_generic_rfc(text: &str, rfc_num: u32) -> Vec<NormativeStatement> {
    let mut statements = Vec::new();
    let keyword_pattern =
        match Regex::new(r"\b(MUST\s+NOT|MUST|SHALL\s+NOT|SHALL|SHOULD\s+NOT|SHOULD)\b") {
            Ok(re) => re,
            Err(_) => return statements,
        };

    // Find sections with normative keywords
    let section_topics = extract_section_topics(text);

    for (section, topic) in section_topics {
        // Check if this section has normative statements
        if keyword_pattern.is_match(&section) {
            let keyword = extract_strongest_keyword(&section);
            let is_mandatory = matches!(keyword.as_str(), "MUST" | "SHALL");

            statements.push(NormativeStatement {
                subject: format!("rfc://{}/{}", rfc_num, topic),
                predicate: if is_mandatory { "required" } else { "recommended" }.to_string(),
                value: ObjectValue::Boolean(true),
                description: format!("RFC {} {} requirement: {}", rfc_num, keyword, topic),
            });
        }
    }

    statements
}

/// Extract section numbers and their topics from RFC text.
fn extract_section_topics(text: &str) -> HashMap<String, String> {
    let section_pattern = match Regex::new(r"(?m)^(\d+(?:\.\d+)*)\.\s+(.+)$") {
        Ok(re) => re,
        Err(_) => return HashMap::new(),
    };
    let mut sections = HashMap::new();

    for cap in section_pattern.captures_iter(text) {
        let section_num = cap.get(1).map(|m| m.as_str()).unwrap_or("");
        let title = cap.get(2).map(|m| m.as_str()).unwrap_or("");

        // Create a slug from the title
        let slug = title
            .to_lowercase()
            .chars()
            .map(|c| if c.is_alphanumeric() { c } else { '_' })
            .collect::<String>()
            .trim_matches('_')
            .to_string();

        if !slug.is_empty() {
            // Extract section content (simplified - just the title for now)
            sections.insert(title.to_string(), format!("{}_{}", section_num, slug));
        }
    }

    sections
}

/// Extract the strongest normative keyword from text.
pub(super) fn extract_strongest_keyword(text: &str) -> String {
    let keywords = [
        ("MUST NOT", 5),
        ("MUST", 4),
        ("SHALL NOT", 3),
        ("SHALL", 2),
        ("SHOULD NOT", 1),
        ("SHOULD", 0),
    ];

    keywords
        .iter()
        .filter(|(kw, _)| text.contains(kw))
        .max_by_key(|(_, priority)| priority)
        .map(|(kw, _)| kw.to_string())
        .unwrap_or_else(|| "SHOULD".to_string())
}

/// Check if text contains a normative statement about a topic.
pub(super) fn contains_normative(text: &str, topic: &str, keyword: &str) -> bool {
    // Look for keyword near topic mention
    let pattern = format!(r"(?i){}[^.]*{}", topic, keyword);
    Regex::new(&pattern).map(|re| re.is_match(text)).unwrap_or(false) || {
        // Also check reverse order
        let reverse_pattern = format!(r"(?i){}[^.]*{}", keyword, topic);
        Regex::new(&reverse_pattern).map(|re| re.is_match(text)).unwrap_or(false)
    }
}
