use crate::history::HistoryRetentionPolicy;
use crate::options::{
    ConnectionRecoveryConfig, HistoryConfig, IdempotencyConfig, PresenceHistoryConfig,
};
use crate::presence_history::PresenceHistoryRetentionPolicy;
use crate::webhook_types::Webhook;
use ahash::AHashMap;
use async_trait::async_trait;
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_aux::field_attributes::{
    deserialize_number_from_string, deserialize_option_number_from_string,
};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppPolicy {
    pub limits: AppLimitsPolicy,
    pub features: AppFeaturesPolicy,
    pub channels: AppChannelsPolicy,
    pub webhooks: Option<Vec<Webhook>>,
    pub idempotency: Option<AppIdempotencyConfig>,
    pub connection_recovery: Option<AppConnectionRecoveryConfig>,
    pub history: Option<AppHistoryConfig>,
    pub presence_history: Option<AppPresenceHistoryConfig>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(default)]
pub struct AppMessageRateLimitConfig {
    pub enabled: bool,
    /// Maximum number of inbound WebSocket messages allowed within `decay_seconds`.
    pub max_attempts: u32,
    /// Sliding window size in seconds. Defaults to 60.
    pub decay_seconds: u64,
    /// If true, the connection is closed when the limit is exceeded.
    pub terminate_on_limit: bool,
}

impl Default for AppMessageRateLimitConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            max_attempts: 60,
            decay_seconds: 60,
            terminate_on_limit: false,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppLimitsPolicy {
    #[serde(deserialize_with = "deserialize_number_from_string")]
    pub max_connections: u32,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_backend_events_per_second: Option<u32>,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    pub max_client_events_per_second: u32,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_read_requests_per_second: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_presence_members_per_channel: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_presence_member_size_in_kb: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_channel_name_length: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_event_channels_at_once: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_event_name_length: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_event_payload_in_kb: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_event_batch_size: Option<u32>,
    /// Sliding window size in seconds for client event rate limiting. Defaults to 1.
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub decay_seconds: Option<u64>,
    /// If true, the connection is closed when the client event rate limit is exceeded.
    /// If false (default), the event is rejected but the connection remains open.
    #[serde(default)]
    pub terminate_on_limit: bool,
    /// Rate limit applied to all inbound WebSocket messages (not just client events).
    #[serde(default)]
    pub message_rate_limit: Option<AppMessageRateLimitConfig>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppFeaturesPolicy {
    pub enable_client_messages: bool,
    pub enable_user_authentication: Option<bool>,
    pub enable_watchlist_events: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppChannelsPolicy {
    #[serde(default, deserialize_with = "deserialize_and_validate_origins")]
    pub allowed_origins: Option<Vec<String>>,
    #[serde(default)]
    pub annotations_enabled: Option<bool>,
    pub channel_delta_compression: Option<AHashMap<String, crate::delta_types::ChannelDeltaConfig>>,
    pub channel_namespaces: Option<Vec<ChannelNamespace>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(default)]
pub struct AppHistoryConfig {
    pub enabled: Option<bool>,
    pub rewind_enabled: Option<bool>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub retention_window_seconds: Option<u64>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_messages_per_channel: Option<usize>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_bytes_per_channel: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(default)]
pub struct NamespaceHistoryConfig {
    pub rewind_enabled: Option<bool>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub retention_window_seconds: Option<u64>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_messages_per_channel: Option<usize>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_bytes_per_channel: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(default)]
pub struct AppPresenceHistoryConfig {
    pub enabled: Option<bool>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub retention_window_seconds: Option<u64>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_events_per_channel: Option<usize>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_bytes_per_channel: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(default)]
pub struct NamespacePresenceHistoryConfig {
    pub enabled: Option<bool>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub retention_window_seconds: Option<u64>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_events_per_channel: Option<usize>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    pub max_bytes_per_channel: Option<u64>,
}

#[derive(Debug, Clone, Copy)]
pub struct AppPolicyRef<'a> {
    pub limits: AppLimitsPolicyRef,
    pub features: AppFeaturesPolicyRef<'a>,
    pub channels: AppChannelsPolicyRef<'a>,
    pub webhooks: Option<&'a [Webhook]>,
    pub idempotency: Option<&'a AppIdempotencyConfig>,
    pub connection_recovery: Option<&'a AppConnectionRecoveryConfig>,
    pub history: Option<&'a AppHistoryConfig>,
    pub presence_history: Option<&'a AppPresenceHistoryConfig>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResolvedHistoryPolicy {
    pub enabled: bool,
    pub rewind_enabled: bool,
    pub max_page_size: usize,
    pub retention_window_seconds: u64,
    pub max_messages_per_channel: Option<usize>,
    pub max_bytes_per_channel: Option<u64>,
}

impl ResolvedHistoryPolicy {
    #[inline]
    pub fn rewind_allowed(self) -> bool {
        self.enabled && self.rewind_enabled
    }

    #[inline]
    pub fn retention(self) -> HistoryRetentionPolicy {
        HistoryRetentionPolicy {
            retention_window_seconds: self.retention_window_seconds,
            max_messages_per_channel: self.max_messages_per_channel,
            max_bytes_per_channel: self.max_bytes_per_channel,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResolvedPresenceHistoryPolicy {
    pub enabled: bool,
    pub max_page_size: usize,
    pub retention_window_seconds: u64,
    pub max_events_per_channel: Option<usize>,
    pub max_bytes_per_channel: Option<u64>,
}

impl ResolvedPresenceHistoryPolicy {
    #[inline]
    pub fn retention(self) -> PresenceHistoryRetentionPolicy {
        PresenceHistoryRetentionPolicy {
            retention_window_seconds: self.retention_window_seconds,
            max_events_per_channel: self.max_events_per_channel,
            max_bytes_per_channel: self.max_bytes_per_channel,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct AppLimitsPolicyRef {
    pub max_connections: u32,
    pub max_backend_events_per_second: Option<u32>,
    pub max_client_events_per_second: u32,
    pub max_read_requests_per_second: Option<u32>,
    pub max_presence_members_per_channel: Option<u32>,
    pub max_presence_member_size_in_kb: Option<u32>,
    pub max_channel_name_length: Option<u32>,
    pub max_event_channels_at_once: Option<u32>,
    pub max_event_name_length: Option<u32>,
    pub max_event_payload_in_kb: Option<u32>,
    pub max_event_batch_size: Option<u32>,
    pub decay_seconds: Option<u64>,
    pub terminate_on_limit: bool,
    pub message_rate_limit: Option<AppMessageRateLimitConfig>,
}

#[derive(Debug, Clone, Copy)]
pub struct AppFeaturesPolicyRef<'a> {
    pub enable_client_messages: bool,
    pub enable_user_authentication: Option<bool>,
    pub enable_watchlist_events: Option<bool>,
    _marker: std::marker::PhantomData<&'a ()>,
}

#[derive(Debug, Clone, Copy)]
pub struct AppChannelsPolicyRef<'a> {
    pub allowed_origins: Option<&'a [String]>,
    pub annotations_enabled: Option<bool>,
    pub channel_delta_compression:
        Option<&'a AHashMap<String, crate::delta_types::ChannelDeltaConfig>>,
    pub channel_namespaces: Option<&'a [ChannelNamespace]>,
}

#[derive(Debug, Clone, Serialize, Default)]
pub struct App {
    pub id: String,
    pub key: String,
    pub secret: String,
    pub enabled: bool,
    #[serde(default)]
    pub policy: AppPolicy,
}

impl App {
    pub fn from_policy(
        id: String,
        key: String,
        secret: String,
        enabled: bool,
        policy: AppPolicy,
    ) -> Self {
        Self {
            id,
            key,
            secret,
            enabled,
            policy,
        }
    }

    pub fn policy(&self) -> AppPolicy {
        self.policy.clone()
    }

    #[inline]
    pub fn policy_ref(&self) -> AppPolicyRef<'_> {
        AppPolicyRef {
            limits: AppLimitsPolicyRef {
                max_connections: self.policy.limits.max_connections,
                max_backend_events_per_second: self.policy.limits.max_backend_events_per_second,
                max_client_events_per_second: self.policy.limits.max_client_events_per_second,
                max_read_requests_per_second: self.policy.limits.max_read_requests_per_second,
                max_presence_members_per_channel: self
                    .policy
                    .limits
                    .max_presence_members_per_channel,
                max_presence_member_size_in_kb: self.policy.limits.max_presence_member_size_in_kb,
                max_channel_name_length: self.policy.limits.max_channel_name_length,
                max_event_channels_at_once: self.policy.limits.max_event_channels_at_once,
                max_event_name_length: self.policy.limits.max_event_name_length,
                max_event_payload_in_kb: self.policy.limits.max_event_payload_in_kb,
                max_event_batch_size: self.policy.limits.max_event_batch_size,
                decay_seconds: self.policy.limits.decay_seconds,
                terminate_on_limit: self.policy.limits.terminate_on_limit,
                message_rate_limit: self.policy.limits.message_rate_limit,
            },
            features: AppFeaturesPolicyRef {
                enable_client_messages: self.policy.features.enable_client_messages,
                enable_user_authentication: self.policy.features.enable_user_authentication,
                enable_watchlist_events: self.policy.features.enable_watchlist_events,
                _marker: std::marker::PhantomData,
            },
            channels: AppChannelsPolicyRef {
                allowed_origins: self.policy.channels.allowed_origins.as_deref(),
                annotations_enabled: self.policy.channels.annotations_enabled,
                channel_delta_compression: self.policy.channels.channel_delta_compression.as_ref(),
                channel_namespaces: self.policy.channels.channel_namespaces.as_deref(),
            },
            webhooks: self.policy.webhooks.as_deref(),
            idempotency: self.policy.idempotency.as_ref(),
            connection_recovery: self.policy.connection_recovery.as_ref(),
            history: self.policy.history.as_ref(),
            presence_history: self.policy.presence_history.as_ref(),
        }
    }

    #[inline]
    pub fn limits(&self) -> AppLimitsPolicy {
        self.policy.limits.clone()
    }

    #[inline]
    pub fn features(&self) -> AppFeaturesPolicy {
        self.policy.features.clone()
    }

    #[inline]
    pub fn channels(&self) -> AppChannelsPolicy {
        self.policy.channels.clone()
    }

    #[inline]
    pub fn policy_mut(&mut self) -> &mut AppPolicy {
        &mut self.policy
    }

    #[inline]
    pub fn watchlist_events_enabled(&self) -> bool {
        self.policy
            .features
            .enable_watchlist_events
            .unwrap_or(false)
    }

    #[inline]
    pub fn client_messages_enabled(&self) -> bool {
        self.policy.features.enable_client_messages
    }

    #[inline]
    pub fn max_connections_limit(&self) -> u32 {
        self.policy.limits.max_connections
    }

    #[inline]
    pub fn user_authentication_enabled(&self) -> bool {
        self.policy
            .features
            .enable_user_authentication
            .unwrap_or(false)
    }

    #[inline]
    pub fn max_channel_name_limit(&self) -> Option<u32> {
        self.policy.limits.max_channel_name_length
    }

    #[inline]
    pub fn max_presence_members_limit(&self) -> Option<u32> {
        self.policy.limits.max_presence_members_per_channel
    }

    #[inline]
    pub fn max_presence_member_size_limit_kb(&self) -> Option<u32> {
        self.policy.limits.max_presence_member_size_in_kb
    }

    #[inline]
    pub fn client_events_per_second_limit(&self) -> u32 {
        self.policy.limits.max_client_events_per_second
    }

    #[inline]
    pub fn client_event_decay_seconds(&self) -> u64 {
        self.policy.limits.decay_seconds.unwrap_or(1)
    }

    #[inline]
    pub fn terminate_on_limit(&self) -> bool {
        self.policy.limits.terminate_on_limit
    }

    #[inline]
    pub fn message_rate_limit(&self) -> Option<&AppMessageRateLimitConfig> {
        self.policy.limits.message_rate_limit.as_ref()
    }

    #[inline]
    pub fn namespaces(&self) -> Option<&[ChannelNamespace]> {
        self.policy.channels.channel_namespaces.as_deref()
    }

    #[inline]
    pub fn event_name_limit(&self) -> Option<u32> {
        self.policy.limits.max_event_name_length
    }

    #[inline]
    pub fn event_payload_limit_kb(&self) -> Option<u32> {
        self.policy.limits.max_event_payload_in_kb
    }

    #[inline]
    pub fn event_channels_at_once_limit(&self) -> Option<u32> {
        self.policy.limits.max_event_channels_at_once
    }

    #[inline]
    pub fn event_batch_size_limit(&self) -> Option<u32> {
        self.policy.limits.max_event_batch_size
    }

    #[inline]
    pub fn allowed_origins_ref(&self) -> Option<&[String]> {
        self.policy.channels.allowed_origins.as_deref()
    }

    #[inline]
    pub fn channel_delta_compression_ref(
        &self,
    ) -> Option<&AHashMap<String, crate::delta_types::ChannelDeltaConfig>> {
        self.policy.channels.channel_delta_compression.as_ref()
    }

    #[inline]
    pub fn webhooks_ref(&self) -> Option<&[Webhook]> {
        self.policy.webhooks.as_deref()
    }

    #[inline]
    pub fn idempotency_override(&self) -> Option<&AppIdempotencyConfig> {
        self.policy.idempotency.as_ref()
    }

    #[inline]
    pub fn connection_recovery_override(&self) -> Option<&AppConnectionRecoveryConfig> {
        self.policy.connection_recovery.as_ref()
    }

    #[inline]
    pub fn history_override(&self) -> Option<&AppHistoryConfig> {
        self.policy.history.as_ref()
    }

    #[inline]
    pub fn presence_history_override(&self) -> Option<&AppPresenceHistoryConfig> {
        self.policy.presence_history.as_ref()
    }

    #[inline]
    pub fn namespace_for_channel(&self, channel: &str) -> Option<&ChannelNamespace> {
        let namespace_name = crate::utils::channel_namespace_name(channel)?;
        self.namespaces()?
            .iter()
            .find(|namespace| namespace.name == namespace_name)
    }

    #[inline]
    pub fn annotations_enabled_for_channel(&self, channel: &str) -> bool {
        self.namespace_for_channel(channel)
            .and_then(|namespace| namespace.annotations_enabled)
            .or(self.policy.channels.annotations_enabled)
            .unwrap_or(false)
    }

    #[inline]
    pub fn resolved_idempotency(&self, global: &IdempotencyConfig) -> IdempotencyConfig {
        match self.idempotency_override() {
            Some(app_config) => IdempotencyConfig {
                enabled: app_config.enabled.unwrap_or(global.enabled),
                ttl_seconds: app_config.ttl_seconds.unwrap_or(global.ttl_seconds),
                max_key_length: global.max_key_length,
            },
            None => global.clone(),
        }
    }

    #[inline]
    pub fn resolved_connection_recovery(
        &self,
        global: &ConnectionRecoveryConfig,
    ) -> ConnectionRecoveryConfig {
        match self.connection_recovery_override() {
            Some(app_config) => ConnectionRecoveryConfig {
                enabled: app_config.enabled.unwrap_or(global.enabled),
                buffer_ttl_seconds: app_config
                    .buffer_ttl_seconds
                    .unwrap_or(global.buffer_ttl_seconds),
                max_buffer_size: app_config.max_buffer_size.unwrap_or(global.max_buffer_size),
            },
            None => global.clone(),
        }
    }

    #[inline]
    pub fn resolved_history(&self, channel: &str, global: &HistoryConfig) -> ResolvedHistoryPolicy {
        let app_config = self.history_override();
        let namespace_config = self
            .namespace_for_channel(channel)
            .and_then(|namespace| namespace.history.as_ref());
        let annotations_require_history = self.annotations_enabled_for_channel(channel);

        ResolvedHistoryPolicy {
            enabled: annotations_require_history
                || app_config
                    .and_then(|config| config.enabled)
                    .unwrap_or(global.enabled),
            rewind_enabled: namespace_config
                .and_then(|config| config.rewind_enabled)
                .or_else(|| app_config.and_then(|config| config.rewind_enabled))
                .unwrap_or(global.rewind_enabled),
            max_page_size: global.max_page_size,
            retention_window_seconds: namespace_config
                .and_then(|config| config.retention_window_seconds)
                .or_else(|| app_config.and_then(|config| config.retention_window_seconds))
                .unwrap_or(global.retention_window_seconds),
            max_messages_per_channel: namespace_config
                .and_then(|config| config.max_messages_per_channel)
                .or_else(|| app_config.and_then(|config| config.max_messages_per_channel))
                .or(global.max_messages_per_channel),
            max_bytes_per_channel: namespace_config
                .and_then(|config| config.max_bytes_per_channel)
                .or_else(|| app_config.and_then(|config| config.max_bytes_per_channel))
                .or(global.max_bytes_per_channel),
        }
    }

    #[inline]
    pub fn resolved_presence_history(
        &self,
        channel: &str,
        global: &PresenceHistoryConfig,
    ) -> ResolvedPresenceHistoryPolicy {
        let app_config = self.presence_history_override();
        let namespace_config = self
            .namespace_for_channel(channel)
            .and_then(|namespace| namespace.presence_history.as_ref());

        ResolvedPresenceHistoryPolicy {
            enabled: namespace_config
                .and_then(|config| config.enabled)
                .or_else(|| app_config.and_then(|config| config.enabled))
                .unwrap_or(global.enabled),
            max_page_size: global.max_page_size,
            retention_window_seconds: namespace_config
                .and_then(|config| config.retention_window_seconds)
                .or_else(|| app_config.and_then(|config| config.retention_window_seconds))
                .unwrap_or(global.retention_window_seconds),
            max_events_per_channel: namespace_config
                .and_then(|config| config.max_events_per_channel)
                .or_else(|| app_config.and_then(|config| config.max_events_per_channel))
                .or(global.max_events_per_channel),
            max_bytes_per_channel: namespace_config
                .and_then(|config| config.max_bytes_per_channel)
                .or_else(|| app_config.and_then(|config| config.max_bytes_per_channel))
                .or(global.max_bytes_per_channel),
        }
    }
}

#[derive(Debug, Deserialize, Default)]
#[serde(default)]
struct AppSerde {
    id: String,
    key: String,
    secret: String,
    enabled: bool,
    #[serde(default)]
    policy: Option<AppPolicy>,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    max_connections: u32,
    enable_client_messages: bool,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_backend_events_per_second: Option<u32>,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    max_client_events_per_second: u32,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_read_requests_per_second: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_presence_members_per_channel: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_presence_member_size_in_kb: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_channel_name_length: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_event_channels_at_once: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_event_name_length: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_event_payload_in_kb: Option<u32>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    max_event_batch_size: Option<u32>,
    #[serde(default)]
    enable_user_authentication: Option<bool>,
    #[serde(default)]
    webhooks: Option<Vec<Webhook>>,
    #[serde(default)]
    enable_watchlist_events: Option<bool>,
    #[serde(default, deserialize_with = "deserialize_and_validate_origins")]
    allowed_origins: Option<Vec<String>>,
    #[serde(default)]
    annotations_enabled: Option<bool>,
    #[serde(default)]
    channel_delta_compression: Option<AHashMap<String, crate::delta_types::ChannelDeltaConfig>>,
    #[serde(default)]
    idempotency: Option<AppIdempotencyConfig>,
    #[serde(default)]
    connection_recovery: Option<AppConnectionRecoveryConfig>,
    #[serde(default)]
    channel_namespaces: Option<Vec<ChannelNamespace>>,
    #[serde(default, deserialize_with = "deserialize_option_number_from_string")]
    decay_seconds: Option<u64>,
    #[serde(default)]
    terminate_on_limit: bool,
    #[serde(default)]
    message_rate_limit: Option<AppMessageRateLimitConfig>,
}

impl<'de> Deserialize<'de> for App {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let app = AppSerde::deserialize(deserializer)?;

        if let Some(policy) = app.policy {
            return Ok(Self {
                id: app.id,
                key: app.key,
                secret: app.secret,
                enabled: app.enabled,
                policy,
            });
        }

        Ok(Self::from_policy(
            app.id,
            app.key,
            app.secret,
            app.enabled,
            AppPolicy {
                limits: AppLimitsPolicy {
                    max_connections: app.max_connections,
                    max_backend_events_per_second: app.max_backend_events_per_second,
                    max_client_events_per_second: app.max_client_events_per_second,
                    max_read_requests_per_second: app.max_read_requests_per_second,
                    max_presence_members_per_channel: app.max_presence_members_per_channel,
                    max_presence_member_size_in_kb: app.max_presence_member_size_in_kb,
                    max_channel_name_length: app.max_channel_name_length,
                    max_event_channels_at_once: app.max_event_channels_at_once,
                    max_event_name_length: app.max_event_name_length,
                    max_event_payload_in_kb: app.max_event_payload_in_kb,
                    max_event_batch_size: app.max_event_batch_size,
                    decay_seconds: app.decay_seconds,
                    terminate_on_limit: app.terminate_on_limit,
                    message_rate_limit: app.message_rate_limit,
                },
                features: AppFeaturesPolicy {
                    enable_client_messages: app.enable_client_messages,
                    enable_user_authentication: app.enable_user_authentication,
                    enable_watchlist_events: app.enable_watchlist_events,
                },
                channels: AppChannelsPolicy {
                    allowed_origins: app.allowed_origins,
                    annotations_enabled: app.annotations_enabled,
                    channel_delta_compression: app.channel_delta_compression,
                    channel_namespaces: app.channel_namespaces,
                },
                webhooks: app.webhooks,
                idempotency: app.idempotency,
                connection_recovery: app.connection_recovery,
                history: None,
                presence_history: None,
            },
        ))
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct ChannelNamespace {
    pub name: String,
    pub channel_name_pattern: Option<String>,
    pub max_channel_name_length: Option<u32>,
    #[serde(default)]
    pub annotations_enabled: Option<bool>,
    #[serde(default)]
    pub allow_user_limited_channels: Option<bool>,
    #[serde(default)]
    pub allow_subscribe_for_client: Option<bool>,
    #[serde(default)]
    pub allow_publish_for_client: Option<bool>,
    #[serde(default)]
    pub allow_presence_for_client: Option<bool>,
    #[serde(default)]
    pub history: Option<NamespaceHistoryConfig>,
    #[serde(default)]
    pub presence_history: Option<NamespacePresenceHistoryConfig>,
}

impl ChannelNamespace {
    pub fn validate(&self) -> Result<(), String> {
        if self.name.is_empty() {
            return Err("channel namespace name must not be empty".to_string());
        }

        if self.name.contains(':') {
            return Err(format!(
                "channel namespace '{}' must not contain ':'",
                self.name
            ));
        }

        if let Some(pattern) = &self.channel_name_pattern {
            Regex::new(pattern).map_err(|e| {
                format!(
                    "invalid channel_name_pattern for namespace '{}': {e}",
                    self.name
                )
            })?;
        }

        if let Some(max_len) = self.max_channel_name_length
            && max_len == 0
        {
            return Err(format!(
                "max_channel_name_length for namespace '{}' must be greater than 0",
                self.name
            ));
        }

        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppIdempotencyConfig {
    pub enabled: Option<bool>,
    pub ttl_seconds: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct AppConnectionRecoveryConfig {
    pub enabled: Option<bool>,
    pub buffer_ttl_seconds: Option<u64>,
    pub max_buffer_size: Option<usize>,
}

fn deserialize_and_validate_origins<'de, D>(
    deserializer: D,
) -> Result<Option<Vec<String>>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::de::Error;
    let value = Option::<Vec<String>>::deserialize(deserializer)?;

    if let Some(ref origins) = value
        && let Err(validation_error) =
            crate::origin_validation::OriginValidator::validate_patterns(origins)
    {
        return Err(D::Error::custom(format!(
            "Origin pattern validation failed: {}",
            validation_error
        )));
    }

    Ok(value)
}

#[async_trait]
pub trait AppManager: Send + Sync + 'static {
    async fn init(&self) -> crate::error::Result<()>;
    async fn create_app(&self, config: App) -> crate::error::Result<()>;
    async fn update_app(&self, config: App) -> crate::error::Result<()>;
    async fn delete_app(&self, app_id: &str) -> crate::error::Result<()>;
    async fn get_apps(&self) -> crate::error::Result<Vec<App>>;
    async fn find_by_key(&self, key: &str) -> crate::error::Result<Option<App>>;
    async fn find_by_id(&self, app_id: &str) -> crate::error::Result<Option<App>>;
    async fn check_health(&self) -> crate::error::Result<()>;
}

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

    fn test_app_json(limits_overrides: &str, policy_suffix: &str) -> String {
        format!(
            r#"{{"id":"test","key":"key","secret":"secret","enabled":true,"policy":{{"limits":{{"max_connections":100,"max_client_events_per_second":100{limits_overrides}}},"features":{{"enable_client_messages":false}}{policy_suffix}}}}}"#
        )
    }

    #[test]
    fn deserialize_optional_numbers_from_integers() {
        let json = test_app_json(
            r#","max_presence_members_per_channel":100,"max_event_payload_in_kb":64"#,
            "",
        );
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(
            app.policy.limits.max_presence_members_per_channel,
            Some(100)
        );
        assert_eq!(app.policy.limits.max_event_payload_in_kb, Some(64));
    }

    #[test]
    fn deserialize_optional_numbers_from_strings() {
        let json = test_app_json(
            r#","max_presence_members_per_channel":"100","max_event_payload_in_kb":"64""#,
            "",
        );
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(
            app.policy.limits.max_presence_members_per_channel,
            Some(100)
        );
        assert_eq!(app.policy.limits.max_event_payload_in_kb, Some(64));
    }

    #[test]
    fn deserialize_optional_numbers_from_null() {
        let json = test_app_json(r#","max_presence_members_per_channel":null"#, "");
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(app.policy.limits.max_presence_members_per_channel, None);
    }

    #[test]
    fn deserialize_optional_numbers_missing_fields() {
        let json = test_app_json("", "");
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(app.policy.limits.max_presence_members_per_channel, None);
        assert_eq!(app.policy.limits.max_event_payload_in_kb, None);
    }

    #[test]
    fn cache_round_trip_preserves_optional_numbers() {
        let json = test_app_json(
            r#","max_presence_members_per_channel":100,"max_backend_events_per_second":50,"max_channel_name_length":200"#,
            "",
        );
        let app: App = sonic_rs::from_str(&json).unwrap();

        let cached = sonic_rs::to_string(&app).unwrap();
        let restored: App = sonic_rs::from_str(&cached).unwrap();

        assert_eq!(
            restored.policy.limits.max_presence_members_per_channel,
            Some(100)
        );
        assert_eq!(
            restored.policy.limits.max_backend_events_per_second,
            Some(50)
        );
        assert_eq!(restored.policy.limits.max_channel_name_length, Some(200));
    }

    #[test]
    fn cache_round_trip_preserves_flat_webhook_headers() {
        let json = test_app_json(
            "",
            r#","webhooks":[{"url":"https://example.com/hook","event_types":["channel_vacated"],"headers":{"X-HOOK-KEY":"secret"},"filter":{"channel_prefix":"private-"}}]"#,
        );
        let app: App = sonic_rs::from_str(&json).unwrap();

        let cached = sonic_rs::to_string(&app).unwrap();
        assert!(cached.contains(r#""headers":{"X-HOOK-KEY":"secret"}"#));
        assert!(!cached.contains(r#""headers":{"headers":"#));

        let restored: App = sonic_rs::from_str(&cached).unwrap();
        let webhooks = restored.policy.webhooks.unwrap();
        let webhook_headers = webhooks[0].headers.as_ref().unwrap();

        assert_eq!(
            webhook_headers.headers.get("X-HOOK-KEY"),
            Some(&"secret".to_string())
        );
    }

    #[test]
    fn deserialize_channel_namespaces() {
        let json = test_app_json(
            "",
            r#","channels":{"channel_namespaces":[{"name":"chat","channel_name_pattern":"^chat:[a-z0-9-]+$","max_channel_name_length":64,"allow_user_limited_channels":true,"allow_subscribe_for_client":true,"allow_publish_for_client":false,"allow_presence_for_client":true}]}"#,
        );
        let app: App = sonic_rs::from_str(&json).unwrap();
        let namespaces = app.policy.channels.channel_namespaces.unwrap();
        assert_eq!(namespaces.len(), 1);
        assert_eq!(namespaces[0].name, "chat");
        assert_eq!(namespaces[0].max_channel_name_length, Some(64));
        assert_eq!(namespaces[0].allow_user_limited_channels, Some(true));
        assert_eq!(namespaces[0].allow_subscribe_for_client, Some(true));
        assert_eq!(namespaces[0].allow_publish_for_client, Some(false));
        assert_eq!(namespaces[0].allow_presence_for_client, Some(true));
        assert_eq!(namespaces[0].annotations_enabled, None);
    }

    #[test]
    fn deserialize_channel_annotation_flags() {
        let json = test_app_json(
            "",
            r#","channels":{"annotations_enabled":true,"channel_namespaces":[{"name":"chat","annotations_enabled":false}]}"#,
        );
        let app: App = sonic_rs::from_str(&json).unwrap();

        assert_eq!(app.policy.channels.annotations_enabled, Some(true));
        assert_eq!(
            app.policy.channels.channel_namespaces.as_ref().unwrap()[0].annotations_enabled,
            Some(false)
        );
        assert!(!app.annotations_enabled_for_channel("chat:room-1"));
        assert!(app.annotations_enabled_for_channel("news:room-1"));
    }

    #[test]
    fn deserialize_legacy_flat_shape_for_compatibility() {
        let json = r#"{"id":"test","key":"key","secret":"secret","enabled":true,"max_connections":"100","enable_client_messages":false,"max_client_events_per_second":"100","allowed_origins":["https://example.com"]}"#;
        let app: App = sonic_rs::from_str(json).unwrap();
        assert_eq!(app.policy.limits.max_connections, 100);
        assert!(!app.policy.features.enable_client_messages);
        assert_eq!(
            app.policy.channels.allowed_origins,
            Some(vec!["https://example.com".to_string()])
        );
    }

    #[test]
    fn validate_channel_namespace_rejects_invalid_regex() {
        let ns = ChannelNamespace {
            name: "chat".to_string(),
            channel_name_pattern: Some("[".to_string()),
            max_channel_name_length: None,
            annotations_enabled: None,
            allow_user_limited_channels: None,
            allow_subscribe_for_client: None,
            allow_publish_for_client: None,
            allow_presence_for_client: None,
            history: None,
            presence_history: None,
        };
        assert!(ns.validate().is_err());
    }

    #[test]
    fn app_policy_round_trip_preserves_grouped_configuration() {
        let policy = AppPolicy {
            limits: AppLimitsPolicy {
                max_connections: 250,
                max_backend_events_per_second: Some(50),
                max_client_events_per_second: 75,
                max_read_requests_per_second: Some(100),
                max_presence_members_per_channel: Some(25),
                max_presence_member_size_in_kb: Some(4),
                max_channel_name_length: Some(120),
                max_event_channels_at_once: Some(20),
                max_event_name_length: Some(80),
                max_event_payload_in_kb: Some(64),
                max_event_batch_size: Some(10),
                decay_seconds: Some(60),
                terminate_on_limit: true,
                message_rate_limit: Some(AppMessageRateLimitConfig {
                    enabled: true,
                    max_attempts: 100,
                    decay_seconds: 60,
                    terminate_on_limit: true,
                }),
            },
            features: AppFeaturesPolicy {
                enable_client_messages: true,
                enable_user_authentication: Some(true),
                enable_watchlist_events: Some(true),
            },
            channels: AppChannelsPolicy {
                allowed_origins: Some(vec!["https://example.com".to_string()]),
                annotations_enabled: Some(true),
                channel_delta_compression: None,
                channel_namespaces: Some(vec![ChannelNamespace {
                    name: "chat".to_string(),
                    channel_name_pattern: Some("^chat:[a-z0-9-]+$".to_string()),
                    max_channel_name_length: Some(64),
                    annotations_enabled: Some(true),
                    allow_user_limited_channels: Some(true),
                    allow_subscribe_for_client: Some(true),
                    allow_publish_for_client: Some(true),
                    allow_presence_for_client: Some(true),
                    history: None,
                    presence_history: None,
                }]),
            },
            webhooks: None,
            idempotency: Some(AppIdempotencyConfig {
                enabled: Some(true),
                ttl_seconds: Some(300),
            }),
            connection_recovery: Some(AppConnectionRecoveryConfig {
                enabled: Some(true),
                buffer_ttl_seconds: Some(120),
                max_buffer_size: Some(50),
            }),
            history: Some(AppHistoryConfig {
                enabled: Some(true),
                rewind_enabled: Some(false),
                retention_window_seconds: Some(120),
                max_messages_per_channel: Some(10),
                max_bytes_per_channel: Some(1024),
            }),
            presence_history: None,
        };

        let app = App::from_policy(
            "test".to_string(),
            "key".to_string(),
            "secret".to_string(),
            true,
            policy.clone(),
        );

        assert_eq!(app.policy().limits.max_connections, 250);
        assert!(app.policy().features.enable_client_messages);
        assert_eq!(
            app.policy().channels.channel_namespaces.unwrap()[0].name,
            "chat"
        );
        assert!(app.policy().channels.annotations_enabled.unwrap());
        assert_eq!(app.policy().idempotency.unwrap().ttl_seconds, Some(300));
        assert_eq!(
            app.policy().connection_recovery.unwrap().max_buffer_size,
            Some(50)
        );
        assert_eq!(
            app.policy().history.unwrap().max_messages_per_channel,
            Some(10)
        );
        assert_eq!(app.policy().limits.decay_seconds, Some(60));
        assert!(app.policy().limits.terminate_on_limit);
    }

    #[test]
    fn resolved_history_uses_global_defaults_without_overrides() {
        let app = App::default();
        let global = HistoryConfig::default();

        let resolved = app.resolved_history("chat:room-1", &global);

        assert_eq!(
            resolved,
            ResolvedHistoryPolicy {
                enabled: false,
                rewind_enabled: true,
                max_page_size: 100,
                retention_window_seconds: 86400,
                max_messages_per_channel: None,
                max_bytes_per_channel: None,
            }
        );
        assert!(!resolved.rewind_allowed());
    }

    #[test]
    fn resolved_history_applies_app_overrides() {
        let app = App::from_policy(
            "app".to_string(),
            "key".to_string(),
            "secret".to_string(),
            true,
            AppPolicy {
                history: Some(AppHistoryConfig {
                    enabled: Some(true),
                    rewind_enabled: Some(false),
                    retention_window_seconds: Some(30),
                    max_messages_per_channel: Some(5),
                    max_bytes_per_channel: Some(512),
                }),
                ..Default::default()
            },
        );
        let global = HistoryConfig::default();

        let resolved = app.resolved_history("chat:room-1", &global);

        assert!(resolved.enabled);
        assert!(!resolved.rewind_enabled);
        assert_eq!(resolved.retention_window_seconds, 30);
        assert_eq!(resolved.max_messages_per_channel, Some(5));
        assert_eq!(resolved.max_bytes_per_channel, Some(512));
        assert!(!resolved.rewind_allowed());
    }

    #[test]
    fn resolved_history_namespace_overrides_beat_app_and_global() {
        let app = App::from_policy(
            "app".to_string(),
            "key".to_string(),
            "secret".to_string(),
            true,
            AppPolicy {
                history: Some(AppHistoryConfig {
                    enabled: Some(true),
                    rewind_enabled: Some(false),
                    retention_window_seconds: Some(60),
                    max_messages_per_channel: Some(2),
                    max_bytes_per_channel: Some(256),
                }),
                channels: AppChannelsPolicy {
                    channel_namespaces: Some(vec![ChannelNamespace {
                        name: "chat".to_string(),
                        channel_name_pattern: None,
                        max_channel_name_length: None,
                        annotations_enabled: None,
                        allow_user_limited_channels: None,
                        allow_subscribe_for_client: None,
                        allow_publish_for_client: None,
                        allow_presence_for_client: None,
                        history: Some(NamespaceHistoryConfig {
                            rewind_enabled: Some(true),
                            retention_window_seconds: Some(600),
                            max_messages_per_channel: Some(8),
                            max_bytes_per_channel: Some(4096),
                        }),
                        presence_history: None,
                    }]),
                    ..Default::default()
                },
                ..Default::default()
            },
        );
        let global = HistoryConfig {
            enabled: false,
            ..HistoryConfig::default()
        };

        let resolved = app.resolved_history("chat:room-1", &global);

        assert!(resolved.enabled);
        assert!(resolved.rewind_enabled);
        assert_eq!(resolved.retention_window_seconds, 600);
        assert_eq!(resolved.max_messages_per_channel, Some(8));
        assert_eq!(resolved.max_bytes_per_channel, Some(4096));
        assert!(resolved.rewind_allowed());
    }

    #[test]
    fn resolved_history_annotations_enabled_forces_persistence() {
        let app = App::from_policy(
            "app".to_string(),
            "key".to_string(),
            "secret".to_string(),
            true,
            AppPolicy {
                history: Some(AppHistoryConfig {
                    enabled: Some(false),
                    rewind_enabled: Some(false),
                    retention_window_seconds: Some(60),
                    max_messages_per_channel: Some(2),
                    max_bytes_per_channel: Some(256),
                }),
                channels: AppChannelsPolicy {
                    annotations_enabled: Some(false),
                    channel_namespaces: Some(vec![ChannelNamespace {
                        name: "chat".to_string(),
                        annotations_enabled: Some(true),
                        ..Default::default()
                    }]),
                    ..Default::default()
                },
                ..Default::default()
            },
        );
        let global = HistoryConfig {
            enabled: false,
            ..HistoryConfig::default()
        };

        let enabled = app.resolved_history("chat:room-1", &global);
        let disabled = app.resolved_history("news:room-1", &global);

        assert!(enabled.enabled);
        assert_eq!(enabled.retention_window_seconds, 60);
        assert!(!disabled.enabled);
    }

    #[test]
    fn deserialize_decay_seconds_and_terminate_on_limit() {
        let json = test_app_json(r#","decay_seconds":30,"terminate_on_limit":true"#, "");
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(app.policy.limits.decay_seconds, Some(30));
        assert!(app.policy.limits.terminate_on_limit);
        assert_eq!(app.client_event_decay_seconds(), 30);
        assert!(app.terminate_on_limit());
    }

    #[test]
    fn decay_seconds_defaults_to_one_when_absent() {
        let json = test_app_json("", "");
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(app.policy.limits.decay_seconds, None);
        assert_eq!(app.client_event_decay_seconds(), 1);
        assert!(!app.terminate_on_limit());
    }

    #[test]
    fn deserialize_decay_seconds_from_string() {
        let json = test_app_json(r#","decay_seconds":"60""#, "");
        let app: App = sonic_rs::from_str(&json).unwrap();
        assert_eq!(app.policy.limits.decay_seconds, Some(60));
        assert_eq!(app.client_event_decay_seconds(), 60);
    }
}
