//! SSH Pool Configuration
//!
//! Configuration types for SSH connection pooling with support for:
//! - Pool settings (min/max connections, timeouts)
//! - Credential management (Control-Center, temporal keys, static fallback)
//! - Health checking and resilience
//! - Security settings (Cedar, MFA, rate limiting)

use std::time::Duration;

use serde::{Deserialize, Serialize};

/// Complete SSH pool configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SshPoolConfig {
    /// Pool settings
    pub pool: PoolSettings,
    /// Credential management configuration
    pub credentials: CredentialConfig,
    /// Health check configuration
    pub health: HealthCheckConfig,
    /// Resilience configuration (retry, circuit breaker, timeouts)
    pub resilience: ResilienceConfig,
    /// Security configuration
    pub security: SecurityConfig,
}

/// Connection pool settings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoolSettings {
    /// Minimum connections to maintain per host
    pub min_connections: usize,
    /// Maximum connections per host
    pub max_connections: usize,
    /// Idle timeout in seconds before connection cleanup
    pub idle_timeout_seconds: u64,
    /// Pre-warm pool at startup
    pub pre_warm: bool,
    /// Acquisition timeout in seconds
    pub acquisition_timeout_seconds: u64,
}

impl Default for PoolSettings {
    fn default() -> Self {
        Self {
            min_connections: 2,
            max_connections: 10,
            idle_timeout_seconds: 300,
            pre_warm: false,
            acquisition_timeout_seconds: 30,
        }
    }
}

/// Credential management configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CredentialConfig {
    /// Control-Center URL for permanent keys
    pub control_center_url: String,
    /// Control-Center JWT token (from environment)
    pub control_center_token: Option<String>,
    /// Use temporal keys from orchestrator
    pub use_temporal_keys: bool,
    /// TTL for temporal keys in seconds
    pub temporal_key_ttl_seconds: u64,
    /// Path to provisioning key for fallback
    pub provisioning_key_path: String,
    /// Token refresh interval in seconds
    pub refresh_interval_seconds: u64,
    /// Cache permanent keys in memory (encrypted)
    pub cache_permanent_keys: bool,
}

impl Default for CredentialConfig {
    fn default() -> Self {
        Self {
            control_center_url: "http://localhost:9080".to_string(),
            control_center_token: None,
            use_temporal_keys: true,
            temporal_key_ttl_seconds: 3600, // 1 hour
            provisioning_key_path: "~/.ssh/provisioning_key".to_string(),
            refresh_interval_seconds: 1800, // 30 minutes
            cache_permanent_keys: true,
        }
    }
}

/// Health check configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckConfig {
    /// Enable passive health tracking
    pub passive_enabled: bool,
    /// Enable active health checks
    pub active_enabled: bool,
    /// Health check interval in seconds
    pub check_interval_seconds: u64,
    /// Health check command to execute
    pub check_command: String,
    /// Health check timeout in seconds
    pub check_timeout_seconds: u64,
    /// Consecutive failures before marking unhealthy
    pub failure_threshold: u32,
    /// Consecutive successes before recovering
    pub recovery_threshold: u32,
}

impl Default for HealthCheckConfig {
    fn default() -> Self {
        Self {
            passive_enabled: true,
            active_enabled: true,
            check_interval_seconds: 60,
            check_command: "echo health_check".to_string(),
            check_timeout_seconds: 5,
            failure_threshold: 3,
            recovery_threshold: 2,
        }
    }
}

/// Resilience configuration (retry, circuit breaker, timeouts)
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResilienceConfig {
    /// Retry configuration
    pub retry: RetryConfig,
    /// Circuit breaker configuration
    pub circuit_breaker: CircuitBreakerConfig,
    /// Timeout settings
    pub timeouts: TimeoutConfig,
}

/// Retry configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
    /// Maximum number of retries
    pub max_retries: u32,
    /// Backoff strategy
    pub backoff_strategy: BackoffStrategy,
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self {
            max_retries: 3,
            backoff_strategy: BackoffStrategy::Exponential {
                base: 2,
                max_seconds: 60,
            },
        }
    }
}

/// Backoff strategies for retry logic
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum BackoffStrategy {
    /// Exponential backoff: base^attempt
    Exponential {
        /// Base for exponential calculation
        base: u64,
        /// Maximum wait time in seconds
        max_seconds: u64,
    },
    /// Linear backoff: base * attempt
    Linear {
        /// Base wait time in seconds
        base: u64,
        /// Maximum wait time in seconds
        max_seconds: u64,
    },
    /// Fibonacci backoff: fib(attempt)
    Fibonacci {
        /// Maximum wait time in seconds
        max_seconds: u64,
    },
    /// Fixed backoff: always same delay
    Fixed {
        /// Wait time in seconds
        seconds: u64,
    },
}

/// Circuit breaker configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CircuitBreakerConfig {
    /// Consecutive failures to open circuit
    pub failure_threshold: u32,
    /// Consecutive successes to close circuit
    pub success_threshold: u32,
    /// Timeout in seconds before attempting half-open
    pub timeout_seconds: u64,
    /// Use per-host circuit breakers
    pub per_host: bool,
}

impl Default for CircuitBreakerConfig {
    fn default() -> Self {
        Self {
            failure_threshold: 5,
            success_threshold: 2,
            timeout_seconds: 60,
            per_host: true,
        }
    }
}

/// Timeout configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeoutConfig {
    /// Connection establishment timeout in seconds
    pub connect_timeout_seconds: u64,
    /// SSH authentication timeout in seconds
    pub auth_timeout_seconds: u64,
    /// Command execution timeout in seconds
    pub command_timeout_seconds: u64,
    /// Total operation timeout in seconds
    pub total_timeout_seconds: u64,
}

impl Default for TimeoutConfig {
    fn default() -> Self {
        Self {
            connect_timeout_seconds: 30,
            auth_timeout_seconds: 60,
            command_timeout_seconds: 300,
            total_timeout_seconds: 900,
        }
    }
}

impl TimeoutConfig {
    /// Convert timeout to Duration
    pub fn connect_duration(&self) -> Duration {
        Duration::from_secs(self.connect_timeout_seconds)
    }

    /// Convert auth timeout to Duration
    pub fn auth_duration(&self) -> Duration {
        Duration::from_secs(self.auth_timeout_seconds)
    }

    /// Convert command timeout to Duration
    pub fn command_duration(&self) -> Duration {
        Duration::from_secs(self.command_timeout_seconds)
    }

    /// Convert total timeout to Duration
    pub fn total_duration(&self) -> Duration {
        Duration::from_secs(self.total_timeout_seconds)
    }
}

/// Security configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
    /// Enable Cedar policy evaluation
    pub cedar_enabled: bool,
    /// Cedar action name for SSH operations
    pub cedar_action: String,
    /// Enable audit logging
    pub audit_enabled: bool,
    /// Require MFA for production servers
    pub mfa_required_for_production: bool,
    /// Rate limit: commands per minute
    pub rate_limit_per_minute: u32,
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            cedar_enabled: true,
            cedar_action: "ssh::execute".to_string(),
            audit_enabled: true,
            mfa_required_for_production: true,
            rate_limit_per_minute: 60,
        }
    }
}

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

    #[test]
    fn test_default_config_valid() {
        let config = SshPoolConfig::default();
        assert!(config.pool.min_connections <= config.pool.max_connections);
        assert!(config.pool.max_connections > 0);
        assert!(config.resilience.retry.max_retries > 0);
    }

    #[test]
    fn test_timeout_conversions() {
        let config = TimeoutConfig::default();
        assert_eq!(config.connect_duration().as_secs(), 30);
        assert_eq!(config.command_duration().as_secs(), 300);
    }
}
