//! OpenTelemetry configuration utilities.
//!
//! This module handles OTel environment variable generation and management
//! for automatic token tracking.

use serde::{Deserialize, Serialize};

/// Default OTel port (matches OpenTelemetry standard HTTP/JSON port).
pub const DEFAULT_OTEL_PORT: u16 = 4318;

/// OTel server processing mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum OtelMode {
    /// Shell out to `mi6 ingest otel` for processing (default).
    /// Enables dynamic upgrades without server restart.
    #[default]
    RelayCli,
    /// Use mi6-core library directly for processing.
    /// Lower latency, but requires server restart for upgrades.
    Library,
}

impl std::str::FromStr for OtelMode {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "relay-cli" | "relay_cli" | "relaycli" => Ok(Self::RelayCli),
            "library" | "lib" => Ok(Self::Library),
            _ => Err(format!(
                "invalid mode '{}', expected 'relay-cli' or 'library'",
                s
            )),
        }
    }
}

impl std::fmt::Display for OtelMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::RelayCli => write!(f, "relay-cli"),
            Self::Library => write!(f, "library"),
        }
    }
}

/// OTel server configuration.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OtelConfig {
    /// Server processing mode.
    #[serde(default)]
    pub mode: OtelMode,
}

/// OTel-related environment variable keys.
///
/// These keys are used to configure OpenTelemetry for automatic token tracking.
pub const OTEL_ENV_KEYS: &[&str] = &[
    "CLAUDE_CODE_ENABLE_TELEMETRY",
    "OTEL_LOGS_EXPORTER",
    "OTEL_EXPORTER_OTLP_PROTOCOL",
    "OTEL_EXPORTER_OTLP_ENDPOINT",
];

/// Generate OpenTelemetry environment variables configuration.
///
/// Returns a JSON object containing the environment variables needed
/// to enable OpenTelemetry-based token tracking.
///
/// # Arguments
/// * `port` - The port where the OTel server will listen.
pub fn generate_otel_env(port: u16) -> serde_json::Value {
    serde_json::json!({
        "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
        "OTEL_LOGS_EXPORTER": "otlp",
        "OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
        "OTEL_EXPORTER_OTLP_ENDPOINT": format!("http://127.0.0.1:{}", port)
    })
}

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

    #[test]
    fn test_generate_otel_env() {
        let env = generate_otel_env(4318);
        assert_eq!(env["CLAUDE_CODE_ENABLE_TELEMETRY"], "1");
        assert_eq!(env["OTEL_LOGS_EXPORTER"], "otlp");
        assert_eq!(env["OTEL_EXPORTER_OTLP_PROTOCOL"], "http/json");
        assert_eq!(env["OTEL_EXPORTER_OTLP_ENDPOINT"], "http://127.0.0.1:4318");
    }

    #[test]
    fn test_generate_otel_env_custom_port() {
        let env = generate_otel_env(9999);
        assert_eq!(env["OTEL_EXPORTER_OTLP_ENDPOINT"], "http://127.0.0.1:9999");
    }

    #[test]
    fn test_otel_env_keys() {
        assert_eq!(OTEL_ENV_KEYS.len(), 4);
        assert!(OTEL_ENV_KEYS.contains(&"CLAUDE_CODE_ENABLE_TELEMETRY"));
        assert!(OTEL_ENV_KEYS.contains(&"OTEL_LOGS_EXPORTER"));
        assert!(OTEL_ENV_KEYS.contains(&"OTEL_EXPORTER_OTLP_PROTOCOL"));
        assert!(OTEL_ENV_KEYS.contains(&"OTEL_EXPORTER_OTLP_ENDPOINT"));
    }

    #[test]
    fn test_otel_mode_default() {
        let mode = OtelMode::default();
        assert_eq!(mode, OtelMode::RelayCli);
    }

    #[test]
    fn test_otel_mode_from_str() {
        assert_eq!(
            "relay-cli".parse::<OtelMode>().expect("parse"),
            OtelMode::RelayCli
        );
        assert_eq!(
            "relay_cli".parse::<OtelMode>().expect("parse"),
            OtelMode::RelayCli
        );
        assert_eq!(
            "relaycli".parse::<OtelMode>().expect("parse"),
            OtelMode::RelayCli
        );
        assert_eq!(
            "library".parse::<OtelMode>().expect("parse"),
            OtelMode::Library
        );
        assert_eq!("lib".parse::<OtelMode>().expect("parse"), OtelMode::Library);
        assert!("invalid".parse::<OtelMode>().is_err());
    }

    #[test]
    fn test_otel_mode_display() {
        assert_eq!(OtelMode::RelayCli.to_string(), "relay-cli");
        assert_eq!(OtelMode::Library.to_string(), "library");
    }

    #[test]
    fn test_otel_mode_serde() {
        // Test serialization
        let mode = OtelMode::RelayCli;
        let json = serde_json::to_string(&mode).expect("serialize");
        assert_eq!(json, "\"relay-cli\"");

        let mode = OtelMode::Library;
        let json = serde_json::to_string(&mode).expect("serialize");
        assert_eq!(json, "\"library\"");

        // Test deserialization
        let mode: OtelMode = serde_json::from_str("\"relay-cli\"").expect("deserialize");
        assert_eq!(mode, OtelMode::RelayCli);

        let mode: OtelMode = serde_json::from_str("\"library\"").expect("deserialize");
        assert_eq!(mode, OtelMode::Library);
    }

    #[test]
    fn test_otel_config_default() {
        let config = OtelConfig::default();
        assert_eq!(config.mode, OtelMode::RelayCli);
    }

    #[test]
    fn test_otel_config_from_toml() {
        let toml_str = r#"
            mode = "library"
        "#;
        let config: OtelConfig = toml::from_str(toml_str).expect("parse toml");
        assert_eq!(config.mode, OtelMode::Library);
    }

    #[test]
    fn test_otel_config_from_toml_default_mode() {
        // Empty config should use default mode
        let toml_str = "";
        let config: OtelConfig = toml::from_str(toml_str).expect("parse toml");
        assert_eq!(config.mode, OtelMode::RelayCli);
    }
}
