//! Server configuration

use anyhow::Result;
use chainfire_types::RaftRole;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};

/// Server configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    /// Node configuration
    pub node: NodeConfig,
    /// Storage configuration
    pub storage: StorageConfig,
    /// Network configuration
    pub network: NetworkConfig,
    /// Cluster configuration
    pub cluster: ClusterConfig,
    /// Raft configuration
    #[serde(default)]
    pub raft: RaftConfig,
}

/// Node-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
    /// Unique node ID
    pub id: u64,
    /// Human-readable name
    pub name: String,
    /// Node role (control_plane or worker)
    pub role: String,
}

/// Storage configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
    /// Data directory
    pub data_dir: PathBuf,
}

/// Network configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
    /// API listen address (gRPC)
    pub api_addr: SocketAddr,
    /// HTTP REST API listen address
    #[serde(default = "default_http_addr")]
    pub http_addr: SocketAddr,
    /// Raft listen address
    pub raft_addr: SocketAddr,
    /// Gossip listen address (UDP)
    pub gossip_addr: SocketAddr,
    /// TLS configuration (optional)
    #[serde(default)]
    pub tls: Option<TlsConfig>,
}

fn default_http_addr() -> SocketAddr {
    "127.0.0.1:8081".parse().unwrap()
}

/// TLS configuration for gRPC servers
#[derive(Debug, Clone, Serialize, Deserialize)]
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,
}

/// Cluster configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterConfig {
    /// Cluster ID
    pub id: u64,
    /// Initial cluster members
    pub initial_members: Vec<MemberConfig>,
    /// Whether to bootstrap a new cluster
    pub bootstrap: bool,
}

/// Cluster member configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemberConfig {
    /// Node ID
    pub id: u64,
    /// Raft address
    pub raft_addr: String,
}

/// Raft-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaftConfig {
    /// Raft participation role: "voter", "learner", or "none"
    ///
    /// - `voter`: Full voting member in Raft consensus
    /// - `learner`: Non-voting replica that receives log replication
    /// - `none`: No Raft participation, node acts as agent/proxy only
    #[serde(default)]
    pub role: RaftRole,
}

impl Default for RaftConfig {
    fn default() -> Self {
        Self {
            role: RaftRole::Voter,
        }
    }
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            node: NodeConfig {
                id: 1,
                name: "chainfire-1".into(),
                role: "control_plane".into(),
            },
            storage: StorageConfig {
                data_dir: PathBuf::from("./data"),
            },
            network: NetworkConfig {
                api_addr: "127.0.0.1:2379".parse().unwrap(),
                http_addr: "127.0.0.1:8081".parse().unwrap(),
                raft_addr: "127.0.0.1:2380".parse().unwrap(),
                gossip_addr: "127.0.0.1:2381".parse().unwrap(),
                tls: None,
            },
            cluster: ClusterConfig {
                id: 1,
                initial_members: vec![],
                bootstrap: true,
            },
            raft: RaftConfig::default(),
        }
    }
}

impl ServerConfig {
    /// Load configuration from a file
    pub fn load(path: &Path) -> Result<Self> {
        let contents = std::fs::read_to_string(path)?;
        let config: ServerConfig = toml::from_str(&contents)?;
        Ok(config)
    }

    /// Save configuration to a file
    pub fn save(&self, path: &Path) -> Result<()> {
        let contents = toml::to_string_pretty(self)?;
        std::fs::write(path, contents)?;
        Ok(())
    }
}

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

    #[test]
    fn test_default_config() {
        let config = ServerConfig::default();
        assert_eq!(config.node.id, 1);
        assert!(config.cluster.bootstrap);
    }

    #[test]
    fn test_config_roundtrip() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("config.toml");

        let config = ServerConfig::default();
        config.save(&path).unwrap();

        let loaded = ServerConfig::load(&path).unwrap();
        assert_eq!(loaded.node.id, config.node.id);
        assert_eq!(loaded.network.api_addr, config.network.api_addr);
    }
}
