//! Internal Raft RPC service implementation
//!
//! This service handles Raft protocol messages between nodes in the cluster.
//! It bridges the gRPC layer with the custom Raft implementation.

use crate::internal_proto::{
    raft_service_server::RaftService,
    AppendEntriesRequest as ProtoAppendEntriesRequest,
    AppendEntriesResponse as ProtoAppendEntriesResponse,
    InstallSnapshotRequest, InstallSnapshotResponse,
    VoteRequest as ProtoVoteRequest,
    VoteResponse as ProtoVoteResponse,
};
use chainfire_raft::core::{
    RaftCore, VoteRequest, AppendEntriesRequest,
};
use chainfire_storage::{LogId, LogEntry as RaftLogEntry, EntryPayload};
use chainfire_types::command::RaftCommand;
use std::sync::Arc;
use tokio::sync::oneshot;
use tonic::{Request, Response, Status, Streaming};
use tracing::{debug, info, trace, warn};

/// Internal Raft RPC service implementation
///
/// This service handles Raft protocol messages between nodes.
pub struct RaftServiceImpl {
    /// Reference to the Raft core
    raft: Arc<RaftCore>,
}

impl RaftServiceImpl {
    /// Create a new Raft service with a RaftCore instance
    pub fn new(raft: Arc<RaftCore>) -> Self {
        Self { raft }
    }
}

#[tonic::async_trait]
impl RaftService for RaftServiceImpl {
    async fn vote(
        &self,
        request: Request<ProtoVoteRequest>,
    ) -> Result<Response<ProtoVoteResponse>, Status> {
        let req = request.into_inner();
        info!(
            term = req.term,
            candidate = req.candidate_id,
            "Vote request received"
        );

        // Convert proto request to custom Raft request
        let vote_req = VoteRequest {
            term: req.term,
            candidate_id: req.candidate_id,
            last_log_index: req.last_log_index,
            last_log_term: req.last_log_term,
        };

        // Forward to Raft core using oneshot channel
        let (resp_tx, resp_rx) = oneshot::channel();
        self.raft.request_vote_rpc(vote_req, resp_tx).await;

        // Wait for response
        let resp = resp_rx.await.map_err(|e| {
            warn!(error = %e, "Vote request channel closed");
            Status::internal("Vote request failed: channel closed")
        })?;

        trace!(term = resp.term, granted = resp.vote_granted, "Vote response");
        Ok(Response::new(ProtoVoteResponse {
            term: resp.term,
            vote_granted: resp.vote_granted,
            last_log_index: 0, // Not used in custom impl
            last_log_term: 0,  // Not used in custom impl
        }))
    }

    async fn append_entries(
        &self,
        request: Request<ProtoAppendEntriesRequest>,
    ) -> Result<Response<ProtoAppendEntriesResponse>, Status> {
        let req = request.into_inner();
        info!(
            term = req.term,
            leader = req.leader_id,
            entries = req.entries.len(),
            "AppendEntries request received"
        );

        // Convert proto entries to custom Raft entries
        let entries: Vec<RaftLogEntry<RaftCommand>> = req
            .entries
            .into_iter()
            .map(|e| {
                let payload = if e.data.is_empty() {
                    EntryPayload::Blank
                } else {
                    // Deserialize the command from the entry data
                    match bincode::deserialize::<RaftCommand>(&e.data) {
                        Ok(cmd) => EntryPayload::Normal(cmd),
                        Err(_) => EntryPayload::Blank,
                    }
                };

                RaftLogEntry {
                    log_id: LogId {
                        term: e.term,
                        index: e.index,
                    },
                    payload,
                }
            })
            .collect();

        let append_req = AppendEntriesRequest {
            term: req.term,
            leader_id: req.leader_id,
            prev_log_index: req.prev_log_index,
            prev_log_term: req.prev_log_term,
            entries,
            leader_commit: req.leader_commit,
        };

        // Forward to Raft core using oneshot channel
        let (resp_tx, resp_rx) = oneshot::channel();
        self.raft.append_entries_rpc(append_req, resp_tx).await;

        // Wait for response
        let resp = resp_rx.await.map_err(|e| {
            warn!(error = %e, "AppendEntries request channel closed");
            Status::internal("AppendEntries request failed: channel closed")
        })?;

        trace!(success = resp.success, "AppendEntries response");
        Ok(Response::new(ProtoAppendEntriesResponse {
            term: resp.term,
            success: resp.success,
            conflict_index: resp.conflict_index.unwrap_or(0),
            conflict_term: resp.conflict_term.unwrap_or(0),
        }))
    }

    async fn install_snapshot(
        &self,
        request: Request<Streaming<InstallSnapshotRequest>>,
    ) -> Result<Response<InstallSnapshotResponse>, Status> {
        let mut stream = request.into_inner();
        debug!("InstallSnapshot stream started");

        // Collect all chunks (for compatibility)
        while let Some(chunk) = stream.message().await? {
            if chunk.done {
                break;
            }
        }

        // Custom Raft doesn't support snapshots yet
        warn!("InstallSnapshot not supported in custom Raft implementation");
        Err(Status::unimplemented("Snapshots not supported in custom Raft implementation"))
    }
}
