//! Application configuration for chattyness.
//!
//! Provides TOML-based configuration with sensible defaults for WebSocket
//! timeouts and stale member cleanup.

use serde::Deserialize;
use std::path::Path;

use chattyness_error::AppError;

/// Root configuration structure.
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct AppConfig {
    /// WebSocket-related configuration.
    pub websocket: WebSocketConfig,
    /// Stale member cleanup configuration.
    pub cleanup: CleanupConfig,
}

/// WebSocket configuration.
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct WebSocketConfig {
    /// Timeout for receiving messages from client before considering connection dead (seconds).
    pub recv_timeout_secs: u64,
    /// Interval for client to send ping to keep connection alive (seconds).
    pub client_ping_interval_secs: u64,
}

/// Cleanup configuration for stale instance members.
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct CleanupConfig {
    /// Interval for running stale member cleanup job (seconds).
    pub reap_interval_secs: u64,
    /// Members with no activity for this duration are considered stale (seconds).
    pub stale_threshold_secs: u64,
    /// Clear all instance_members on server startup (recommended for single-server deployments).
    pub clear_on_startup: bool,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            websocket: WebSocketConfig::default(),
            cleanup: CleanupConfig::default(),
        }
    }
}

impl Default for WebSocketConfig {
    fn default() -> Self {
        Self {
            recv_timeout_secs: 45,
            client_ping_interval_secs: 30,
        }
    }
}

impl Default for CleanupConfig {
    fn default() -> Self {
        Self {
            reap_interval_secs: 120,
            stale_threshold_secs: 120,
            clear_on_startup: true,
        }
    }
}

impl AppConfig {
    /// Load configuration from a TOML file.
    ///
    /// If no path is provided, returns default configuration.
    pub fn load(path: Option<&Path>) -> Result<Self, AppError> {
        match path {
            Some(p) => {
                let content = std::fs::read_to_string(p).map_err(|e| {
                    AppError::Internal(format!("Failed to read config file {:?}: {}", p, e))
                })?;
                toml::from_str(&content).map_err(|e| {
                    AppError::Internal(format!("Failed to parse config file {:?}: {}", p, e))
                })
            }
            None => Ok(Self::default()),
        }
    }
}
