//! Gateway configuration management.

use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::time::Duration;

/// Main gateway configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct GatewayConfig {
    /// Server configuration
    pub server: ServerConfig,

    /// Authentication configuration
    pub auth: AuthConfig,

    /// Rate limiting configuration
    pub rate_limit: RateLimitConfig,

    /// CORS configuration
    pub cors: CorsConfig,

    /// Service endpoints
    pub services: ServiceEndpoints,

    /// Metrics configuration
    pub metrics: MetricsConfig,
}

impl Default for GatewayConfig {
    fn default() -> Self {
        Self {
            server: ServerConfig::default(),
            auth: AuthConfig::default(),
            rate_limit: RateLimitConfig::default(),
            cors: CorsConfig::default(),
            services: ServiceEndpoints::default(),
            metrics: MetricsConfig::default(),
        }
    }
}

/// HTTP server configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
    /// Listen address for REST API
    pub listen_addr: SocketAddr,

    /// Listen address for WebSocket
    pub ws_addr: SocketAddr,

    /// Maximum request body size in bytes
    pub max_body_size: usize,

    /// Request timeout
    #[serde(with = "humantime_serde")]
    pub request_timeout: Duration,

    /// Shutdown grace period
    #[serde(with = "humantime_serde")]
    pub shutdown_timeout: Duration,

    /// Enable compression
    pub compression: bool,

    /// API version prefix
    pub api_version: String,
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            listen_addr: "0.0.0.0:8000".parse().unwrap(),
            ws_addr: "0.0.0.0:8001".parse().unwrap(),
            max_body_size: 10 * 1024 * 1024, // 10 MB
            request_timeout: Duration::from_secs(30),
            shutdown_timeout: Duration::from_secs(10),
            compression: true,
            api_version: "v1".to_string(),
        }
    }
}

/// Authentication configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct AuthConfig {
    /// Enable authentication
    pub enabled: bool,

    /// JWT secret for token signing
    pub jwt_secret: String,

    /// JWT token expiration
    #[serde(with = "humantime_serde")]
    pub jwt_expiration: Duration,

    /// API key header name
    pub api_key_header: String,

    /// Allow unauthenticated access to health endpoints
    pub allow_health_unauthenticated: bool,

    /// Allow unauthenticated access to docs
    pub allow_docs_unauthenticated: bool,
}

impl Default for AuthConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            jwt_secret: "change-me-in-production".to_string(),
            jwt_expiration: Duration::from_secs(3600), // 1 hour
            api_key_header: "X-API-Key".to_string(),
            allow_health_unauthenticated: true,
            allow_docs_unauthenticated: true,
        }
    }
}

/// Rate limiting configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct RateLimitConfig {
    /// Enable rate limiting
    pub enabled: bool,

    /// Default requests per minute
    pub default_rpm: u32,

    /// Burst size
    pub burst_size: u32,

    /// Rate limit tiers
    pub tiers: RateLimitTiers,
}

impl Default for RateLimitConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            default_rpm: 60,
            burst_size: 10,
            tiers: RateLimitTiers::default(),
        }
    }
}

/// Rate limit tiers for different API key types.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct RateLimitTiers {
    pub free: TierConfig,
    pub developer: TierConfig,
    pub pro: TierConfig,
    pub enterprise: TierConfig,
}

impl Default for RateLimitTiers {
    fn default() -> Self {
        Self {
            free: TierConfig { rpm: 60, burst: 10, concurrent: 5 },
            developer: TierConfig { rpm: 600, burst: 100, concurrent: 20 },
            pro: TierConfig { rpm: 6000, burst: 1000, concurrent: 100 },
            enterprise: TierConfig { rpm: 0, burst: 0, concurrent: 0 }, // Unlimited
        }
    }
}

/// Configuration for a single rate limit tier.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TierConfig {
    /// Requests per minute
    pub rpm: u32,
    /// Burst allowance
    pub burst: u32,
    /// Max concurrent requests
    pub concurrent: u32,
}

/// CORS configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct CorsConfig {
    /// Enable CORS
    pub enabled: bool,

    /// Allowed origins (empty = allow all)
    pub allowed_origins: Vec<String>,

    /// Allowed methods
    pub allowed_methods: Vec<String>,

    /// Allowed headers
    pub allowed_headers: Vec<String>,

    /// Expose headers
    pub expose_headers: Vec<String>,

    /// Allow credentials
    pub allow_credentials: bool,

    /// Max age for preflight cache
    pub max_age: u64,
}

impl Default for CorsConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            allowed_origins: vec![], // Empty = allow all
            allowed_methods: vec![
                "GET".to_string(),
                "POST".to_string(),
                "PUT".to_string(),
                "DELETE".to_string(),
                "PATCH".to_string(),
                "OPTIONS".to_string(),
            ],
            allowed_headers: vec![
                "Content-Type".to_string(),
                "Authorization".to_string(),
                "X-API-Key".to_string(),
                "X-Request-Id".to_string(),
            ],
            expose_headers: vec![
                "X-Request-Id".to_string(),
                "X-RateLimit-Limit".to_string(),
                "X-RateLimit-Remaining".to_string(),
                "X-RateLimit-Reset".to_string(),
            ],
            allow_credentials: true,
            max_age: 3600,
        }
    }
}

/// Backend service endpoints.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ServiceEndpoints {
    /// RPC service endpoint
    pub rpc: String,

    /// Wallet service endpoint
    pub wallet: String,

    /// Storage service endpoint
    pub storage: String,

    /// Database service endpoint
    pub database: String,

    /// DEX service endpoint
    pub dex: String,

    /// IBC bridge service endpoint
    pub ibc: String,

    /// ZK service endpoint
    pub zk: String,

    /// Compiler service endpoint
    pub compiler: String,

    /// Crypto service endpoint
    pub crypto: String,

    /// Mining service endpoint
    pub mining: String,

    /// Governance service endpoint
    pub governance: String,

    /// Economics service endpoint
    pub economics: String,

    /// Hosting service endpoint
    pub hosting: String,
}

impl Default for ServiceEndpoints {
    fn default() -> Self {
        Self {
            rpc: "http://127.0.0.1:16110".to_string(),
            wallet: "http://127.0.0.1:8010".to_string(),
            storage: "http://127.0.0.1:8030".to_string(),
            database: "http://127.0.0.1:8040".to_string(),
            dex: "http://127.0.0.1:8120".to_string(),
            ibc: "http://127.0.0.1:8140".to_string(),
            zk: "http://127.0.0.1:8130".to_string(),
            compiler: "http://127.0.0.1:8150".to_string(),
            crypto: "http://127.0.0.1:8160".to_string(),
            mining: "http://127.0.0.1:8070".to_string(),
            governance: "http://127.0.0.1:8090".to_string(),
            economics: "http://127.0.0.1:8080".to_string(),
            hosting: "http://127.0.0.1:8050".to_string(),
        }
    }
}

/// Metrics configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct MetricsConfig {
    /// Enable metrics endpoint
    pub enabled: bool,

    /// Metrics endpoint path
    pub path: String,

    /// Include detailed histograms
    pub detailed_histograms: bool,
}

impl Default for MetricsConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            path: "/metrics".to_string(),
            detailed_histograms: true,
        }
    }
}

/// Duration serialization helper using humantime format.
mod humantime_serde {
    use serde::{Deserialize, Deserializer, Serializer};
    use std::time::Duration;

    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = humantime::format_duration(*duration).to_string();
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        humantime::parse_duration(&s).map_err(serde::de::Error::custom)
    }
}

impl GatewayConfig {
    /// Load configuration from file.
    pub fn from_file(path: &str) -> anyhow::Result<Self> {
        let settings = config::Config::builder()
            .add_source(config::File::with_name(path))
            .add_source(config::Environment::with_prefix("SYNOR_GATEWAY"))
            .build()?;

        Ok(settings.try_deserialize()?)
    }

    /// Load configuration from environment variables only.
    pub fn from_env() -> anyhow::Result<Self> {
        let settings = config::Config::builder()
            .add_source(config::Environment::with_prefix("SYNOR_GATEWAY"))
            .build()?;

        Ok(settings.try_deserialize().unwrap_or_default())
    }
}
