//! Configuration management for the RAG module

pub mod service_registry;

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tokio::fs;

/// Main configuration structure matching JavaScript version
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    #[serde(rename = "embeddingModel")]
    pub embedding_model: String,

    #[serde(rename = "embeddingDimensions")]
    pub embedding_dimensions: u32,

    #[serde(rename = "vectorStore")]
    pub vector_store: VectorStoreConfig,

    #[serde(rename = "chunkSize")]
    pub chunk_size: usize,

    #[serde(rename = "searchTopK")]
    pub search_top_k: usize,

    #[serde(rename = "privacyLevel")]
    pub privacy_level: String,

    #[serde(rename = "backendMapping")]
    pub backend_mapping: bool,

    // Compatibility fields for existing code
    pub encryption: crate::types::EncryptionConfig,
    pub embedding: crate::types::EmbeddingConfig,
    pub privacy: PrivacyConfig,
    pub iam: IAMConfig,

    #[serde(rename = "s3Config")]
    pub s3_config: S3Config,

    #[serde(rename = "azureBlobConfig")]
    pub azure_blob_config: AzureBlobConfig,

    #[serde(rename = "gcsConfig")]
    pub gcs_config: GcsConfig,
}

/// Vector store configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorStoreConfig {
    pub backend: String,
    pub connection: crate::types::QdrantConnectionConfig,
    #[serde(rename = "storagePath")]
    pub storage_path: Option<String>,
    /// Server URL for syncing (when using embedded mode)
    #[serde(rename = "serverSyncUrl")]
    pub server_sync_url: Option<String>,
    /// Whether to enable dual mode (embedded + server sync)
    #[serde(rename = "enableServerSync")]
    pub enable_server_sync: bool,
}

impl Default for VectorStoreConfig {
    fn default() -> Self {
        Self {
            backend: "qdrant-embedded".to_string(),
            connection: crate::types::QdrantConnectionConfig {
                url: "http://localhost:6333".to_string(),
                api_key: None,
                timeout_secs: 30,
            },
            storage_path: Some("./qdrant-data".to_string()),
            server_sync_url: Some("http://dev-qdrant-nlb-e8c337edc3ee861b.elb.ap-south-1.amazonaws.com:6334".to_string()),
            enable_server_sync: true,
        }
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            embedding_model: "embaas/sentence-transformers-e5-large-v2".to_string(),
            embedding_dimensions: 1024,
            vector_store: VectorStoreConfig::default(),
            chunk_size: 1024,
            search_top_k: 10,
            privacy_level: "minimal-aws".to_string(),
            backend_mapping: false,
            // Compatibility fields
            encryption: crate::types::EncryptionConfig::default(),
            embedding: crate::types::EmbeddingConfig::default(),
            privacy: PrivacyConfig::default(),
            iam: IAMConfig::default(),
            // JavaScript-style configs
            s3_config: S3Config::default(),
            azure_blob_config: AzureBlobConfig::default(),
            gcs_config: GcsConfig::default(),
        }
    }
}

/// Encryption configuration matching JavaScript structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptionConfig {
    pub algorithm: String,

    #[serde(rename = "enableContentEncryption")]
    pub enable_content_encryption: bool,

    #[serde(rename = "enableEmbeddingEncryption")]
    pub enable_embedding_encryption: bool,

    #[serde(rename = "enableMetadataEncryption")]
    pub enable_metadata_encryption: bool,

    #[serde(rename = "enableIdHashing")]
    pub enable_id_hashing: bool,

    #[serde(rename = "keyRotationDays")]
    pub key_rotation_days: u32,
}

impl Default for EncryptionConfig {
    fn default() -> Self {
        Self {
            algorithm: "AES-256-GCM".to_string(),
            enable_content_encryption: false,
            enable_embedding_encryption: false,
            enable_metadata_encryption: false,
            enable_id_hashing: false,
            key_rotation_days: 90,
        }
    }
}

/// S3 configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct S3Config {
    pub enabled: bool,
    pub bucket: String,
    pub region: String,
    #[serde(rename = "encryptionEnabled")]
    pub encryption_enabled: bool,
    #[serde(rename = "endpointUrl")]
    pub endpoint_url: Option<String>,
}

impl Default for S3Config {
    fn default() -> Self {
        Self {
            enabled: false,
            bucket: String::new(),
            region: "us-east-1".to_string(),
            encryption_enabled: true,
            endpoint_url: None,
        }
    }
}

/// Azure Blob configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AzureBlobConfig {
    pub enabled: bool,
    #[serde(rename = "containerName")]
    pub container_name: String,
    #[serde(rename = "encryptionEnabled")]
    pub encryption_enabled: bool,
}

impl Default for AzureBlobConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            container_name: String::new(),
            encryption_enabled: true,
        }
    }
}

/// Google Cloud Storage configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GcsConfig {
    pub enabled: bool,
    #[serde(rename = "bucketName")]
    pub bucket_name: String,
    #[serde(rename = "encryptionEnabled")]
    pub encryption_enabled: bool,
}

impl Default for GcsConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            bucket_name: String::new(),
            encryption_enabled: true,
        }
    }
}

/// IAM configuration for backwards compatibility
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IAMConfig {
    pub enable_iam_analysis: bool,
    pub supported_services: Vec<String>,
    pub risk_assessment: bool,
}

impl Default for IAMConfig {
    fn default() -> Self {
        Self {
            enable_iam_analysis: true,
            supported_services: vec![
                "ec2".to_string(),
                "rds".to_string(),
                "s3".to_string(),
                "lambda".to_string(),
            ],
            risk_assessment: true,
        }
    }
}

/// Privacy configuration for backwards compatibility
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivacyConfig {
    pub level: String,
    pub enable_data_filtering: bool,
    pub confidential_fields: Vec<String>,
    pub non_confidential_fields: Vec<String>,
}

impl Default for PrivacyConfig {
    fn default() -> Self {
        Self {
            level: "minimal-aws".to_string(),
            enable_data_filtering: true,
            confidential_fields: vec![
                "resource_id".to_string(),
                "arn".to_string(),
                "vpc_id".to_string(),
                "security_groups".to_string(),
                "iam_roles".to_string(),
            ],
            non_confidential_fields: vec![
                "account_id".to_string(),
                "region".to_string(),
                "service".to_string(),
                "instance_type".to_string(),
                "environment".to_string(),
            ],
        }
    }
}

/// Configuration manager matching JavaScript version
#[derive(Clone)]
pub struct ConfigManager {
    base_path: PathBuf,
    config_path: PathBuf,
    default_config: Config,
    config: Config,
}

impl ConfigManager {
    /// Create a new configuration manager
    pub async fn new(base_path: &Path) -> Result<Self> {
        let base_path = base_path.to_path_buf();
        let config_path = base_path.join("config").join("config.yaml");
        let default_config = Config::default();

        let mut manager = Self {
            base_path,
            config_path,
            default_config: default_config.clone(),
            config: default_config,
        };

        manager.initialize().await?;
        Ok(manager)
    }

    /// Initialize configuration manager
    pub async fn initialize(&mut self) -> Result<bool> {
        // Ensure config directory exists
        if let Some(parent) = self.config_path.parent() {
            fs::create_dir_all(parent).await?;
        }

        self.load_config().await?;
        Ok(true)
    }

    /// Load configuration from file
    pub async fn load_config(&mut self) -> Result<()> {
        if self.config_path.exists() {
            let config_text = fs::read_to_string(&self.config_path).await?;

            // Try YAML first, then JSON
            if let Ok(file_config) = serde_yaml::from_str::<Config>(&config_text) {
                // Merge with default config
                self.config = self.merge_configs(&self.default_config, &file_config);
            } else if let Ok(file_config) = serde_json::from_str::<Config>(&config_text) {
                self.config = self.merge_configs(&self.default_config, &file_config);
            } else {
                eprintln!("Failed to load config, using defaults");
                self.config = self.default_config.clone();
                self.save_config().await?;
            }
        } else {
            self.config = self.default_config.clone();
            self.save_config().await?;
        }

        Ok(())
    }

    /// Save configuration to file
    pub async fn save_config(&self) -> Result<()> {
        let config_text = serde_yaml::to_string(&self.config)?;
        fs::write(&self.config_path, config_text).await?;
        Ok(())
    }

    /// Update configuration (partial update)
    pub async fn update_config(&mut self, updates: Config) -> Result<()> {
        self.config = self.merge_configs(&self.config, &updates);
        self.save_config().await?;
        Ok(())
    }

    /// Get current configuration
    pub fn get_config(&self) -> Config {
        self.config.clone()
    }

    /// Get specific config value by key (simplified version)
    pub fn get(&self, key: &str) -> Option<serde_yaml::Value> {
        let config_value = serde_yaml::to_value(&self.config).ok()?;
        if let serde_yaml::Value::Mapping(map) = config_value {
            map.get(&serde_yaml::Value::String(key.to_string())).cloned()
        } else {
            None
        }
    }

    /// Set specific config value by key (simplified version)
    pub async fn set(&mut self, key: &str, value: serde_yaml::Value) -> Result<()> {
        let mut config_value = serde_yaml::to_value(&self.config)?;

        if let serde_yaml::Value::Mapping(ref mut map) = config_value {
            map.insert(serde_yaml::Value::String(key.to_string()), value);
            self.config = serde_yaml::from_value(config_value)?;
            self.save_config().await?;
        }

        Ok(())
    }

    /// Get encryption configuration (returns types::EncryptionConfig for compatibility)
    pub fn get_encryption_config(&self) -> &crate::types::EncryptionConfig {
        &self.config.encryption
    }

    /// Get S3 configuration
    pub fn get_s3_config(&self) -> &S3Config {
        &self.config.s3_config
    }

    /// Get Azure Blob configuration
    pub fn get_azure_blob_config(&self) -> &AzureBlobConfig {
        &self.config.azure_blob_config
    }

    /// Get GCS configuration
    pub fn get_gcs_config(&self) -> &GcsConfig {
        &self.config.gcs_config
    }

    /// Merge configurations (second config overrides first)
    fn merge_configs(&self, base: &Config, updates: &Config) -> Config {
        // For simplicity, just clone the updates
        // In a more sophisticated implementation, you could do field-by-field merging
        updates.clone()
    }
}

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

    #[tokio::test]
    async fn test_config_manager_creation() {
        let temp_dir = TempDir::new().unwrap();
        let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();

        let config = config_manager.get_config();
        assert_eq!(config.embedding_model, "BAAI/bge-m3");
        assert_eq!(config.embedding_dimensions, 1024);
        assert_eq!(config.privacy_level, "minimal-aws");
    }

    #[tokio::test]
    async fn test_config_save_load() {
        let temp_dir = TempDir::new().unwrap();
        let mut config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();

        let mut new_config = Config::default();
        new_config.embedding_model = "test-model".to_string();
        new_config.chunk_size = 2048;

        config_manager.update_config(new_config).await.unwrap();

        // Create new manager to test loading
        let new_config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
        let config = new_config_manager.get_config();
        assert_eq!(config.embedding_model, "test-model");
        assert_eq!(config.chunk_size, 2048);
    }

    #[tokio::test]
    async fn test_config_get_set() {
        let temp_dir = TempDir::new().unwrap();
        let mut config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();

        // Test get
        let chunk_size = config_manager.get("chunkSize");
        assert!(chunk_size.is_some());

        // Test set
        let new_value = serde_yaml::Value::Number(serde_yaml::Number::from(2048));
        config_manager.set("chunkSize", new_value).await.unwrap();

        let config = config_manager.get_config();
        assert_eq!(config.chunk_size, 2048);
    }

    #[tokio::test]
    async fn test_encryption_config() {
        let temp_dir = TempDir::new().unwrap();
        let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();

        let encryption_config = config_manager.get_encryption_config();
        assert_eq!(encryption_config.algorithm, "AES-256-GCM");
        assert_eq!(encryption_config.enable_content_encryption, true);
        assert_eq!(encryption_config.enable_metadata_encryption, true);
        assert_eq!(encryption_config.key_rotation_days, Some(90));
    }

    #[tokio::test]
    async fn test_cloud_configs() {
        let temp_dir = TempDir::new().unwrap();
        let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();

        let s3_config = config_manager.get_s3_config();
        assert_eq!(s3_config.enabled, false);
        assert_eq!(s3_config.region, "us-east-1");
        assert_eq!(s3_config.encryption_enabled, true);

        let azure_config = config_manager.get_azure_blob_config();
        assert_eq!(azure_config.enabled, false);
        assert_eq!(azure_config.encryption_enabled, true);

        let gcs_config = config_manager.get_gcs_config();
        assert_eq!(gcs_config.enabled, false);
        assert_eq!(gcs_config.encryption_enabled, true);
    }
}