//! Cluster management service implementation
//!
//! This service handles cluster operations and status queries.
//!
//! NOTE: Custom RaftCore does not yet support dynamic membership changes.
//! Member add/remove operations are disabled for now.

use crate::conversions::make_header;
use crate::proto::{
    cluster_server::Cluster, GetSnapshotRequest, GetSnapshotResponse, Member, MemberAddRequest,
    MemberAddResponse, MemberListRequest, MemberListResponse, MemberRemoveRequest,
    MemberRemoveResponse, SnapshotMeta, StatusRequest, StatusResponse, TransferSnapshotRequest,
    TransferSnapshotResponse,
};
use chainfire_raft::core::RaftCore;
use std::sync::Arc;
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tonic::{Request, Response, Status};
use tracing::{debug, info, warn};

/// Cluster service implementation
pub struct ClusterServiceImpl {
    /// Raft core
    raft: Arc<RaftCore>,
    /// gRPC Raft client for managing node addresses
    rpc_client: Arc<crate::GrpcRaftClient>,
    /// Cluster ID
    cluster_id: u64,
    /// Configured members with client and peer URLs
    members: Vec<Member>,
    /// Server version
    version: String,
}

impl ClusterServiceImpl {
    /// Create a new cluster service
    pub fn new(
        raft: Arc<RaftCore>,
        rpc_client: Arc<crate::GrpcRaftClient>,
        cluster_id: u64,
        members: Vec<Member>,
    ) -> Self {
        Self {
            raft,
            rpc_client,
            cluster_id,
            members,
            version: env!("CARGO_PKG_VERSION").to_string(),
        }
    }

    fn make_header(&self, revision: u64) -> crate::proto::ResponseHeader {
        make_header(self.cluster_id, self.raft.node_id(), revision, 0)
    }

    /// Get current members as proto Member list
    /// NOTE: Custom RaftCore doesn't track membership dynamically yet, so this returns
    /// the configured static membership that the server was booted with.
    async fn get_member_list(&self) -> Vec<Member> {
        if self.members.is_empty() {
            return vec![Member {
                id: self.raft.node_id(),
                name: format!("node-{}", self.raft.node_id()),
                peer_urls: vec![],
                client_urls: vec![],
                is_learner: false,
            }];
        }
        self.members.clone()
    }
}

#[tonic::async_trait]
impl Cluster for ClusterServiceImpl {
    async fn member_add(
        &self,
        request: Request<MemberAddRequest>,
    ) -> Result<Response<MemberAddResponse>, Status> {
        let req = request.into_inner();
        debug!(node_id = req.node_id, peer_urls = ?req.peer_urls, is_learner = req.is_learner, "Member add request");

        // Custom RaftCore doesn't support dynamic membership changes yet
        warn!("Member add not supported in custom Raft implementation");
        Err(Status::unimplemented(
            "Dynamic membership changes not supported in custom Raft implementation. \
             All cluster members must be configured at startup via initial_members."
        ))
    }

    async fn member_remove(
        &self,
        request: Request<MemberRemoveRequest>,
    ) -> Result<Response<MemberRemoveResponse>, Status> {
        let req = request.into_inner();
        debug!(member_id = req.id, "Member remove request");

        // Custom RaftCore doesn't support dynamic membership changes yet
        warn!("Member remove not supported in custom Raft implementation");
        Err(Status::unimplemented(
            "Dynamic membership changes not supported in custom Raft implementation"
        ))
    }

    async fn member_list(
        &self,
        _request: Request<MemberListRequest>,
    ) -> Result<Response<MemberListResponse>, Status> {
        debug!("Member list request");

        Ok(Response::new(MemberListResponse {
            header: Some(self.make_header(0)),
            members: self.get_member_list().await,
        }))
    }

    async fn status(
        &self,
        _request: Request<StatusRequest>,
    ) -> Result<Response<StatusResponse>, Status> {
        debug!("Status request");

        let leader = self.raft.leader().await;
        let term = self.raft.current_term().await;
        let commit_index = self.raft.commit_index().await;
        let last_applied = self.raft.last_applied().await;

        Ok(Response::new(StatusResponse {
            header: Some(self.make_header(last_applied)),
            version: self.version.clone(),
            db_size: 0, // TODO: get actual RocksDB size
            leader: leader.unwrap_or(0),
            raft_index: commit_index,
            raft_term: term,
            raft_applied_index: last_applied,
        }))
    }

    /// Transfer snapshot to a target node for pre-seeding (T041 Option C)
    ///
    /// This is a workaround for OpenRaft 0.9.x learner replication bug.
    /// By pre-seeding learners with a snapshot, we avoid the assertion failure
    /// during log replication.
    ///
    /// TODO(T041.S5): Full implementation pending - currently returns placeholder
    async fn transfer_snapshot(
        &self,
        request: Request<TransferSnapshotRequest>,
    ) -> Result<Response<TransferSnapshotResponse>, Status> {
        let req = request.into_inner();
        info!(
            target_node_id = req.target_node_id,
            target_addr = %req.target_addr,
            "Snapshot transfer request (T041 Option C)"
        );

        // Get current state from state machine
        let sm = self.raft.state_machine();
        let revision = sm.current_revision();
        let term = self.raft.current_term().await;
        let membership = self.raft.membership().await;

        let meta = SnapshotMeta {
            last_log_index: revision,
            last_log_term: term,
            membership: membership.clone(),
            size: 0, // Will be set when full impl is done
        };

        // TODO(T041.S5): Implement full snapshot transfer
        // 1. Serialize KV data using chainfire_storage::snapshot::SnapshotBuilder
        // 2. Stream snapshot to target via InstallSnapshot RPC
        // 3. Wait for target to apply snapshot
        //
        // For now, return success placeholder - the actual workaround can use
        // data directory copy (Option C1) until this API is complete.

        warn!(
            target = %req.target_addr,
            "TransferSnapshot not yet fully implemented - use data dir copy workaround"
        );

        Ok(Response::new(TransferSnapshotResponse {
            header: Some(self.make_header(revision)),
            success: false,
            error: "TransferSnapshot API not yet implemented - use data directory copy".to_string(),
            meta: Some(meta),
        }))
    }

    type GetSnapshotStream = ReceiverStream<Result<GetSnapshotResponse, Status>>;

    /// Get snapshot from this node as a stream of chunks
    ///
    /// TODO(T041.S5): Full implementation pending - currently returns empty snapshot
    async fn get_snapshot(
        &self,
        _request: Request<GetSnapshotRequest>,
    ) -> Result<Response<Self::GetSnapshotStream>, Status> {
        debug!("Get snapshot request (T041 Option C)");

        // Get current state from state machine
        let sm = self.raft.state_machine();
        let revision = sm.current_revision();
        let term = self.raft.current_term().await;
        let membership = self.raft.membership().await;

        let meta = SnapshotMeta {
            last_log_index: revision,
            last_log_term: term,
            membership,
            size: 0,
        };

        // Create channel for streaming response
        let (tx, rx) = mpsc::channel(4);

        // TODO(T041.S5): Stream actual KV data
        // For now, just send metadata with empty data
        tokio::spawn(async move {
            let response = GetSnapshotResponse {
                meta: Some(meta),
                chunk: vec![],
                done: true,
            };
            let _ = tx.send(Ok(response)).await;
        });

        Ok(Response::new(ReceiverStream::new(rx)))
    }
}
