//! Builder pattern for cluster creation

use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::Arc;

use chainfire_gossip::{GossipAgent, GossipId};
use chainfire_types::node::NodeRole;
use chainfire_types::RaftRole;

use crate::callbacks::{ClusterEventHandler, KvEventHandler};
use crate::cluster::Cluster;
use crate::config::{ClusterConfig, MemberConfig, StorageBackendConfig, TimeoutConfig};
use crate::error::{ClusterError, Result};
use crate::events::EventDispatcher;

/// Builder for creating a Chainfire cluster instance
///
/// # Example
///
/// ```ignore
/// use chainfire_core::ClusterBuilder;
///
/// let cluster = ClusterBuilder::new(1)
///     .name("node-1")
///     .gossip_addr("0.0.0.0:7946".parse()?)
///     .raft_addr("0.0.0.0:2380".parse()?)
///     .bootstrap(true)
///     .build()
///     .await?;
/// ```
pub struct ClusterBuilder {
    config: ClusterConfig,
    cluster_handlers: Vec<Arc<dyn ClusterEventHandler>>,
    kv_handlers: Vec<Arc<dyn KvEventHandler>>,
}

impl ClusterBuilder {
    /// Create a new cluster builder with the given node ID
    pub fn new(node_id: u64) -> Self {
        Self {
            config: ClusterConfig {
                node_id,
                ..Default::default()
            },
            cluster_handlers: Vec::new(),
            kv_handlers: Vec::new(),
        }
    }

    /// Set the node name
    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.config.node_name = name.into();
        self
    }

    /// Set the node role (ControlPlane or Worker)
    pub fn role(mut self, role: NodeRole) -> Self {
        self.config.node_role = role;
        self
    }

    /// Set the Raft participation role (Voter, Learner, or None)
    pub fn raft_role(mut self, role: RaftRole) -> Self {
        self.config.raft_role = role;
        self
    }

    /// Set the API listen address
    pub fn api_addr(mut self, addr: SocketAddr) -> Self {
        self.config.api_addr = Some(addr);
        self
    }

    /// Set the Raft listen address (for control plane nodes)
    pub fn raft_addr(mut self, addr: SocketAddr) -> Self {
        self.config.raft_addr = Some(addr);
        self
    }

    /// Set the gossip listen address
    pub fn gossip_addr(mut self, addr: SocketAddr) -> Self {
        self.config.gossip_addr = addr;
        self
    }

    /// Set the storage backend
    pub fn storage(mut self, backend: StorageBackendConfig) -> Self {
        self.config.storage = backend;
        self
    }

    /// Set the data directory (convenience method for RocksDB storage)
    pub fn data_dir(mut self, path: impl Into<PathBuf>) -> Self {
        self.config.storage = StorageBackendConfig::RocksDb { path: path.into() };
        self
    }

    /// Use in-memory storage
    pub fn memory_storage(mut self) -> Self {
        self.config.storage = StorageBackendConfig::Memory;
        self
    }

    /// Add initial cluster members (for bootstrap)
    pub fn initial_members(mut self, members: Vec<MemberConfig>) -> Self {
        self.config.initial_members = members;
        self
    }

    /// Add a single initial member
    pub fn add_member(mut self, member: MemberConfig) -> Self {
        self.config.initial_members.push(member);
        self
    }

    /// Enable cluster bootstrap (first node)
    pub fn bootstrap(mut self, bootstrap: bool) -> Self {
        self.config.bootstrap = bootstrap;
        self
    }

    /// Set the cluster ID
    pub fn cluster_id(mut self, id: u64) -> Self {
        self.config.cluster_id = id;
        self
    }

    /// Enable gRPC API server
    pub fn with_grpc_api(mut self, enabled: bool) -> Self {
        self.config.enable_grpc_api = enabled;
        self
    }

    /// Set timeout configuration
    pub fn timeouts(mut self, timeouts: TimeoutConfig) -> Self {
        self.config.timeouts = timeouts;
        self
    }

    /// Register a cluster event handler
    ///
    /// Multiple handlers can be registered. They will all be called
    /// when cluster events occur.
    pub fn on_cluster_event<H>(mut self, handler: H) -> Self
    where
        H: ClusterEventHandler + 'static,
    {
        self.cluster_handlers.push(Arc::new(handler));
        self
    }

    /// Register a cluster event handler (Arc version)
    pub fn on_cluster_event_arc(mut self, handler: Arc<dyn ClusterEventHandler>) -> Self {
        self.cluster_handlers.push(handler);
        self
    }

    /// Register a KV event handler
    ///
    /// Multiple handlers can be registered. They will all be called
    /// when KV events occur.
    pub fn on_kv_event<H>(mut self, handler: H) -> Self
    where
        H: KvEventHandler + 'static,
    {
        self.kv_handlers.push(Arc::new(handler));
        self
    }

    /// Register a KV event handler (Arc version)
    pub fn on_kv_event_arc(mut self, handler: Arc<dyn KvEventHandler>) -> Self {
        self.kv_handlers.push(handler);
        self
    }

    /// Validate the configuration
    fn validate(&self) -> Result<()> {
        if self.config.node_id == 0 {
            return Err(ClusterError::Config("node_id must be non-zero".into()));
        }

        if self.config.node_name.is_empty() {
            return Err(ClusterError::Config("node_name is required".into()));
        }

        // Raft-participating nodes need a Raft address
        if self.config.raft_role.participates_in_raft() && self.config.raft_addr.is_none() {
            return Err(ClusterError::Config(
                "raft_addr is required for Raft-participating nodes".into(),
            ));
        }

        Ok(())
    }

    /// Build the cluster instance
    ///
    /// This initializes the storage backend, Raft (if applicable), and gossip.
    pub async fn build(self) -> Result<Cluster> {
        self.validate()?;

        // Create event dispatcher with registered handlers
        let mut event_dispatcher = EventDispatcher::new();
        for handler in self.cluster_handlers {
            event_dispatcher.add_cluster_handler(handler);
        }
        for handler in self.kv_handlers {
            event_dispatcher.add_kv_handler(handler);
        }

        // Initialize gossip agent
        let gossip_identity = GossipId::new(
            self.config.node_id,
            self.config.gossip_addr,
            self.config.node_role,
        );

        let gossip_agent = GossipAgent::new(gossip_identity, chainfire_gossip::agent::default_config())
            .await
            .map_err(|e| ClusterError::Gossip(e.to_string()))?;

        tracing::info!(
            node_id = self.config.node_id,
            gossip_addr = %self.config.gossip_addr,
            "Gossip agent initialized"
        );

        // Create the cluster
        let cluster = Cluster::new(self.config, Some(gossip_agent), event_dispatcher);

        // TODO: Initialize storage backend
        // TODO: Initialize Raft if role participates
        // TODO: Start background tasks

        Ok(cluster)
    }
}
