//! `CapabilityProvider` — adapts [`CapabilityBackend`] to the [`Provider`] trait.
//!
//! Phase 2 adapter from RFC-0032.  The existing `CapabilityBackend` (YAML-defined
//! REST API tools) is wrapped without modification.

use std::sync::Arc;

use async_trait::async_trait;
use serde_json::Value;

use super::{Provider, ProviderHealth, flatten_tool_call_result};
use crate::Result;
use crate::capability::CapabilityBackend;
use crate::protocol::Tool;

/// Provider adapter that wraps an existing [`CapabilityBackend`].
///
/// Capabilities are YAML-defined REST API tools executed directly by the
/// gateway.  This adapter surfaces them through the unified `Provider` API.
///
/// # Example
///
/// ```rust
/// use std::sync::Arc;
/// use mcp_gateway::provider::CapabilityProvider;
/// use mcp_gateway::capability::{CapabilityBackend, CapabilityExecutor};
///
/// let executor = Arc::new(CapabilityExecutor::new());
/// let backend = Arc::new(CapabilityBackend::new("rest_tools", executor));
/// let provider = Arc::new(CapabilityProvider::new(backend));
/// ```
pub struct CapabilityProvider {
    backend: Arc<CapabilityBackend>,
}

impl CapabilityProvider {
    /// Wrap an existing capability backend as a provider.
    #[must_use]
    pub fn new(backend: Arc<CapabilityBackend>) -> Self {
        Self { backend }
    }

    /// Access the underlying backend (e.g. for hot-reload).
    #[must_use]
    pub fn backend(&self) -> &Arc<CapabilityBackend> {
        &self.backend
    }
}

#[async_trait]
impl Provider for CapabilityProvider {
    fn name(&self) -> &str {
        &self.backend.name
    }

    async fn list_tools(&self) -> Result<Vec<Tool>> {
        Ok(self.backend.get_tools())
    }

    async fn invoke(&self, tool: &str, args: Value) -> Result<Value> {
        let result = self.backend.call_tool(tool, args).await?;
        flatten_tool_call_result(result)
    }

    async fn health(&self) -> ProviderHealth {
        if self.backend.is_empty() {
            ProviderHealth::Degraded("No capabilities loaded".to_string())
        } else {
            ProviderHealth::Healthy
        }
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::capability::{CapabilityBackend, CapabilityExecutor};

    fn make_backend() -> Arc<CapabilityBackend> {
        let executor = Arc::new(CapabilityExecutor::new());
        Arc::new(CapabilityBackend::new("test_caps", executor))
    }

    #[test]
    fn capability_provider_name_matches_backend() {
        let backend = make_backend();
        let provider = CapabilityProvider::new(backend);
        assert_eq!(provider.name(), "test_caps");
    }

    #[tokio::test]
    async fn capability_provider_lists_empty_tools_when_no_capabilities() {
        // GIVEN: a provider with no loaded capabilities
        let backend = make_backend();
        let provider = CapabilityProvider::new(backend);

        // WHEN: listing tools
        let tools = provider.list_tools().await.unwrap();

        // THEN: empty list
        assert!(tools.is_empty());
    }

    #[tokio::test]
    async fn capability_provider_health_degraded_when_empty() {
        // GIVEN: empty backend
        let backend = make_backend();
        let provider = CapabilityProvider::new(backend);

        // WHEN: checking health
        let health = provider.health().await;

        // THEN: degraded (no capabilities loaded)
        assert!(matches!(health, ProviderHealth::Degraded(_)));
    }

    #[tokio::test]
    async fn capability_provider_invoke_unknown_tool_returns_error() {
        // GIVEN: backend with no capabilities
        let backend = make_backend();
        let provider = CapabilityProvider::new(backend);

        // WHEN: invoking a nonexistent tool
        let result = provider.invoke("ghost_tool", serde_json::json!({})).await;

        // THEN: error returned
        assert!(result.is_err());
    }

    #[test]
    fn capability_provider_is_send_sync() {
        fn assert_send_sync<T: Send + Sync>() {}
        assert_send_sync::<CapabilityProvider>();
    }

    #[test]
    fn capability_provider_backend_accessor() {
        let backend = make_backend();
        let provider = CapabilityProvider::new(Arc::clone(&backend));
        assert_eq!(provider.backend().name, "test_caps");
    }
}
