//! Service configuration — reflects `[services.*]` in `el.toml`.

use crate::binding::BindingKind;

/// Authentication configuration for a service binding.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthConfig {
    /// No authentication.
    None,
    /// Bearer token (set in environment variable or injected at runtime).
    Bearer { token_env: String },
    /// Basic auth.
    Basic { username_env: String, password_env: String },
    /// API key in header.
    ApiKey { header: String, key_env: String },
}

impl AuthConfig {
    /// Produce the HTTP Authorization header value for this auth config.
    /// Returns `None` if auth is `None` or the env var is not set.
    pub fn authorization_header(&self) -> Option<String> {
        match self {
            Self::None => None,
            Self::Bearer { token_env } => {
                let token = std::env::var(token_env).ok()?;
                Some(format!("Bearer {}", token))
            }
            Self::Basic { username_env, password_env } => {
                let user = std::env::var(username_env).ok()?;
                let pass = std::env::var(password_env).ok()?;
                // Base64 encode user:pass
                let raw = format!("{}:{}", user, pass);
                let encoded = base64_encode(raw.as_bytes());
                Some(format!("Basic {}", encoded))
            }
            Self::ApiKey { .. } => None,
        }
    }

    /// Produce custom header key/value for API key auth.
    pub fn api_key_header(&self) -> Option<(String, String)> {
        match self {
            Self::ApiKey { header, key_env } => {
                let key = std::env::var(key_env).ok()?;
                Some((header.clone(), key))
            }
            _ => None,
        }
    }
}

fn base64_encode(input: &[u8]) -> String {
    const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let mut out = String::new();
    for chunk in input.chunks(3) {
        let b0 = chunk[0] as u32;
        let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
        let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
        let n = (b0 << 16) | (b1 << 8) | b2;
        out.push(CHARS[((n >> 18) & 63) as usize] as char);
        out.push(CHARS[((n >> 12) & 63) as usize] as char);
        out.push(if chunk.len() > 1 { CHARS[((n >> 6) & 63) as usize] as char } else { '=' });
        out.push(if chunk.len() > 2 { CHARS[(n & 63) as usize] as char } else { '=' });
    }
    out
}

/// Full configuration for one service, from `[services.ServiceName]`.
#[derive(Debug, Clone)]
pub struct ServiceConfig {
    /// Service name (matches the `service Name { ... }` declaration).
    pub name: String,
    /// The binding protocol.
    pub binding: BindingKind,
    /// Base URL (for REST/WebSocket/gRPC).
    pub base_url: Option<String>,
    /// Auth configuration.
    pub auth: AuthConfig,
    /// Connection timeout in milliseconds.
    pub timeout_ms: u64,
}

impl ServiceConfig {
    pub fn new(name: impl Into<String>, binding: BindingKind) -> Self {
        Self {
            name: name.into(),
            binding,
            base_url: None,
            auth: AuthConfig::None,
            timeout_ms: 30_000,
        }
    }

    pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
        self.base_url = Some(url.into());
        self
    }

    pub fn with_auth(mut self, auth: AuthConfig) -> Self {
        self.auth = auth;
        self
    }

    pub fn with_timeout(mut self, ms: u64) -> Self {
        self.timeout_ms = ms;
        self
    }
}
