//! Raft commands and responses
//!
//! These types are submitted to Raft for consensus and applied to the state machine.

use crate::kv::KvEntry;
use crate::Revision;
use serde::{Deserialize, Serialize};

/// Commands submitted to Raft consensus
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Default)]
pub enum RaftCommand {
    /// Put a key-value pair
    Put {
        key: Vec<u8>,
        value: Vec<u8>,
        lease_id: Option<i64>,
        /// If true, return the previous value
        prev_kv: bool,
    },

    /// Delete a single key
    Delete {
        key: Vec<u8>,
        /// If true, return the deleted value
        prev_kv: bool,
    },

    /// Delete a range of keys
    DeleteRange {
        start: Vec<u8>,
        end: Vec<u8>,
        /// If true, return deleted values
        prev_kv: bool,
    },

    /// Transaction with multiple operations
    Txn {
        /// Comparison conditions
        compare: Vec<Compare>,
        /// Operations to execute if all comparisons succeed
        success: Vec<TxnOp>,
        /// Operations to execute if any comparison fails
        failure: Vec<TxnOp>,
    },

    /// Grant a new lease
    LeaseGrant {
        /// Requested lease ID (0 for server-assigned)
        id: i64,
        /// TTL in seconds
        ttl: i64,
    },

    /// Revoke a lease (deletes all attached keys)
    LeaseRevoke {
        /// Lease ID to revoke
        id: i64,
    },

    /// Refresh a lease TTL (keep-alive)
    LeaseRefresh {
        /// Lease ID to refresh
        id: i64,
    },

    /// No-op command for Raft leadership establishment
    #[default]
    Noop,
}


/// Comparison for transaction conditions
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Compare {
    pub key: Vec<u8>,
    pub target: CompareTarget,
    pub result: CompareResult,
}

/// What to compare against
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CompareTarget {
    /// Compare the version number
    Version(u64),
    /// Compare the creation revision
    CreateRevision(Revision),
    /// Compare the modification revision
    ModRevision(Revision),
    /// Compare the value
    Value(Vec<u8>),
}

/// Comparison operator
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CompareResult {
    Equal,
    NotEqual,
    Greater,
    Less,
}

/// Operation in a transaction
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TxnOp {
    Put {
        key: Vec<u8>,
        value: Vec<u8>,
        lease_id: Option<i64>,
    },
    Delete {
        key: Vec<u8>,
    },
    DeleteRange {
        start: Vec<u8>,
        end: Vec<u8>,
    },
    /// Range query within a transaction
    Range {
        key: Vec<u8>,
        range_end: Vec<u8>,
        limit: i64,
        keys_only: bool,
        count_only: bool,
    },
}

/// Response from a single operation in a transaction
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TxnOpResponse {
    /// Response from a Put operation
    Put {
        prev_kv: Option<KvEntry>,
    },
    /// Response from a Delete/DeleteRange operation
    Delete {
        deleted: u64,
        prev_kvs: Vec<KvEntry>,
    },
    /// Response from a Range operation
    Range {
        kvs: Vec<KvEntry>,
        count: u64,
        more: bool,
    },
}

/// Response from applying a Raft command
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct RaftResponse {
    /// Current revision after this operation
    pub revision: Revision,
    /// Previous key-value (if requested and existed)
    pub prev_kv: Option<KvEntry>,
    /// Number of keys deleted (for delete operations)
    pub deleted: u64,
    /// Whether transaction succeeded (for Txn)
    pub succeeded: bool,
    /// Previous key-values for batch deletes
    pub prev_kvs: Vec<KvEntry>,
    /// Lease ID (for lease operations)
    pub lease_id: Option<i64>,
    /// Lease TTL (for lease operations)
    pub lease_ttl: Option<i64>,
    /// Individual operation responses (for Txn)
    pub txn_responses: Vec<TxnOpResponse>,
}

impl RaftResponse {
    /// Create a simple response with just revision
    pub fn new(revision: Revision) -> Self {
        Self {
            revision,
            ..Default::default()
        }
    }

    /// Create a response with previous key-value
    pub fn with_prev_kv(revision: Revision, prev_kv: Option<KvEntry>) -> Self {
        Self {
            revision,
            prev_kv,
            ..Default::default()
        }
    }

    /// Create a response for delete operations
    pub fn deleted(revision: Revision, deleted: u64, prev_kvs: Vec<KvEntry>) -> Self {
        Self {
            revision,
            deleted,
            prev_kvs,
            ..Default::default()
        }
    }

    /// Create a response for transaction
    pub fn txn(revision: Revision, succeeded: bool, txn_responses: Vec<TxnOpResponse>) -> Self {
        Self {
            revision,
            succeeded,
            txn_responses,
            ..Default::default()
        }
    }

    /// Create a response for lease operations
    pub fn lease(revision: Revision, lease_id: i64, ttl: i64) -> Self {
        Self {
            revision,
            lease_id: Some(lease_id),
            lease_ttl: Some(ttl),
            ..Default::default()
        }
    }
}

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

    #[test]
    fn test_put_command() {
        let cmd = RaftCommand::Put {
            key: b"key".to_vec(),
            value: b"value".to_vec(),
            lease_id: None,
            prev_kv: false,
        };

        let serialized = bincode::serialize(&cmd).unwrap();
        let deserialized: RaftCommand = bincode::deserialize(&serialized).unwrap();

        assert_eq!(cmd, deserialized);
    }

    #[test]
    fn test_txn_command() {
        let cmd = RaftCommand::Txn {
            compare: vec![Compare {
                key: b"key".to_vec(),
                target: CompareTarget::Version(1),
                result: CompareResult::Equal,
            }],
            success: vec![TxnOp::Put {
                key: b"key".to_vec(),
                value: b"new_value".to_vec(),
                lease_id: None,
            }],
            failure: vec![],
        };

        let serialized = bincode::serialize(&cmd).unwrap();
        let deserialized: RaftCommand = bincode::deserialize(&serialized).unwrap();

        assert_eq!(cmd, deserialized);
    }

    #[test]
    fn test_response() {
        let entry = KvEntry::new(b"key".to_vec(), b"old".to_vec(), 1);
        let response = RaftResponse::with_prev_kv(5, Some(entry.clone()));

        assert_eq!(response.revision, 5);
        assert_eq!(response.prev_kv, Some(entry));
    }
}
