//! Environment Variable Parsing and Validation
//!
//! This module provides secure parsing and validation of environment variables
//! and .env files with security validation.

use std::env;
use std::fs;
use std::path::Path;

/// Parse .env file securely without overriding existing environment variables
pub fn parse_env_file(env_file: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let content = fs::read_to_string(env_file)
        .map_err(|e| format!("Failed to read .env file {}: {}", env_file.display(), e))?;

    for (line_num, line) in content.lines().enumerate() {
        let line = line.trim();

        // Skip empty lines and comments
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        // Parse KEY=VALUE format
        if let Some(equals_pos) = line.find('=') {
            let key = line[..equals_pos].trim();
            let value = line[equals_pos + 1..].trim();

            // Security: Validate key format (alphanumeric + underscores only)
            if !is_valid_env_key(key) {
                eprintln!(
                    "Warning: Invalid environment variable key '{}' at line {} in {}",
                    key,
                    line_num + 1,
                    env_file.display()
                );
                continue;
            }

            // Only set if not already present (don't override existing env vars)
            if env::var(key).is_err() {
                let expanded_value = expand_shell_variables(value);
                env::set_var(key, expanded_value);
            }
        }
    }

    Ok(())
}

/// Validate environment variable key format for security
pub fn is_valid_env_key(key: &str) -> bool {
    !key.is_empty()
        && key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
        && !key.starts_with('_') // Reject private variables by convention
}

/// Expand shell variables like $(pwd) and ${VARIABLE} in a secure manner
pub fn expand_shell_variables(value: &str) -> String {
    let mut result = value.to_string();

    // Handle $(pwd) expansion
    if result.contains("$(pwd)") {
        if let Ok(pwd) = env::current_dir() {
            result = result.replace("$(pwd)", &pwd.to_string_lossy());
        }
    }

    // Handle ${VARIABLE} expansion
    // Use a simple regex-like approach to find and replace ${VAR} patterns
    while let Some(start) = result.find("${") {
        if let Some(end) = result[start..].find('}') {
            let end = start + end;
            let var_name = &result[start + 2..end];

            // Security: Only expand known safe environment variables
            if is_valid_env_key(var_name) {
                if let Ok(var_value) = env::var(var_name) {
                    let pattern = format!("${{{}}}", var_name);
                    result = result.replace(&pattern, &var_value);
                } else {
                    // If variable not found, remove the ${VAR} pattern to avoid infinite loops
                    let pattern = format!("${{{}}}", var_name);
                    result = result.replace(&pattern, "");
                }
            } else {
                // Invalid variable name, remove to avoid infinite loops
                let pattern = format!("${{{}}}", var_name);
                result = result.replace(&pattern, "");
            }
        } else {
            // Malformed ${VAR pattern, stop processing
            break;
        }
    }

    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_env_key_validation() {
        // Valid keys
        assert!(is_valid_env_key("SITE_ROOT_PATH"));
        assert!(is_valid_env_key("TEST_VAR"));
        assert!(is_valid_env_key("A1B2"));
        assert!(is_valid_env_key("VAR123"));

        // Invalid keys
        assert!(!is_valid_env_key(""));
        assert!(!is_valid_env_key("_PRIVATE"));
        assert!(!is_valid_env_key("VAR-NAME"));
        assert!(!is_valid_env_key("VAR NAME"));
    }

    #[test]
    fn test_shell_variable_expansion() {
        let test_value = "prefix_$(pwd)_suffix";
        let expanded = expand_shell_variables(test_value);

        if let Ok(pwd) = env::current_dir() {
            assert!(expanded.contains(&pwd.to_string_lossy().to_string()));
            assert!(expanded.starts_with("prefix_"));
            assert!(expanded.ends_with("_suffix"));
        }
    }

    #[test]
    fn test_env_variable_expansion() {
        // Set a test environment variable
        env::set_var("TEST_ROOT", "testroot");

        let test_value = "prefix/${TEST_ROOT}/suffix";
        let expanded = expand_shell_variables(test_value);
        assert_eq!(expanded, "prefix/testroot/suffix");

        // Test nested expansion
        env::set_var("TEST_NESTED", "${TEST_ROOT}/nested");
        let test_nested = "${TEST_NESTED}/file";
        let expanded_nested = expand_shell_variables(test_nested);
        assert_eq!(expanded_nested, "testroot/nested/file");

        // Clean up
        env::remove_var("TEST_ROOT");
        env::remove_var("TEST_NESTED");
    }
}
