use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::RwLock;

/// Reserved namespaces that cannot have their mode changed.
/// These are automatically created with Strong consistency on server startup.
pub const RESERVED_NAMESPACES: &[&str] = &["iam", "metrics", "_system"];

fn default_store_id() -> u64 {
    1
}

fn default_addr() -> SocketAddr {
    "127.0.0.1:50052".parse().expect("default addr parse")
}

fn default_http_addr() -> SocketAddr {
    "127.0.0.1:8082".parse().expect("default http addr parse")
}

fn default_data_dir() -> PathBuf {
    PathBuf::from("data")
}

fn default_pd_addr() -> String {
    "127.0.0.1:2479".to_string()
}

fn default_pd_endpoints() -> Vec<String> {
    Vec::new()
}

fn default_peers() -> HashMap<u64, SocketAddr> {
    HashMap::new()
}

fn default_namespace_modes() -> HashMap<String, ConsistencyMode> {
    let mut namespace_modes = HashMap::new();
    namespace_modes.insert("default".to_string(), ConsistencyMode::Strong);
    namespace_modes
}

fn default_log_level() -> String {
    "info".to_string()
}

fn default_default_namespace_mode() -> ConsistencyMode {
    ConsistencyMode::Strong
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConsistencyMode {
    Strong,
    Eventual,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NamespaceConfig {
    pub id: u32,
    pub name: String,
    pub mode: ConsistencyMode,
    pub explicit: bool,
}

#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NamespaceModeDiff {
    pub namespace: String,
    pub self_id: u32,
    pub other_id: u32,
    pub self_mode: ConsistencyMode,
    pub other_mode: ConsistencyMode,
}

// =========================================================================
// Main Server Configuration (Serializable)
// =========================================================================

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
    #[serde(default = "default_store_id")]
    pub store_id: u64,
    #[serde(default = "default_addr")]
    pub addr: SocketAddr,
    #[serde(default = "default_http_addr")]
    pub http_addr: SocketAddr,
    #[serde(default = "default_data_dir")]
    pub data_dir: PathBuf,
    #[serde(default = "default_pd_addr")]
    pub pd_addr: String,
    #[serde(default = "default_pd_endpoints")]
    pub pd_endpoints: Vec<String>,
    #[serde(default = "default_peers")]
    pub peers: HashMap<u64, SocketAddr>,
    /// Mode used when namespaces are implicitly created (non-reserved)
    #[serde(default = "default_default_namespace_mode")]
    pub default_namespace_mode: ConsistencyMode,
    #[serde(default = "default_namespace_modes")]
    pub namespace_modes: HashMap<String, ConsistencyMode>,
    #[serde(default = "default_log_level")]
    pub log_level: String,
    /// TLS configuration (optional)
    #[serde(default)]
    pub tls: Option<TlsConfig>,
}

/// TLS configuration for gRPC servers
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TlsConfig {
    /// Path to server certificate file (PEM format)
    pub cert_file: String,
    /// Path to server private key file (PEM format)
    pub key_file: String,
    /// Path to CA certificate file for client verification (optional, enables mTLS)
    pub ca_file: Option<String>,
    /// Require client certificates (mTLS mode)
    #[serde(default)]
    pub require_client_cert: bool,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            store_id: default_store_id(),
            addr: default_addr(),
            http_addr: default_http_addr(),
            data_dir: default_data_dir(),
            pd_addr: default_pd_addr(),
            pd_endpoints: default_pd_endpoints(),
            peers: default_peers(),
            default_namespace_mode: default_default_namespace_mode(),
            namespace_modes: default_namespace_modes(),
            log_level: default_log_level(),
            tls: None,
        }
    }
}

impl Config {
    pub fn resolved_pd_endpoints(&self) -> Vec<String> {
        let mut endpoints = self.pd_endpoints.clone();
        if endpoints.is_empty() {
            endpoints.push(self.pd_addr.clone());
        }
        endpoints.sort();
        endpoints.dedup();
        endpoints
    }
}

// =========================================================================
// Namespace Manager (Runtime)
// =========================================================================

#[derive(Debug)]
pub struct NamespaceManager {
    namespaces: RwLock<HashMap<String, NamespaceConfig>>,
    default_mode: ConsistencyMode,
    next_id: AtomicU32,
    reserved_namespaces: Vec<String>,
}

impl NamespaceManager {
    pub fn new(
        default_mode: ConsistencyMode,
        mut namespaces: HashMap<String, NamespaceConfig>,
    ) -> Self {
        // Ensure default namespace exists with id=1
        if !namespaces.contains_key("default") {
            namespaces.insert(
                "default".to_string(),
                NamespaceConfig {
                    id: 1,
                    name: "default".to_string(),
                    mode: default_mode.clone(),
                    explicit: true,
                },
            );
        }

        // Auto-create reserved namespaces with Strong consistency
        let mut next_id = namespaces.values().map(|n| n.id).max().unwrap_or(1) + 1;
        for &reserved in RESERVED_NAMESPACES {
            if !namespaces.contains_key(reserved) {
                namespaces.insert(
                    reserved.to_string(),
                    NamespaceConfig {
                        id: next_id,
                        name: reserved.to_string(),
                        mode: ConsistencyMode::Strong, // Always strong for reserved
                        explicit: true,
                    },
                );
                next_id += 1;
            }
        }

        Self {
            namespaces: RwLock::new(namespaces),
            default_mode,
            next_id: AtomicU32::new(next_id),
            reserved_namespaces: RESERVED_NAMESPACES.iter().map(|s| s.to_string()).collect(),
        }
    }

    /// Helper to create a manager from a serializable config
    pub fn from_config(config: &Config) -> Self {
        let mut namespaces_map = HashMap::new();
        for (name, mode) in config.namespace_modes.clone() {
            namespaces_map.insert(
                name.clone(),
                NamespaceConfig {
                    id: 0, // ID will be assigned by new()
                    name,
                    mode,
                    explicit: true,
                },
            );
        }
        Self::new(config.default_namespace_mode.clone(), namespaces_map)
    }

    /// Check if a namespace is reserved (mode cannot be changed).
    pub fn is_reserved(&self, namespace: &str) -> bool {
        self.reserved_namespaces.contains(&namespace.to_string())
    }

    /// Fetch or create a namespace configuration.
    pub fn get_namespace(&self, namespace: &str) -> NamespaceConfig {
        {
            let guard = self.namespaces.read().expect("namespace read");
            if let Some(ns) = guard.get(namespace) {
                return ns.clone();
            }
        }

        // Insert lazily with a unique id.
        let mut guard = self.namespaces.write().expect("namespace write");
        let ns_id = self.next_id.fetch_add(1, Ordering::SeqCst);
        let cfg = NamespaceConfig {
            id: ns_id,
            name: namespace.to_string(),
            mode: self.default_mode.clone(),
            explicit: false,
        };
        guard.insert(namespace.to_string(), cfg.clone());
        cfg
    }

    /// Update or insert a namespace and return the resulting config.
    /// Returns Err if attempting to change mode of a reserved namespace.
    pub fn set_namespace_mode(
        &self,
        namespace: &str,
        mode: ConsistencyMode,
    ) -> Result<NamespaceConfig, String> {
        // Block mode changes for reserved namespaces
        if self.is_reserved(namespace) {
            let guard = self.namespaces.read().expect("namespace read");
            if let Some(existing) = guard.get(namespace) {
                if existing.mode != mode {
                    return Err(format!(
                        "cannot change mode of reserved namespace '{}' (current: {:?}, requested: {:?})",
                        namespace, existing.mode, mode
                    ));
                }
                return Ok(existing.clone());
            }
        }

        let mut guard = self.namespaces.write().expect("namespace write");
        let entry = guard.entry(namespace.to_string());
        let cfg = entry
            .and_modify(|c| {
                c.mode = mode.clone();
                c.explicit = true;
            })
            .or_insert_with(|| NamespaceConfig {
                id: self.next_id.fetch_add(1, Ordering::SeqCst),
                name: namespace.to_string(),
                mode: mode.clone(),
                explicit: true,
            })
            .clone();
        Ok(cfg)
    }

    /// Merge a new set of namespace=mode pairs, keeping existing IDs where possible.
    #[allow(dead_code)]
    pub fn reload_from_pairs(&self, pairs: &[String]) -> Result<Vec<NamespaceConfig>, String> {
        let mut updated = Vec::new();
        for p in pairs {
            if let Some((name, mode_str)) = p.split_once('=') {
                let mode = match mode_str.to_lowercase().as_str() {
                    "strong" => ConsistencyMode::Strong,
                    "eventual" => ConsistencyMode::Eventual,
                    _ => {
                        return Err(format!(
                            "invalid mode '{}' for namespace '{}'",
                            mode_str, name
                        ))
                    }
                };
                let cfg = self.set_namespace_mode(name, mode)?;
                updated.push(cfg);
            } else {
                return Err(format!(
                    "invalid namespace mode format '{}', expected name=mode",
                    p
                ));
            }
        }
        Ok(updated)
    }

    pub fn list_namespaces(&self) -> Vec<NamespaceConfig> {
        self.namespaces
            .read()
            .expect("namespace read")
            .values()
            .cloned()
            .collect()
    }

    pub fn mode_as_str(mode: &ConsistencyMode) -> &'static str {
        match mode {
            ConsistencyMode::Strong => "strong",
            ConsistencyMode::Eventual => "eventual",
        }
    }

    /// Detect differences between this config and another (by namespace name).
    #[allow(dead_code)]
    pub fn diff_modes(&self, other: &NamespaceManager) -> Vec<NamespaceModeDiff> {
        let mine = self.list_namespaces();
        let mut diffs = Vec::new();
        for ns in mine {
            let o = other.get_namespace(&ns.name);
            if ns.mode != o.mode || ns.id != o.id {
                diffs.push(NamespaceModeDiff {
                    namespace: ns.name.clone(),
                    self_id: ns.id,
                    other_id: o.id,
                    self_mode: ns.mode,
                    other_mode: o.mode,
                });
            }
        }
        diffs
    }
}

pub fn parse_namespace_modes(pairs: &[String]) -> Result<HashMap<String, ConsistencyMode>, String> {
    let mut namespace_modes = HashMap::new();
    for p in pairs {
        if let Some((name, mode_str)) = p.split_once('=') {
            let mode = parse_mode(mode_str)?;
            namespace_modes.insert(name.to_string(), mode);
        } else {
            return Err(format!(
                "invalid namespace mode format '{}', expected name=mode",
                p
            ));
        }
    }
    Ok(namespace_modes)
}

pub fn parse_mode(mode_str: &str) -> Result<ConsistencyMode, String> {
    match mode_str.to_lowercase().as_str() {
        "strong" => Ok(ConsistencyMode::Strong),
        "eventual" => Ok(ConsistencyMode::Eventual),
        _ => Err(format!("invalid mode '{}', expected strong|eventual", mode_str)),
    }
}

/// Encode a key with the namespace id prefix (big endian) for RocksDB ordering.
pub fn encode_namespaced_key(ns_id: u32, key: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(4 + key.len());
    out.extend_from_slice(&ns_id.to_be_bytes());
    out.extend_from_slice(key);
    out
}

/// Encode value with a timestamp prefix (big endian u64) for LWW resolution.
pub fn encode_value_with_ts(ts: u64, value: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(8 + value.len());
    out.extend_from_slice(&ts.to_be_bytes());
    out.extend_from_slice(value);
    out
}

/// Decode value with timestamp prefix; returns (ts, value_slice).
pub fn decode_value_with_ts(data: &[u8]) -> (u64, Vec<u8>) {
    if data.len() < 8 {
        return (0, data.to_vec());
    }
    let ts = u64::from_be_bytes([
        data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
    ]);
    (ts, data[8..].to_vec())
}
