use serde::Deserialize;
use config::{Config, ConfigError, File, Environment};
use std::env;

#[derive(Debug, Deserialize, Clone)]
pub struct DatabaseConfig {
    pub url: String,
    pub pool_size: u32,
}

#[derive(Debug, Deserialize, Clone)]
pub struct RedisConfig {
    pub url: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct AuthConfig {
    pub jwt_secret: String, // Kept for legacy if needed, but we'll use keys
    pub jwt_public_key: String,
    pub jwt_private_key: String,
    pub refresh_token_ttl_days: u32,
}

#[derive(Debug, Deserialize, Clone)]
pub struct StripeConfig {
    pub secret_key: String,
    pub webhook_secret: String,
    pub starter_price_id: String,
    pub starter_metered_price_id: String,
    pub growth_price_id: String,
    pub growth_metered_price_id: String,
    pub success_url: String,
    pub cancel_url: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct TierRateLimits {
    pub standard: i64,
    pub compute_heavy: i64,
    pub webhook: i64,
}

#[derive(Debug, Deserialize, Clone)]
pub struct SandboxRateLimits {
    pub per_key_sustained_rps: i64,
    pub per_key_burst_rps: i64,
    pub per_tenant_rps: i64,
    pub daily_resolves: i64,
    pub max_concurrent: usize,
}

impl Default for SandboxRateLimits {
    fn default() -> Self {
        Self {
            per_key_sustained_rps: 5,
            per_key_burst_rps: 10,
            per_tenant_rps: 15,
            daily_resolves: 100,
            max_concurrent: 5,
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct RateLimitConfig {
    pub enabled: bool,
    pub fail_open: bool,
    pub unauthenticated: i64,
    pub low: TierRateLimits,
    pub medium: TierRateLimits,
    pub high: TierRateLimits,
    #[serde(default)]
    pub enterprise: TierRateLimits,
    #[serde(default)]
    pub sandbox: SandboxRateLimits,
}

impl Default for TierRateLimits {
    fn default() -> Self {
        Self {
            standard: 60,
            compute_heavy: 5,
            webhook: 60,
        }
    }
}

impl Default for RateLimitConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            fail_open: true,
            unauthenticated: 5,
            low: TierRateLimits { standard: 60, compute_heavy: 5, webhook: 60 },
            medium: TierRateLimits { standard: 200, compute_heavy: 20, webhook: 200 },
            high: TierRateLimits { standard: 500, compute_heavy: 50, webhook: 500 },
            enterprise: TierRateLimits { standard: 10000, compute_heavy: 1000, webhook: 10000 },
            sandbox: SandboxRateLimits::default(),
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct EmailConfig {
    #[serde(default = "default_smtp_host")]
    pub smtp_host: String,
    #[serde(default = "default_smtp_port")]
    pub smtp_port: u16,
    #[serde(default)]
    pub smtp_username: String,
    #[serde(default)]
    pub smtp_password: String,
    #[serde(default = "default_from_address")]
    pub from_address: String,
    #[serde(default)]
    pub enabled: bool,
}

fn default_smtp_host() -> String {
    "localhost".to_string()
}

fn default_smtp_port() -> u16 {
    587
}

fn default_from_address() -> String {
    "noreply@kanoniv.com".to_string()
}

impl Default for EmailConfig {
    fn default() -> Self {
        Self {
            smtp_host: default_smtp_host(),
            smtp_port: default_smtp_port(),
            smtp_username: String::new(),
            smtp_password: String::new(),
            from_address: default_from_address(),
            enabled: false,
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct SalesforceConfig {
    pub client_id: String,
    pub client_secret: String,
    pub redirect_uri: String,
    #[serde(default = "default_sf_api_version")]
    pub api_version: String,
}

fn default_sf_api_version() -> String {
    "v59.0".to_string()
}

#[derive(Debug, Deserialize, Clone)]
pub struct PipedriveConfig {
    pub client_id: String,
    pub client_secret: String,
    pub redirect_uri: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct LlmConfig {
    #[serde(default)]
    pub enabled: bool,
    #[serde(default)]
    pub api_key: String,
    #[serde(default = "default_llm_base_url")]
    pub base_url: String,
    #[serde(default = "default_llm_model")]
    pub model: String,
    #[serde(default = "default_llm_max_tokens")]
    pub max_tokens: u32,
    #[serde(default = "default_llm_temperature")]
    pub temperature: f32,
}

fn default_llm_base_url() -> String {
    "https://api.anthropic.com".to_string()
}

fn default_llm_model() -> String {
    "claude-sonnet-4-20250514".to_string()
}

fn default_llm_max_tokens() -> u32 {
    4096
}

fn default_llm_temperature() -> f32 {
    0.7
}

impl Default for LlmConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            api_key: String::new(),
            base_url: default_llm_base_url(),
            model: default_llm_model(),
            max_tokens: default_llm_max_tokens(),
            temperature: default_llm_temperature(),
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct EmbeddingsConfig {
    #[serde(default)]
    pub enabled: bool,
    #[serde(default = "default_embeddings_provider")]
    pub provider: String,
    #[serde(default)]
    pub api_key: String,
    #[serde(default = "default_embeddings_model")]
    pub model: String,
    #[serde(default = "default_embeddings_dimensions")]
    pub dimensions: usize,
    #[serde(default = "default_embeddings_base_url")]
    pub base_url: String,
    #[serde(default = "default_embeddings_dedup_threshold")]
    pub dedup_threshold: f64,
}

fn default_embeddings_provider() -> String {
    "openai".to_string()
}

fn default_embeddings_model() -> String {
    "text-embedding-3-small".to_string()
}

fn default_embeddings_dimensions() -> usize {
    1536
}

fn default_embeddings_base_url() -> String {
    "https://api.openai.com".to_string()
}

fn default_embeddings_dedup_threshold() -> f64 {
    0.92
}

impl Default for EmbeddingsConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            provider: default_embeddings_provider(),
            api_key: String::new(),
            model: default_embeddings_model(),
            dimensions: default_embeddings_dimensions(),
            base_url: default_embeddings_base_url(),
            dedup_threshold: default_embeddings_dedup_threshold(),
        }
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct AppConfig {
    pub port: u16,
    pub database: DatabaseConfig,
    pub redis: RedisConfig,
    pub auth: AuthConfig,
    pub env: String,
    pub cors_origins: Option<String>,
    pub stripe: Option<StripeConfig>,
    pub salesforce: Option<SalesforceConfig>,
    pub pipedrive: Option<PipedriveConfig>,
    #[serde(default)]
    pub rate_limit: RateLimitConfig,
    #[serde(default)]
    pub email: EmailConfig,
    #[serde(default)]
    pub llm: LlmConfig,
    #[serde(default)]
    pub embeddings: EmbeddingsConfig,
}

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

    /// Deserialize a RateLimitConfig from TOML without enterprise or sandbox sections.
    /// Both should fall back to their serde(default) values.
    #[test]
    fn test_rate_limit_config_missing_optional_sections() {
        let toml = r#"
            enabled = true
            fail_open = false
            unauthenticated = 10

            [low]
            standard = 60
            compute_heavy = 5
            webhook = 60

            [medium]
            standard = 200
            compute_heavy = 20
            webhook = 200

            [high]
            standard = 500
            compute_heavy = 50
            webhook = 500
        "#;

        let config: RateLimitConfig = toml::from_str(toml).unwrap();
        assert!(config.enabled);
        assert!(!config.fail_open);
        assert_eq!(config.unauthenticated, 10);

        // enterprise should use TierRateLimits::default() (60/5/60)
        assert_eq!(config.enterprise.standard, 60);
        assert_eq!(config.enterprise.compute_heavy, 5);

        // sandbox should use SandboxRateLimits::default()
        assert_eq!(config.sandbox.per_key_burst_rps, 10);
        assert_eq!(config.sandbox.daily_resolves, 100);
        assert_eq!(config.sandbox.max_concurrent, 5);
    }

    /// Deserialize with explicit enterprise and sandbox sections.
    #[test]
    fn test_rate_limit_config_with_all_sections() {
        let toml = r#"
            enabled = true
            fail_open = true
            unauthenticated = 5

            [low]
            standard = 60
            compute_heavy = 5
            webhook = 60

            [medium]
            standard = 200
            compute_heavy = 20
            webhook = 200

            [high]
            standard = 500
            compute_heavy = 50
            webhook = 500

            [enterprise]
            standard = 10000
            compute_heavy = 1000
            webhook = 10000

            [sandbox]
            per_key_sustained_rps = 3
            per_key_burst_rps = 6
            per_tenant_rps = 10
            daily_resolves = 50
            max_concurrent = 2
        "#;

        let config: RateLimitConfig = toml::from_str(toml).unwrap();
        assert_eq!(config.enterprise.standard, 10000);
        assert_eq!(config.sandbox.per_key_sustained_rps, 3);
        assert_eq!(config.sandbox.per_key_burst_rps, 6);
        assert_eq!(config.sandbox.per_tenant_rps, 10);
        assert_eq!(config.sandbox.daily_resolves, 50);
        assert_eq!(config.sandbox.max_concurrent, 2);
    }

    #[test]
    fn test_sandbox_rate_limits_default_values() {
        let defaults = SandboxRateLimits::default();
        assert_eq!(defaults.per_key_sustained_rps, 5);
        assert_eq!(defaults.per_key_burst_rps, 10);
        assert_eq!(defaults.per_tenant_rps, 15);
        assert_eq!(defaults.daily_resolves, 100);
        assert_eq!(defaults.max_concurrent, 5);
    }

    #[test]
    fn test_tier_rate_limits_default_values() {
        let defaults = TierRateLimits::default();
        assert_eq!(defaults.standard, 60);
        assert_eq!(defaults.compute_heavy, 5);
        assert_eq!(defaults.webhook, 60);
    }

    #[test]
    fn test_rate_limit_config_default_all_tiers_present() {
        let config = RateLimitConfig::default();
        // low
        assert_eq!(config.low.standard, 60);
        // medium
        assert_eq!(config.medium.standard, 200);
        // high
        assert_eq!(config.high.standard, 500);
        // enterprise
        assert_eq!(config.enterprise.standard, 10000);
        assert_eq!(config.enterprise.compute_heavy, 1000);
        // sandbox
        assert_eq!(config.sandbox.per_key_burst_rps, 10);
    }

    #[test]
    fn test_embeddings_config_defaults() {
        let config = EmbeddingsConfig::default();
        assert!(!config.enabled);
        assert_eq!(config.provider, "openai");
        assert!(config.api_key.is_empty());
        assert_eq!(config.model, "text-embedding-3-small");
        assert_eq!(config.dimensions, 1536);
        assert_eq!(config.base_url, "https://api.openai.com");
        assert!((config.dedup_threshold - 0.92).abs() < f64::EPSILON);
    }

    #[test]
    fn test_embeddings_config_from_toml() {
        let toml = r#"
            enabled = true
            provider = "openai"
            api_key = "sk-test-key"
            model = "text-embedding-3-large"
            dimensions = 3072
            base_url = "https://custom.api.com"
            dedup_threshold = 0.85
        "#;
        let config: EmbeddingsConfig = toml::from_str(toml).unwrap();
        assert!(config.enabled);
        assert_eq!(config.api_key, "sk-test-key");
        assert_eq!(config.model, "text-embedding-3-large");
        assert_eq!(config.dimensions, 3072);
        assert_eq!(config.base_url, "https://custom.api.com");
        assert!((config.dedup_threshold - 0.85).abs() < f64::EPSILON);
    }

    #[test]
    fn test_embeddings_config_partial_toml_uses_defaults() {
        let toml = r#"
            enabled = true
            api_key = "sk-key"
        "#;
        let config: EmbeddingsConfig = toml::from_str(toml).unwrap();
        assert!(config.enabled);
        assert_eq!(config.api_key, "sk-key");
        // Rest should be defaults
        assert_eq!(config.model, "text-embedding-3-small");
        assert_eq!(config.dimensions, 1536);
        assert_eq!(config.provider, "openai");
    }
}

impl AppConfig {
    pub fn load() -> Result<Self, ConfigError> {
        let env_name = env::var("RUN_MODE").unwrap_or_else(|_| "development".into());

        let s = Config::builder()
            .add_source(File::with_name("config/default").required(false))
            .add_source(File::with_name(&format!("config/{}", env_name)).required(false))
            .add_source(Environment::with_prefix("CANNON").separator("__"))
            .build()?;

        s.try_deserialize()
    }
}
