use std::{any::Any, sync::Arc};

use crate::{error::Error, service_container::ServiceContainer};

/// Core service trait. Every type registered in a [`ServiceContainer`]
/// must implement it.
///
/// - `init` is the factory used by the container to build a new instance.
/// - `as_any` erases the concrete type so the container can store all
///   services behind a single `Arc<dyn Service>` and downcast on resolution.
pub trait Service: Send + Sync + 'static {
    fn init(service_provider: &dyn ServiceProvider) -> Arc<dyn Service>
    where
        Self: Sized;

    fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
}

/// Bridge between a concrete service and the trait object exposed to callers.
pub struct ServiceResolver<T: ?Sized + 'static> {
    pub as_interface: fn(service: Arc<dyn Any + Sync + Send + 'static>) -> Arc<T>,
}

/// Shared interface implemented by the root and scoped providers.
pub trait ServiceProvider {
    fn as_service_provider(&self) -> &dyn ServiceProvider;
    fn get_or_create_instance(&self, type_name: &str) -> Result<Arc<dyn Service>, Error>;
    fn get_service_container(&self) -> &ServiceContainer;
}

/// Get an instance of the specified type.
/// Initializes a new object according to the registered lifetime.
pub fn get_instance<T: Send + Sync + Service + 'static>(
    service_provider: &dyn ServiceProvider,
) -> Result<Arc<T>, Error> {
    let type_name = std::any::type_name::<T>();
    let service = service_provider.get_or_create_instance(type_name)?;

    // Return the created service
    Arc::downcast::<T>(service.as_any())
        .map_err(|_| Error::Internal("Cannot downcast service".to_string()))
}

/// Get an instance of the specified trait.
/// Initializes a new object according to the registered lifetime.
pub fn get_trait_instance<T: ?Sized + Send + Sync + 'static>(
    service_provider: &dyn ServiceProvider,
) -> Result<Arc<T>, Error> {
    let trait_name = std::any::type_name::<T>();
    let resolver_any = service_provider
        .get_service_container()
        .trait_service_map
        .get(trait_name)
        .ok_or_else(|| Error::Internal(format!("No trait mapping registered for {trait_name}")))?;

    // The service is registered in the container under the trait's type name
    // (see `ServiceContainer::add_trait_service`).
    let service = service_provider.get_or_create_instance(trait_name)?;

    // Get service resolver
    let service_resolver = resolver_any
        .downcast_ref::<ServiceResolver<T>>()
        .expect("Cannot get service resolver");

    Ok((service_resolver.as_interface)(service.as_any()))
}
