use std::time::Duration;

/// JWT authorization configuration.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthConfig {
    /// HS256 secret used to validate bearer tokens.
    pub secret: String,
    /// Exact request paths that bypass token validation.
    pub public_paths: Vec<String>,
}

impl AuthConfig {
    /// Returns whether `path` is public.
    pub fn is_public(&self, path: &str) -> bool {
        self.public_paths.iter().any(|item| item == path)
    }
}

/// REST service runtime configuration.
#[derive(Debug, Clone)]
pub struct RestConfig {
    /// Service name used by logs and traces.
    pub name: String,
    /// Request timeout.
    pub timeout: Duration,
    /// Maximum accepted request body size.
    pub max_body_bytes: usize,
    /// Optional JWT authorization configuration.
    pub auth: Option<AuthConfig>,
    /// Default middleware controls.
    pub middlewares: RestMiddlewareConfig,
    /// Metrics registry used when metrics middleware is enabled.
    #[cfg(feature = "observability")]
    pub metrics_registry: Option<crate::observability::MetricsRegistry>,
}

impl Default for RestConfig {
    fn default() -> Self {
        Self {
            name: "rs-zero".to_string(),
            timeout: Duration::from_secs(5),
            max_body_bytes: 1024 * 1024,
            auth: None,
            middlewares: RestMiddlewareConfig::default(),
            #[cfg(feature = "observability")]
            metrics_registry: None,
        }
    }
}

impl RestConfig {
    /// Creates a REST config with go-zero style production protection enabled.
    pub fn go_zero_defaults(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            middlewares: RestMiddlewareConfig {
                resilience: RestResilienceConfig::go_zero_defaults(),
                metrics: RestMetricsConfig { enabled: true },
            },
            ..Self::default()
        }
    }
}

/// Controls optional REST middleware.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct RestMiddlewareConfig {
    /// Resilience middleware configuration.
    pub resilience: RestResilienceConfig,
    /// Metrics middleware configuration.
    pub metrics: RestMetricsConfig,
}

/// REST resilience middleware configuration.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RestResilienceConfig {
    /// Whether route-level circuit breaking is enabled.
    pub breaker_enabled: bool,
    /// Consecutive failures that open a route breaker.
    pub breaker_failure_threshold: u32,
    /// Time before an open breaker allows a half-open trial call.
    pub breaker_reset_timeout: Duration,
    /// Whether the route breaker uses Google SRE style adaptive rejection.
    pub breaker_sre_enabled: bool,
    /// SRE breaker multiplier in millis. `1500` means `k = 1.5`.
    pub breaker_sre_k_millis: u32,
    /// Minimum total samples before SRE breaker rejection can start.
    pub breaker_sre_protection: u64,
    /// Optional maximum number of in-flight requests.
    pub max_concurrency: Option<usize>,
    /// Optional timeout overriding [`RestConfig::timeout`] for the default stack.
    pub request_timeout: Option<Duration>,
    /// Whether adaptive request shedding is enabled.
    pub shedding_enabled: bool,
    /// Maximum in-flight requests used by the adaptive shedder.
    pub shedding_max_in_flight: Option<usize>,
    /// Minimum request samples before latency-based shedding can reject.
    pub shedding_min_request_count: u64,
    /// Average latency threshold used by adaptive shedding.
    pub shedding_max_latency: Duration,
    /// CPU usage threshold used by adaptive shedding, where `1000` means 100%.
    pub shedding_cpu_threshold_millis: u32,
    /// Cool-off duration after a recent adaptive shedder drop.
    pub shedding_cool_off: Duration,
    /// Number of buckets used by adaptive shedder rolling windows.
    pub shedding_window_buckets: usize,
    /// Duration represented by each adaptive shedder bucket.
    pub shedding_window_bucket_duration: Duration,
    /// Optional Redis-backed REST limiter.
    #[cfg(all(feature = "resil", feature = "cache-redis"))]
    pub rate_limiter: RestRateLimiterConfig,
}

impl Default for RestResilienceConfig {
    fn default() -> Self {
        Self {
            breaker_enabled: false,
            breaker_failure_threshold: 5,
            breaker_reset_timeout: Duration::from_secs(30),
            breaker_sre_enabled: false,
            breaker_sre_k_millis: 1500,
            breaker_sre_protection: 5,
            max_concurrency: None,
            request_timeout: None,
            shedding_enabled: false,
            shedding_max_in_flight: None,
            shedding_min_request_count: 20,
            shedding_max_latency: Duration::from_millis(250),
            shedding_cpu_threshold_millis: 900,
            shedding_cool_off: Duration::from_secs(1),
            shedding_window_buckets: 50,
            shedding_window_bucket_duration: Duration::from_millis(100),
            #[cfg(all(feature = "resil", feature = "cache-redis"))]
            rate_limiter: RestRateLimiterConfig::default(),
        }
    }
}

impl RestResilienceConfig {
    /// Returns a go-zero style resilience profile for production services.
    pub fn go_zero_defaults() -> Self {
        Self {
            breaker_enabled: true,
            breaker_sre_enabled: true,
            max_concurrency: Some(1024),
            request_timeout: Some(Duration::from_secs(5)),
            shedding_enabled: true,
            shedding_max_in_flight: Some(1024),
            shedding_min_request_count: 20,
            shedding_max_latency: Duration::from_millis(250),
            ..Self::default()
        }
    }
}

/// Redis-backed REST limiter selection.
#[cfg(all(feature = "resil", feature = "cache-redis"))]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum RestRateLimiterConfig {
    /// No Redis limiter is applied.
    #[default]
    Disabled,
    /// Token-bucket limiter backed by Redis.
    RedisToken(crate::resil::RedisTokenLimiterConfig),
    /// Fixed-window period limiter backed by Redis.
    RedisPeriod(crate::resil::RedisPeriodLimiterConfig),
}

/// REST metrics middleware configuration.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct RestMetricsConfig {
    /// Whether metrics middleware should be applied by the default stack.
    pub enabled: bool,
}
