//! Simulation bridge configuration

use mecha10_core::prelude::*;

/// Signaling mode - server (local) or client (relay)
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum SignalingMode {
    /// Server mode: run local signaling server (default)
    #[default]
    Server,
    /// Client mode: connect to remote relay service
    Client,
}

/// Relay configuration for client mode signaling
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SignalingRelayConfig {
    /// Relay WebSocket URL (e.g., wss://api.mecha.industries/webrtc-relay)
    /// Can be set via WEBRTC_RELAY_URL environment variable
    #[serde(default = "default_relay_url")]
    pub url: String,

    /// Robot ID for authentication
    /// Can be set via ROBOT_ID environment variable
    #[serde(default = "default_robot_id")]
    pub robot_id: String,

    /// API key for authentication
    /// Can be set via ROBOT_API_KEY environment variable
    #[serde(default = "default_api_key")]
    pub api_key: String,

    /// Reconnect interval in milliseconds
    #[serde(default = "default_reconnect_interval")]
    pub reconnect_interval_ms: u64,
}

fn default_relay_url() -> String {
    std::env::var("WEBRTC_RELAY_URL").unwrap_or_default()
}

fn default_robot_id() -> String {
    std::env::var("ROBOT_ID").unwrap_or_default()
}

fn default_api_key() -> String {
    std::env::var("ROBOT_API_KEY").unwrap_or_default()
}

fn default_reconnect_interval() -> u64 {
    5000
}

fn default_signaling_relay() -> Option<SignalingRelayConfig> {
    // Auto-create relay config from environment variables when missing from JSON
    // This allows environment-based configuration without modifying config files
    let url = std::env::var("WEBRTC_RELAY_URL").unwrap_or_default();
    let robot_id = std::env::var("ROBOT_ID").unwrap_or_default();
    let api_key = std::env::var("ROBOT_API_KEY").unwrap_or_default();

    // Only create config if at least relay URL is provided
    if !url.is_empty() {
        Some(SignalingRelayConfig {
            url,
            robot_id,
            api_key,
            reconnect_interval_ms: 5000,
        })
    } else {
        None
    }
}

/// Camera streaming configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CameraConfig {
    /// Camera resolution width (160-1920)
    #[serde(default = "default_camera_width")]
    pub width: u32,

    /// Camera resolution height (120-1080)
    #[serde(default = "default_camera_height")]
    pub height: u32,

    /// Camera frames per second (5-60)
    #[serde(default = "default_camera_fps")]
    pub fps: u32,
}

impl CameraConfig {
    /// Validate camera configuration
    pub fn validate(&self) -> Result<()> {
        if self.width < 160 || self.width > 1920 {
            return Err(Mecha10Error::Configuration(format!(
                "Camera width must be between 160 and 1920, got {}",
                self.width
            )));
        }
        if self.height < 120 || self.height > 1080 {
            return Err(Mecha10Error::Configuration(format!(
                "Camera height must be between 120 and 1080, got {}",
                self.height
            )));
        }
        if self.fps < 5 || self.fps > 60 {
            return Err(Mecha10Error::Configuration(format!(
                "Camera FPS must be between 5 and 60, got {}",
                self.fps
            )));
        }
        Ok(())
    }
}

impl Default for CameraConfig {
    fn default() -> Self {
        Self {
            width: default_camera_width(),
            height: default_camera_height(),
            fps: default_camera_fps(),
        }
    }
}

fn default_camera_width() -> u32 {
    640 // Higher resolution enabled by JPEG compression (protocol v4)
}

fn default_camera_height() -> u32 {
    480 // Higher resolution enabled by JPEG compression (protocol v4)
}

fn default_camera_fps() -> u32 {
    10 // Reduced FPS for higher resolution, ~10-15KB per frame with JPEG
}

/// Simulation bridge configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SimulationBridgeConfig {
    /// Godot WebSocket URL for control (port 11008)
    #[serde(default = "default_godot_url")]
    pub godot_url: String,

    /// Godot WebSocket URL for camera streaming (port 11009)
    #[serde(default = "default_camera_url")]
    pub camera_url: String,

    /// Signaling mode: server (local) or client (relay)
    #[serde(default)]
    pub signaling_mode: SignalingMode,

    /// WebRTC signaling server port (port 11010) - only used in server mode
    #[serde(default = "default_signaling_port")]
    pub signaling_port: u16,

    /// Relay configuration (required for client mode)
    /// Auto-populated from environment variables if missing
    #[serde(default = "default_signaling_relay")]
    pub signaling_relay: Option<SignalingRelayConfig>,

    /// Sensor publishing rate in Hz
    #[serde(default = "default_publish_rate")]
    pub publish_rate_hz: u64,

    /// Camera streaming configuration
    #[serde(default)]
    pub camera: CameraConfig,
}

fn default_godot_url() -> String {
    std::env::var("GODOT_URL").unwrap_or_else(|_| "ws://localhost:11008".to_string())
}

fn default_camera_url() -> String {
    std::env::var("GODOT_CAMERA_URL").unwrap_or_else(|_| "ws://localhost:11009".to_string())
}

fn default_signaling_port() -> u16 {
    std::env::var("WEBRTC_SIGNALING_PORT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(11010)
}

fn default_publish_rate() -> u64 {
    30 // 30 Hz sensor publishing
}

impl Default for SimulationBridgeConfig {
    fn default() -> Self {
        Self {
            godot_url: default_godot_url(),
            camera_url: default_camera_url(),
            signaling_mode: SignalingMode::default(),
            signaling_port: default_signaling_port(),
            signaling_relay: None,
            publish_rate_hz: default_publish_rate(),
            camera: CameraConfig::default(),
        }
    }
}

impl SimulationBridgeConfig {
    /// Validate configuration
    pub fn validate(&self) -> Result<()> {
        self.camera.validate()?;

        // Client mode requires relay config
        if self.signaling_mode == SignalingMode::Client {
            let relay = self
                .signaling_relay
                .as_ref()
                .ok_or_else(|| Mecha10Error::Configuration("signaling_relay required for client mode".to_string()))?;

            if relay.url.is_empty() {
                return Err(Mecha10Error::Configuration(
                    "signaling_relay.url is required".to_string(),
                ));
            }
            if relay.robot_id.is_empty() {
                return Err(Mecha10Error::Configuration(
                    "signaling_relay.robot_id is required".to_string(),
                ));
            }
            if relay.api_key.is_empty() {
                return Err(Mecha10Error::Configuration(
                    "signaling_relay.api_key is required".to_string(),
                ));
            }
        }

        Ok(())
    }
}
