//! Callback traits for cluster events

use async_trait::async_trait;

use chainfire_types::node::NodeInfo;

use crate::kvs::KvEntry;

/// Handler for cluster lifecycle events
///
/// Implement this trait to receive notifications about cluster membership
/// and leadership changes.
#[async_trait]
pub trait ClusterEventHandler: Send + Sync {
    /// Called when a node joins the cluster
    async fn on_node_joined(&self, _node: &NodeInfo) {}

    /// Called when a node leaves the cluster
    async fn on_node_left(&self, _node_id: u64, _reason: LeaveReason) {}

    /// Called when leadership changes
    async fn on_leader_changed(&self, _old_leader: Option<u64>, _new_leader: u64) {}

    /// Called when this node becomes leader
    async fn on_became_leader(&self) {}

    /// Called when this node loses leadership
    async fn on_lost_leadership(&self) {}

    /// Called when cluster membership changes
    async fn on_membership_changed(&self, _members: &[NodeInfo]) {}

    /// Called when a network partition is detected
    async fn on_partition_detected(&self, _reachable: &[u64], _unreachable: &[u64]) {}

    /// Called when cluster is ready (initial leader elected, etc.)
    async fn on_cluster_ready(&self) {}
}

/// Handler for KV store events
///
/// Implement this trait to receive notifications about key-value changes.
#[async_trait]
pub trait KvEventHandler: Send + Sync {
    /// Called when a key is created or updated
    async fn on_key_changed(
        &self,
        _namespace: &str,
        _key: &[u8],
        _value: &[u8],
        _revision: u64,
    ) {
    }

    /// Called when a key is deleted
    async fn on_key_deleted(&self, _namespace: &str, _key: &[u8], _revision: u64) {}

    /// Called when multiple keys with a prefix are changed
    async fn on_prefix_changed(&self, _namespace: &str, _prefix: &[u8], _entries: &[KvEntry]) {}
}

/// Reason for node departure from the cluster
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LeaveReason {
    /// Node left gracefully
    Graceful,

    /// Node timed out (failed to respond)
    Timeout,

    /// Network partition detected
    NetworkPartition,

    /// Node was explicitly evicted
    Evicted,

    /// Unknown reason
    Unknown,
}

impl std::fmt::Display for LeaveReason {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LeaveReason::Graceful => write!(f, "graceful"),
            LeaveReason::Timeout => write!(f, "timeout"),
            LeaveReason::NetworkPartition => write!(f, "network_partition"),
            LeaveReason::Evicted => write!(f, "evicted"),
            LeaveReason::Unknown => write!(f, "unknown"),
        }
    }
}

/// A no-op event handler for when callbacks are not needed
pub struct NoOpClusterEventHandler;

#[async_trait]
impl ClusterEventHandler for NoOpClusterEventHandler {}

/// A no-op KV event handler
pub struct NoOpKvEventHandler;

#[async_trait]
impl KvEventHandler for NoOpKvEventHandler {}
