//! Service provider

use std::sync::Arc;

use bytes::Bytes;
use dashmap::DashMap;
use futures::future::{ready, BoxFuture};

/// Service provider
pub trait Provider<S: Sync + Send>: Sync + Send {
    //fn name(&self) -> &'static str;
    /// Call a method of this service
    fn call(
        &self,
        session: &Arc<S>,
        service: &str,
        method: &str,
        data: Bytes,
    ) -> BoxFuture<'static, Result<Bytes, ProviderError>>;
}

/// This trait adds a service name to the service provider
/// which is used for the router.
pub trait NamedProvider<S: Sync + Send>: Provider<S> {
    /// Name of the provided service
    const NAME: &'static str;
}

/// The response type used when calling providers.
pub type Response<T> = Result<T, ProviderError>;

/// Error returned by the `Provider::call` method
#[derive(Debug)]
pub enum ProviderError {
    /// The requested service was not found.
    ServiceNotFound,
    /// The requested method was not found.
    MethodNotFound,
    /// An error occured while deserializing the request.
    DeserializerError(serde_json::Error),
    /// An error occured while serializing the response.
    SerializerError(serde_json::Error),
    /// The request did not validate
    ValidationError(validator::ValidationErrors),
    /// Something else went wrong.
    InternalError(Box<dyn std::error::Error + Sync + Send>),
    /// The server is being shut down and therefore request processing is
    /// impossible. This error rarely if ever happens and should not be
    /// generated by user code.
    Shutdown,
}

impl ProviderError {
    /// Convert error to Bytes
    pub fn to_bytes(&self) -> Bytes {
        match self {
            Self::ServiceNotFound => "ServiceNotFound",
            Self::MethodNotFound => "MethodNotFound",
            // SerializerError maps to InternalError as this is nothing
            // the consumer can do about.
            Self::SerializerError(_) => "InternalError",
            Self::DeserializerError(_) => "DeserializerError",
            Self::ValidationError(_) => "ValidationError",
            Self::InternalError(_) => "InternalError",
            // This erros should never actually be returned to the client.
            Self::Shutdown => "InternalError",
        }
        .into()
    }
}

/// The router is used to register service provider and dispatch
/// requests.
pub struct Router<S: Sync + Send> {
    services: DashMap<String, Box<dyn Provider<S>>>,
}

impl<S: Sync + Send> Router<S> {
    /// Create an empty router
    pub fn new() -> Self {
        Self {
            services: DashMap::new(),
        }
    }
    /// Add service provider to this router
    pub fn service<P>(&self, provider: P)
    where
        P: NamedProvider<S> + 'static,
    {
        self.services.insert(P::NAME.to_owned(), Box::new(provider));
    }
}

impl<S: Sync + Send> Provider<S> for Router<S> {
    fn call(
        &self,
        session: &Arc<S>,
        service: &str,
        method: &str,
        data: Bytes,
    ) -> BoxFuture<'static, Result<Bytes, ProviderError>> {
        match self.services.get(service) {
            Some(provider) => provider.call(session, service, method, data),
            None => Box::pin(ready(Err(ProviderError::ServiceNotFound))),
        }
    }
}

impl<S: Sync + Send> Default for Router<S> {
    fn default() -> Self {
        Self::new()
    }
}
