use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::types::Severity;

/// Application configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
    pub scan: ScanConfig,
    pub api: ApiConfig,
    pub repo: RepoConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanConfig {
    pub analyze_dependencies: bool,
    pub analyze_db_impact: bool,
    pub min_severity: Severity,
    pub exclude: Vec<String>,
    pub max_file_size_bytes: usize,
    pub follow_symlinks: bool,
    /// Optional filter: only scan these languages (empty = scan all)
    #[serde(default)]
    pub languages: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiConfig {
    pub url: String,
    pub api_key: Option<String>,
    pub timeout_seconds: u64,
    /// When set (e.g. from KYBERCHECK_SCAN_ID), submission updates this scan record (e.g. dashboard-triggered run).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scan_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoConfig {
    pub id: Option<String>,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            scan: ScanConfig::default(),
            api: ApiConfig::default(),
            repo: RepoConfig::default(),
        }
    }
}

impl Default for ScanConfig {
    fn default() -> Self {
        Self {
            analyze_dependencies: true,
            analyze_db_impact: true,
            min_severity: Severity::Low,
            exclude: vec![
                // Version control
                ".git/**".to_string(),
                ".svn/**".to_string(),
                ".hg/**".to_string(),
                ".bzr/**".to_string(),
                
                // Node.js / JavaScript
                "node_modules/**".to_string(),
                "**/node_modules/**".to_string(),
                "**/*.min.js".to_string(),
                "**/*.bundle.js".to_string(),
                "**/*.chunk.js".to_string(),
                "**/*.map".to_string(),
                "**/dist/**".to_string(),
                "**/build/**".to_string(),
                "**/.next/**".to_string(),
                "**/.nuxt/**".to_string(),
                "**/bower_components/**".to_string(),
                
                // Python
                "**/__pycache__/**".to_string(),
                "**/.venv/**".to_string(),
                "**/venv/**".to_string(),
                "**/env/**".to_string(),
                "**/.env/**".to_string(),
                "**/virtualenv/**".to_string(),
                "**/*.pyc".to_string(),
                "**/*.pyo".to_string(),
                "**/*.egg-info/**".to_string(),
                "**/site-packages/**".to_string(),
                
                // Rust
                "target/**".to_string(),
                "**/target/**".to_string(),
                
                // Go
                "**/vendor/**".to_string(),
                "vendor/**".to_string(),
                
                // Java/JVM
                "**/bin/**".to_string(),
                "**/out/**".to_string(),
                "**/.gradle/**".to_string(),
                "**/.m2/**".to_string(),
                "**/*.class".to_string(),
                "**/*.jar".to_string(),
                "**/*.war".to_string(),
                
                // C#/.NET
                "**/obj/**".to_string(),
                "**/.nuget/**".to_string(),
                "**/*.dll".to_string(),
                "**/*.exe".to_string(),
                
                // PHP
                "**/vendor/**".to_string(),
                
                // Ruby
                "**/bundle/**".to_string(),
                "**/.bundle/**".to_string(),
                
                // Swift/iOS
                "**/Pods/**".to_string(),
                "**/.build/**".to_string(),
                "**/DerivedData/**".to_string(),
                
                // Dart/Flutter
                "**/.dart_tool/**".to_string(),
                "**/build/**".to_string(),
                
                // IDE/Editor
                "**/.idea/**".to_string(),
                "**/.vscode/**".to_string(),
                "**/.vs/**".to_string(),
                "**/*.swp".to_string(),
                "**/*.swo".to_string(),
                "**/*~".to_string(),
                
                // Test coverage & reports
                "**/coverage/**".to_string(),
                "**/.nyc_output/**".to_string(),
                "**/htmlcov/**".to_string(),
                "**/.coverage".to_string(),
                
                // Documentation
                "**/docs/_build/**".to_string(),
                "**/site/**".to_string(),
                
                // Misc generated/cached
                "**/.cache/**".to_string(),
                "**/tmp/**".to_string(),
                "**/temp/**".to_string(),
                "**/*.log".to_string(),
                "**/.DS_Store".to_string(),
                "**/Thumbs.db".to_string(),
            ],
            max_file_size_bytes: 10 * 1024 * 1024, // 10MB
            follow_symlinks: false,
            languages: vec![], // Empty = scan all languages
        }
    }
}

impl Default for ApiConfig {
    fn default() -> Self {
        Self {
            url: "https://api.kybercheck.io".to_string(),
            api_key: None,
            timeout_seconds: 30,
            scan_id: None,
        }
    }
}

impl Default for RepoConfig {
    fn default() -> Self {
        Self { id: None }
    }
}

impl AppConfig {
    pub fn new(
        analyze_dependencies: bool,
        analyze_db_impact: bool,
        min_severity: String,
        exclude: Vec<String>,
        languages: Option<Vec<String>>,
    ) -> Result<Self> {
        let mut config = Self::default();

        config.scan.analyze_dependencies = analyze_dependencies;
        config.scan.analyze_db_impact = analyze_db_impact;
        config.scan.min_severity = min_severity.parse().unwrap_or(Severity::Low);

        if !exclude.is_empty() {
            config.scan.exclude.extend(exclude);
        }

        // Set language filter if provided
        if let Some(langs) = languages {
            config.scan.languages = langs.iter().map(|s| s.to_lowercase()).collect();
        }

        // Try to load from config file
        if let Ok(file_config) = Self::load_from_file() {
            config.merge(file_config);
        }

        // Override with environment variables
        if let Ok(api_key) = std::env::var("KYBERCHECK_API_KEY") {
            config.api.api_key = Some(api_key);
        }
        if let Ok(api_url) = std::env::var("KYBERCHECK_API_URL") {
            config.api.url = api_url;
        }
        if let Ok(repo_id) = std::env::var("KYBERCHECK_REPO_ID") {
            config.repo.id = Some(repo_id);
        }
        if let Ok(scan_id) = std::env::var("KYBERCHECK_SCAN_ID") {
            config.api.scan_id = Some(scan_id);
        }

        Ok(config)
    }

    fn load_from_file() -> Result<Self> {
        let config_path = std::path::Path::new(".kybercheck.toml");
        if !config_path.exists() {
            anyhow::bail!("Config file not found");
        }

        let contents = std::fs::read_to_string(config_path)?;
        let config: Self = toml::from_str(&contents)?;
        Ok(config)
    }

    fn merge(&mut self, other: Self) {
        // Merge scan config
        if !other.scan.exclude.is_empty() {
            self.scan.exclude.extend(other.scan.exclude);
        }

        // API config
        if other.api.api_key.is_some() {
            self.api.api_key = other.api.api_key;
        }
        if other.api.url != ApiConfig::default().url {
            self.api.url = other.api.url;
        }

        // Repo config
        if other.repo.id.is_some() {
            self.repo.id = other.repo.id;
        }
    }
}

// Add toml dependency for config parsing
use serde;

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

    #[test]
    fn test_default_config() {
        let config = AppConfig::default();
        assert!(config.scan.analyze_dependencies);
        assert!(config.scan.analyze_db_impact);
        assert_eq!(config.scan.min_severity, Severity::Low);
    }
}
