use serde::{Deserialize, Deserializer, Serialize};

/// Health status response from gateway
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
    pub healthy: bool,
    pub reachable_nodes: usize,
    pub joined: bool,
}

/// Cluster status overview
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterStatusResponse {
    pub node_count: usize,
    pub shard_count: u32,
    pub meta_version: u64,
    pub nodes: Vec<NodeStatusInfo>,
}

/// Information about a single node in the cluster
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeStatusInfo {
    pub id: String,       // Short hex (first 8 chars of UUID)
    pub state: String,    // "Alive", "Suspect", "Dead"
    pub shards: Vec<u32>, // Shard IDs assigned to this node
}

/// Wrapper for gateway ranges response (gateway returns {"ranges": [...]})
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct RangesWrapper {
    pub ranges: Vec<RangeInfoDto>,
}

/// Gateway shard info response (different format than ranges)
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct ShardInfoResponse {
    pub shard_id: u32,
    pub start_key: Option<String>,
    pub end_key: Option<String>,
    pub size_bytes: u64,
    pub assertion_count: u64,
    pub replicas: Vec<String>,
    pub generation: u64,
}

impl From<ShardInfoResponse> for RangeInfoDto {
    fn from(info: ShardInfoResponse) -> Self {
        Self {
            range_id: info.shard_id,
            start_key: info.start_key.unwrap_or_default(),
            end_key: info.end_key.unwrap_or_default(),
            size_bytes: info.size_bytes,
            assertion_count: info.assertion_count,
            leader_node: info.replicas.first().cloned().unwrap_or_default(),
            replica_nodes: info.replicas,
            generation: info.generation,
        }
    }
}

/// Detailed information about a shard range
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RangeInfoDto {
    #[serde(deserialize_with = "deserialize_range_id")]
    pub range_id: u32,
    pub start_key: String,
    pub end_key: String,
    pub size_bytes: u64,
    pub assertion_count: u64,
    pub leader_node: String,
    pub replica_nodes: Vec<String>,
    pub generation: u64,
}

/// Custom deserializer to handle both string "shard_X" and integer formats
fn deserialize_range_id<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::de::Error;

    #[derive(Deserialize)]
    #[serde(untagged)]
    enum RangeId {
        String(String),
        Integer(u32),
    }

    match RangeId::deserialize(deserializer)? {
        RangeId::Integer(id) => Ok(id),
        RangeId::String(s) => {
            // Handle "shard_X" format
            if let Some(num_str) = s.strip_prefix("shard_") {
                num_str
                    .parse::<u32>()
                    .map_err(|_| Error::custom(format!("Invalid shard ID format: {}", s)))
            } else {
                // Try parsing as plain number
                s.parse::<u32>()
                    .map_err(|_| Error::custom(format!("Invalid shard ID format: {}", s)))
            }
        }
    }
}

/// Complete cluster state export for debugging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterDebugExport {
    pub timestamp: String,
    pub gateway_version: String,
    pub cluster: ClusterStatusResponse,
    pub health: HealthResponse,
    pub shards: Vec<RangeInfoDto>,
}
