//! Type definitions for OpenRaft integration
//!
//! This module defines the core types used throughout the OpenRaft implementation,
//! including the RaftTypeConfig, request/response types, and node metadata.

use serde::{Deserialize, Serialize};
use std::io::Cursor;

/// Node ID type - matches existing store_id
pub type FlareNodeId = u64;

/// Request types for the state machine
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum FlareRequest {
    /// Key-value write operation (Last-Write-Wins, eventual consistency)
    KvWrite {
        namespace_id: u32,
        key: Vec<u8>,
        value: Vec<u8>,
        ts: u64,
    },
    /// Key-value delete operation (Last-Write-Wins, eventual consistency)
    KvDelete {
        namespace_id: u32,
        key: Vec<u8>,
        ts: u64,
    },
    /// Compare-and-swap write operation (strong consistency)
    /// CAS is executed atomically within the state machine to ensure
    /// linearizable semantics across all replicas.
    CasWrite {
        namespace_id: u32,
        key: Vec<u8>,
        value: Vec<u8>,
        expected_version: u64,
        ts: u64,
    },
    /// Compare-and-swap delete operation (strong consistency)
    /// Deletes the key if the version matches expected_version.
    /// If expected_version is 0, deletes if key exists (no version check).
    CasDelete {
        namespace_id: u32,
        key: Vec<u8>,
        expected_version: u64,
        ts: u64,
    },
    /// Region split operation
    Split {
        region_id: u64,
        split_key: Vec<u8>,
        new_region_id: u64,
    },
    /// No-op for leader election
    Noop,
}

/// Response types from the state machine
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum FlareResponse {
    /// Operation completed successfully
    Ok,
    /// Write applied with index
    Applied { index: u64 },
    /// Delete operation result
    DeleteResult {
        existed: bool,
    },
    /// Compare-and-swap result
    CasResult {
        success: bool,
        current_version: u64,
        new_version: u64,
    },
    /// CAS delete result
    CasDeleteResult {
        success: bool,
        current_version: u64,
        existed: bool,
    },
    /// Split operation created new region
    SplitCreated { new_region_id: u64 },
}

/// Node metadata - contains address and region information
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct FlareNode {
    /// Network address (host:port)
    pub addr: String,
    /// Region ID this node belongs to
    pub region_id: u64,
}

impl std::fmt::Display for FlareNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "region-{}@{}", self.region_id, self.addr)
    }
}

/// Snapshot data type - uses in-memory cursor for simplicity
pub type FlareSnapshotData = Cursor<Vec<u8>>;

// Declare the RaftTypeConfig using OpenRaft's macro
openraft::declare_raft_types!(
    /// FlareDB's OpenRaft type configuration
    pub FlareTypeConfig:
        D = FlareRequest,
        R = FlareResponse,
        Node = FlareNode,
        NodeId = FlareNodeId,
);

/// Type alias for OpenRaft's Entry with our config
pub type FlareEntry = openraft::Entry<FlareTypeConfig>;

/// Type alias for OpenRaft's LogId with our config
pub type FlareLogId = openraft::LogId<FlareNodeId>;

/// Type alias for OpenRaft's Vote with our config
pub type FlareVote = openraft::Vote<FlareNodeId>;

/// Type alias for OpenRaft's Membership with our config
pub type FlareMembership = openraft::Membership<FlareNodeId, FlareNode>;

/// Type alias for StoredMembership with our config
pub type FlareStoredMembership = openraft::StoredMembership<FlareNodeId, FlareNode>;

/// Type alias for the Raft instance
pub type FlareRaft = openraft::Raft<FlareTypeConfig>;

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

    #[test]
    fn test_flare_request_serialization() {
        let req = FlareRequest::KvWrite {
            namespace_id: 1,
            key: b"test_key".to_vec(),
            value: b"test_value".to_vec(),
            ts: 12345,
        };
        let json = serde_json::to_string(&req).unwrap();
        let decoded: FlareRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(req, decoded);
    }

    #[test]
    fn test_flare_node_display() {
        let node = FlareNode {
            addr: "127.0.0.1:50051".to_string(),
            region_id: 1,
        };
        assert_eq!(format!("{}", node), "region-1@127.0.0.1:50051");
    }
}
