//! Integration test: a fake plugin and a fake executor wire up correctly.

use std::sync::Arc;

use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;
use folk_api::{Executor, Plugin, PluginContext, RpcMethodDef};
use tokio::sync::watch;

struct StubExecutor;

#[async_trait]
impl Executor for StubExecutor {
    async fn execute_method(&self, _: &str, payload: Bytes) -> Result<Bytes> {
        Ok(payload)
    }
}

struct DemoPlugin {
    booted: bool,
}

#[async_trait]
impl Plugin for DemoPlugin {
    fn name(&self) -> &'static str {
        "demo"
    }

    async fn boot(&mut self, _ctx: PluginContext) -> Result<()> {
        self.booted = true;
        Ok(())
    }

    async fn shutdown(&self) -> Result<()> {
        Ok(())
    }

    fn rpc_methods(&self) -> Vec<RpcMethodDef> {
        vec![RpcMethodDef::new("demo.ping", "ping the demo plugin")]
    }
}

#[tokio::test]
async fn plugin_boot_and_executor_round_trip() {
    let (_tx, rx) = watch::channel(false);

    let mut plugin = DemoPlugin { booted: false };
    let executor: Arc<dyn Executor> = Arc::new(StubExecutor);

    let ctx = PluginContext {
        executor: executor.clone(),
        shutdown: rx,
        rpc_registrar: None,
        health_registry: None,
        metrics_registry: None,
    };

    plugin.boot(ctx).await.unwrap();
    assert!(plugin.booted);

    // Round-trip through the executor:
    let payload = Bytes::from("hello");
    let response = executor.execute(payload.clone()).await.unwrap();
    assert_eq!(response, payload);
}

#[tokio::test]
async fn execute_value_default_roundtrips_via_json() {
    let executor: Arc<dyn Executor> = Arc::new(StubExecutor);
    let payload = serde_json::json!({"key": "value", "num": 42});
    let result = executor
        .execute_value("test", payload.clone())
        .await
        .unwrap();
    assert_eq!(result, payload);
}

#[test]
fn folk_api_version_is_set() {
    assert!(!folk_api::FOLK_API_VERSION.is_empty());
    assert!(folk_api::FOLK_API_VERSION.starts_with("0."));
}
