use async_trait::async_trait;

use crate::error::KnafehError;
use crate::rpc::message::{RpcRequest, RpcResponse};
use crate::rpc::stream::{RpcStreamRequest, RpcStreamResponse};

/// Describes the type of an RPC method.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MethodKind {
    /// Unary request/response.
    Unary,
    /// Server streams response chunks to the client.
    ServerStreaming,
    /// Client streams request chunks to the server.
    ClientStreaming,
    /// Both sides stream concurrently.
    BidiStreaming,
}

/// Metadata about a single RPC method.
#[derive(Debug, Clone)]
pub struct MethodDescriptor {
    pub name: String,
    pub kind: MethodKind,
}

/// A service that handles RPC calls.
///
/// Implement this trait to define your RPC service. Each service has a name
/// and a set of methods. The framework routes incoming requests by matching
/// `"{service_name}/{method_name}"` paths.
#[async_trait]
pub trait Service: Send + Sync + 'static {
    /// The service name used for routing (e.g., `"greeter"`).
    fn name(&self) -> &str;

    /// List all methods this service exposes.
    fn methods(&self) -> Vec<MethodDescriptor>;

    /// Handle a unary RPC call.
    async fn call_unary(
        &self,
        method: &str,
        request: RpcRequest,
    ) -> Result<RpcResponse, KnafehError>;

    /// Handle a server-streaming RPC call.
    async fn call_server_stream(
        &self,
        method: &str,
        request: RpcRequest,
    ) -> Result<RpcStreamResponse, KnafehError>;

    /// Handle a client-streaming RPC call.
    async fn call_client_stream(
        &self,
        method: &str,
        stream: RpcStreamRequest,
    ) -> Result<RpcResponse, KnafehError>;

    /// Handle a bidirectional-streaming RPC call.
    async fn call_bidi_stream(
        &self,
        method: &str,
        stream: RpcStreamRequest,
    ) -> Result<RpcStreamResponse, KnafehError>;
}
