//! Configuration schemas and cloud storage configurations.
//!
//! This module defines configuration schemas for various cloud storage backends
//! and configuration versioning. Primarily focused on S3-compatible storage
//! for model artifacts and configuration management in the Foxhunt trading system.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
use uuid::Uuid;

/// Configuration schema metadata for versioning and tracking.
///
/// Provides versioning and audit trail information for configuration schemas.
///
/// Used to track configuration changes over time and maintain compatibility
/// across different versions of the trading system.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigSchema {
    /// Unique identifier for this configuration schema
    pub id: Uuid,
    /// Semantic version string (e.g., "1.2.3")
    pub version: String,
    /// Timestamp when this schema was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when this schema was last updated
    pub updated_at: DateTime<Utc>,
}

/// Amazon S3 and S3-compatible storage configuration.
///
/// Configures access to S3 or S3-compatible storage services for storing
///
/// ML model artifacts, configuration backups, and other binary data.
///
/// Supports various authentication methods and connection options.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct S3Config {
    /// S3 bucket name for storing model artifacts and data
    pub bucket_name: String,
    /// AWS region or S3-compatible service region
    pub region: String,
    /// AWS access key ID (optional, can use IAM roles or environment variables)
    pub access_key_id: Option<String>,
    /// AWS secret access key (optional, can use IAM roles or environment variables)
    pub secret_access_key: Option<String>,
    /// AWS session token for temporary credentials (optional)
    pub session_token: Option<String>,
    /// Custom S3-compatible endpoint URL (e.g., MinIO, DigitalOcean Spaces)
    pub endpoint_url: Option<String>,
    /// Force path-style URLs instead of virtual-hosted-style URLs
    pub force_path_style: bool,
    /// Request timeout duration for S3 operations
    pub timeout: Duration,
    /// Maximum number of retry attempts for failed requests
    pub max_retry_attempts: u32,
    /// Enable SSL/TLS for S3 connections
    pub use_ssl: bool,
}

impl S3Config {
    /// Validates the S3 configuration for correctness.
    ///
    /// Performs validation checks on the S3 configuration to ensure all
    /// required fields are present and have valid values before attempting
    /// to establish connections to S3 services.
    ///
    /// # Errors
    ///
    /// Returns an error string if the configuration is invalid:
    /// - Empty bucket name
    ///
    /// - Empty region
    /// - Invalid endpoint URL format
    pub fn validate(&self) -> Result<(), String> {
        if self.bucket_name.is_empty() {
            return Err("S3 bucket name cannot be empty".to_owned());
        }
        if self.region.is_empty() {
            return Err("S3 region cannot be empty".to_owned());
        }
        Ok(())
    }

    /// Create S3Config for testing purposes (generic testing)
    #[cfg(test)]
    pub fn default_for_testing(bucket: &str) -> Self {
        Self {
            bucket_name: bucket.to_owned(),
            region: "us-east-1".to_owned(),
            access_key_id: None,
            secret_access_key: None,
            session_token: None,
            endpoint_url: None,
            force_path_style: false,
            timeout: Duration::from_secs(30),
            max_retry_attempts: 3,
            use_ssl: true,
        }
    }

    /// Create S3Config for MinIO testing (real S3-compatible testing)
    ///
    /// Available in test mode or when test-utils feature is enabled
    pub fn for_minio_testing(bucket: &str) -> Self {
        Self {
            bucket_name: bucket.to_owned(),
            region: "us-east-1".to_owned(),
            access_key_id: Some("foxhunt_test".to_owned()),
            secret_access_key: Some("foxhunt_test_password".to_owned()),
            session_token: None,
            endpoint_url: Some("http://localhost:9000".to_owned()),
            force_path_style: true, // MinIO requires path-style
            timeout: Duration::from_secs(30),
            max_retry_attempts: 3,
            use_ssl: false, // Local MinIO uses HTTP
        }
    }
}

/// Schema-level asset classification configuration for sector and type categorization.
///
/// Provides configuration-driven asset classification that replaces hardcoded
/// symbol-based classification logic. Supports flexible categorization rules
/// based on instrument properties rather than specific symbol names.
///
/// **Note**: This is a simpler schema-level config. For full asset classification
/// with volatility profiles and pattern rules, use `structures::AssetClassificationConfig`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetClassificationSchema {
    /// Classification rules based on asset type patterns
    pub asset_type_rules: HashMap<String, String>,
    /// Default classifications for different asset categories
    pub default_sectors: HashMap<String, String>,
    /// Regex patterns for currency pair detection
    pub currency_patterns: Vec<String>,
    /// Regex patterns for cryptocurrency detection
    pub crypto_patterns: Vec<String>,
}

impl AssetClassificationSchema {
    /// Creates a new asset classification schema with default rules.
    pub fn new() -> Self {
        let mut asset_type_rules = HashMap::new();
        asset_type_rules.insert("EQUITY".to_owned(), "Equity".to_owned());
        asset_type_rules.insert("FOREX".to_owned(), "Currencies".to_owned());
        asset_type_rules.insert("CRYPTO".to_owned(), "Cryptocurrency".to_owned());
        asset_type_rules.insert("COMMODITY".to_owned(), "Commodities".to_owned());
        asset_type_rules.insert("BOND".to_owned(), "Fixed Income".to_owned());

        let mut default_sectors = HashMap::new();
        default_sectors.insert("Equity".to_owned(), "Other".to_owned());
        default_sectors.insert("Currencies".to_owned(), "Currencies".to_owned());
        default_sectors.insert("Cryptocurrency".to_owned(), "Cryptocurrency".to_owned());
        default_sectors.insert("Commodities".to_owned(), "Commodities".to_owned());
        default_sectors.insert("Fixed Income".to_owned(), "Fixed Income".to_owned());

        Self {
            asset_type_rules,
            default_sectors,
            currency_patterns: vec![
                r"^[A-Z]{3}[A-Z]{3}$".to_owned(), // USDEUR format
                r".*USD.*".to_owned(),
                r".*EUR.*".to_owned(),
                r".*GBP.*".to_owned(),
                r".*JPY.*".to_owned(),
            ],
            crypto_patterns: vec![
                r".*BTC.*".to_owned(),
                r".*ETH.*".to_owned(),
                r".*CRYPTO.*".to_owned(),
            ],
        }
    }

    /// Classifies an instrument based on configuration rules.
    pub fn classify_sector(&self, instrument_id: &str, asset_type: Option<&str>) -> String {
        // First try to classify based on asset type if provided
        if let Some(asset_type) = asset_type {
            if let Some(sector) = self.asset_type_rules.get(asset_type) {
                return sector.clone();
            }
        }

        // Check for currency patterns
        for pattern in &self.currency_patterns {
            if let Ok(regex) = regex::Regex::new(pattern) {
                if regex.is_match(instrument_id) {
                    return "Currencies".to_owned();
                }
            }
        }

        // Check for crypto patterns
        for pattern in &self.crypto_patterns {
            if let Ok(regex) = regex::Regex::new(pattern) {
                if regex.is_match(instrument_id) {
                    return "Cryptocurrency".to_owned();
                }
            }
        }

        // Default classification
        "Other".to_owned()
    }
}

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

impl Default for S3Config {
    fn default() -> Self {
        Self {
            bucket_name: "foxhunt-models".to_owned(),
            region: "us-east-1".to_owned(),
            access_key_id: None,
            secret_access_key: None,
            session_token: None,
            endpoint_url: None,
            force_path_style: false,
            timeout: Duration::from_secs(30),
            max_retry_attempts: 3,
            use_ssl: true,
        }
    }
}
