//! Configuration types for the LiveSpeech SDK

use std::time::Duration;

/// Pipeline mode for audio processing
/// 
/// - `Live`: Uses Gemini Live API for end-to-end audio conversation (default, lower latency)
/// - `Composed`: Uses separate STT + LLM + TTS services (more customizable)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PipelineMode {
    /// Gemini Live API - direct audio-to-audio conversation
    #[default]
    Live,
    /// Composed pipeline - separate STT + LLM + TTS services
    Composed,
}

impl PipelineMode {
    /// Get the string representation for the protocol
    pub fn as_str(&self) -> &'static str {
        match self {
            PipelineMode::Live => "live",
            PipelineMode::Composed => "composed",
        }
    }
}

impl std::fmt::Display for PipelineMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Available regions for LiveSpeech service
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Region {
    /// Asia Pacific (Seoul)
    ApNortheast2,
    /// US West (Oregon) - Coming soon
    UsWest2,
}

impl Region {
    /// Get the WebSocket endpoint URL for this region
    pub fn endpoint(&self) -> &'static str {
        match self {
            Region::ApNortheast2 => "wss://talk.drawdream.co.kr",
            Region::UsWest2 => "wss://talk.drawdream.ca", // Coming soon
        }
    }

    /// Get the region identifier string
    pub fn as_str(&self) -> &'static str {
        match self {
            Region::ApNortheast2 => "ap-northeast-2",
            Region::UsWest2 => "us-west-2",
        }
    }
}

impl std::fmt::Display for Region {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Configuration for the LiveSpeech client
#[derive(Debug, Clone)]
pub struct Config {
    /// WebSocket endpoint URL
    pub endpoint: String,
    /// API key for authentication
    pub api_key: String,
    /// Connection timeout
    pub connection_timeout: Duration,
    /// Enable automatic reconnection
    pub auto_reconnect: bool,
    /// Maximum reconnection attempts
    pub max_reconnect_attempts: u32,
    /// Base delay between reconnection attempts
    pub reconnect_delay: Duration,
    /// Enable debug logging
    pub debug: bool,
}

impl Config {
    /// Create a new config builder
    pub fn builder() -> ConfigBuilder {
        ConfigBuilder::default()
    }
}

/// Builder for Config
#[derive(Debug, Default)]
pub struct ConfigBuilder {
    region: Option<Region>,
    api_key: Option<String>,
    connection_timeout: Option<Duration>,
    auto_reconnect: Option<bool>,
    max_reconnect_attempts: Option<u32>,
    reconnect_delay: Option<Duration>,
    debug: Option<bool>,
}

impl ConfigBuilder {
    /// Set the region (required)
    /// 
    /// This will automatically resolve the correct endpoint URL for the region.
    /// 
    /// # Example
    /// ```
    /// use livespeech_sdk::{Config, Region};
    /// 
    /// let config = Config::builder()
    ///     .region(Region::ApNortheast2)
    ///     .api_key("your-api-key")
    ///     .build()
    ///     .unwrap();
    /// ```
    pub fn region(mut self, region: Region) -> Self {
        self.region = Some(region);
        self
    }

    /// Set the API key (required)
    pub fn api_key(mut self, api_key: impl Into<String>) -> Self {
        self.api_key = Some(api_key.into());
        self
    }

    /// Set the connection timeout
    pub fn connection_timeout(mut self, timeout: Duration) -> Self {
        self.connection_timeout = Some(timeout);
        self
    }

    /// Enable or disable auto reconnection
    pub fn auto_reconnect(mut self, enabled: bool) -> Self {
        self.auto_reconnect = Some(enabled);
        self
    }

    /// Set maximum reconnection attempts
    pub fn max_reconnect_attempts(mut self, attempts: u32) -> Self {
        self.max_reconnect_attempts = Some(attempts);
        self
    }

    /// Set base reconnection delay
    pub fn reconnect_delay(mut self, delay: Duration) -> Self {
        self.reconnect_delay = Some(delay);
        self
    }

    /// Enable or disable debug mode
    pub fn debug(mut self, enabled: bool) -> Self {
        self.debug = Some(enabled);
        self
    }

    /// Build the Config
    pub fn build(self) -> Result<Config, ConfigError> {
        let region = self.region.ok_or(ConfigError::MissingRegion)?;
        let api_key = self.api_key.ok_or(ConfigError::MissingApiKey)?;

        Ok(Config {
            endpoint: region.endpoint().to_string(),
            api_key,
            connection_timeout: self.connection_timeout.unwrap_or(Duration::from_secs(30)),
            auto_reconnect: self.auto_reconnect.unwrap_or(true),
            max_reconnect_attempts: self.max_reconnect_attempts.unwrap_or(5),
            reconnect_delay: self.reconnect_delay.unwrap_or(Duration::from_secs(1)),
            debug: self.debug.unwrap_or(false),
        })
    }
}

/// Configuration errors
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
    #[error("region is required")]
    MissingRegion,
    #[error("api_key is required")]
    MissingApiKey,
}

/// Session configuration
#[derive(Debug, Clone, Default)]
pub struct SessionConfig {
    /// System prompt for the AI (optional)
    pub pre_prompt: Option<String>,
    /// Language code for speech recognition (e.g., "en-US", "ko-KR")
    pub language: Option<String>,
    /// Pipeline mode for audio processing (default: Live)
    pub pipeline_mode: PipelineMode,
}

impl SessionConfig {
    /// Create a new session config with a pre-prompt
    pub fn new(pre_prompt: impl Into<String>) -> Self {
        Self {
            pre_prompt: Some(pre_prompt.into()),
            language: None,
            pipeline_mode: PipelineMode::Live,
        }
    }

    /// Create an empty session config
    pub fn empty() -> Self {
        Self::default()
    }

    /// Set the language for speech recognition
    pub fn with_language(mut self, language: impl Into<String>) -> Self {
        self.language = Some(language.into());
        self
    }

    /// Set the pre-prompt
    pub fn with_pre_prompt(mut self, pre_prompt: impl Into<String>) -> Self {
        self.pre_prompt = Some(pre_prompt.into());
        self
    }

    /// Set the pipeline mode
    /// 
    /// - `PipelineMode::Live`: Gemini Live API for direct audio conversation (default, lower latency)
    /// - `PipelineMode::Composed`: Separate STT + LLM + TTS services (more customizable)
    pub fn with_pipeline_mode(mut self, mode: PipelineMode) -> Self {
        self.pipeline_mode = mode;
        self
    }
}

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

    #[test]
    fn test_config_builder_with_region() {
        let config = Config::builder()
            .region(Region::ApNortheast2)
            .api_key("test-key")
            .debug(true)
            .build()
            .unwrap();

        assert_eq!(config.endpoint, "wss://talk.drawdream.co.kr");
        assert_eq!(config.api_key, "test-key");
        assert!(config.debug);
    }

    #[test]
    fn test_config_builder_missing_region() {
        let result = Config::builder().api_key("test-key").build();
        assert!(matches!(result, Err(ConfigError::MissingRegion)));
    }

    #[test]
    fn test_config_builder_missing_api_key() {
        let result = Config::builder().region(Region::ApNortheast2).build();
        assert!(matches!(result, Err(ConfigError::MissingApiKey)));
    }

    #[test]
    fn test_region_endpoint() {
        assert_eq!(Region::ApNortheast2.endpoint(), "wss://talk.drawdream.co.kr");
        assert_eq!(Region::ApNortheast2.as_str(), "ap-northeast-2");
    }

    #[test]
    fn test_session_config() {
        let config = SessionConfig::new("You are a helpful assistant");
        assert_eq!(config.pre_prompt, Some("You are a helpful assistant".to_string()));
    }

    #[test]
    fn test_session_config_empty() {
        let config = SessionConfig::empty();
        assert_eq!(config.pre_prompt, None);
    }

    #[test]
    fn test_pipeline_mode_default() {
        let config = SessionConfig::empty();
        assert_eq!(config.pipeline_mode, PipelineMode::Live);
    }

    #[test]
    fn test_pipeline_mode_composed() {
        let config = SessionConfig::empty().with_pipeline_mode(PipelineMode::Composed);
        assert_eq!(config.pipeline_mode, PipelineMode::Composed);
        assert_eq!(config.pipeline_mode.as_str(), "composed");
    }
}
