//! The `Executor` trait — how plugins send work to PHP workers.
//!
//! `folk-core` provides the concrete implementation, backed by the worker pool.

use std::sync::Arc;

use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;

/// Sends a serialized payload to a PHP worker and returns the response.
///
/// Plugins call this; they never see the worker pool directly.
#[async_trait]
pub trait Executor: Send + Sync + 'static {
    /// Send `payload` to a worker and return the response bytes.
    /// Uses the RPC method name "dispatch" by default.
    async fn execute(&self, payload: Bytes) -> Result<Bytes> {
        self.execute_method("dispatch", payload).await
    }

    /// Send `payload` to a worker using a specific RPC method name.
    async fn execute_method(&self, method: &str, payload: Bytes) -> Result<Bytes>;

    /// Send structured data directly (no serialization).
    ///
    /// The default implementation falls back to JSON serialization
    /// via [`execute_method`]. Extension runtimes override this to
    /// pass values through the channel without JSON encode/decode.
    async fn execute_value(
        &self,
        method: &str,
        payload: serde_json::Value,
    ) -> Result<serde_json::Value> {
        let bytes = serde_json::to_vec(&payload)?;
        let result = self.execute_method(method, Bytes::from(bytes)).await?;
        Ok(serde_json::from_slice(&result)?)
    }

    /// Like [`execute_value`], but also returns the `request_id` under which the
    /// request was executed — the same id exposed to PHP via `folk_request_id()`.
    ///
    /// Plugins that maintain an access log (e.g. HTTP) use this to record the id
    /// for log correlation. The default implementation calls [`execute_value`]
    /// and returns an empty id (runtimes that don't track one).
    async fn execute_value_traced(
        &self,
        method: &str,
        payload: serde_json::Value,
    ) -> Result<(serde_json::Value, Arc<str>)> {
        let value = self.execute_value(method, payload).await?;
        Ok((value, Arc::from("")))
    }
}

/// Blanket impl: an `Arc<dyn Executor>` is also an `Executor`.
#[async_trait]
impl<T: Executor + ?Sized> Executor for Arc<T> {
    async fn execute_method(&self, method: &str, payload: Bytes) -> Result<Bytes> {
        (**self).execute_method(method, payload).await
    }

    async fn execute_value(
        &self,
        method: &str,
        payload: serde_json::Value,
    ) -> Result<serde_json::Value> {
        (**self).execute_value(method, payload).await
    }

    async fn execute_value_traced(
        &self,
        method: &str,
        payload: serde_json::Value,
    ) -> Result<(serde_json::Value, Arc<str>)> {
        (**self).execute_value_traced(method, payload).await
    }
}
