//! Framework error types.

/// Convenient result type for Nidus operations.
pub type Result<T> = std::result::Result<T, NidusError>;

/// Errors emitted by Nidus core primitives.
#[derive(Debug, thiserror::Error)]
pub enum NidusError {
    /// A dependency was requested but no provider exists for its concrete type.
    #[error("missing provider for type `{type_name}`")]
    MissingProvider {
        /// Rust type name requested from the container.
        type_name: &'static str,
    },

    /// A provider was registered more than once for the same concrete type.
    #[error("duplicate provider for type `{type_name}`")]
    DuplicateProvider {
        /// Rust type name registered more than once.
        type_name: &'static str,
    },

    /// A request-scoped provider was resolved outside an explicit request scope.
    #[error("request-scoped provider `{type_name}` must be resolved through RequestScope")]
    RequestScopeRequired {
        /// Rust type name requested from the root container.
        type_name: &'static str,
    },

    /// A provider factory recursively requested a provider already being built.
    #[error("circular provider resolution detected for type `{type_name}`")]
    CircularProviderResolution {
        /// Rust type name that was requested recursively.
        type_name: &'static str,
    },

    /// A module was registered more than once with the same name.
    #[error("duplicate module `{module}`")]
    DuplicateModule {
        /// Module name registered more than once.
        module: String,
    },

    /// A module declares the same provider more than once.
    #[error("module `{module}` declares duplicate provider `{provider}`")]
    DuplicateModuleProvider {
        /// Module declaring the duplicate provider.
        module: String,
        /// Provider name declared more than once.
        provider: String,
    },

    /// A module declares the same controller more than once.
    #[error("module `{module}` declares duplicate controller `{controller}`")]
    DuplicateModuleController {
        /// Module declaring the duplicate controller.
        module: String,
        /// Controller name declared more than once.
        controller: String,
    },

    /// A module declares the same type name as both a provider and a controller.
    #[error("module `{module}` declares `{type_name}` as both provider and controller")]
    ModuleProviderControllerConflict {
        /// Module declaring the conflicting metadata.
        module: String,
        /// Type name declared in both metadata sections.
        type_name: String,
    },

    /// A module imports the same module more than once.
    #[error("module `{module}` imports `{import}` more than once")]
    DuplicateModuleImport {
        /// Module declaring the duplicate import.
        module: String,
        /// Imported module named more than once.
        import: String,
    },

    /// A module exports the same provider more than once.
    #[error("module `{module}` exports `{provider}` more than once")]
    DuplicateModuleExport {
        /// Module declaring the duplicate export.
        module: String,
        /// Provider export named more than once.
        provider: String,
    },

    /// A registered provider factory returned an error.
    #[error("provider factory failed for type `{type_name}`: {source}")]
    ProviderFactory {
        /// Rust type name whose factory failed.
        type_name: &'static str,
        /// Underlying framework error.
        #[source]
        source: Box<NidusError>,
    },

    /// A module imports another module that is not present in the graph.
    #[error("module `{module}` imports missing module `{import}`")]
    MissingModuleImport {
        /// Module declaring the import.
        module: String,
        /// Missing imported module.
        import: String,
    },

    /// A circular module import chain was detected.
    #[error("circular module import detected: {}", cycle.join(" -> "))]
    CircularModuleImport {
        /// Ordered cycle path.
        cycle: Vec<String>,
    },

    /// A module exports a provider it does not own.
    #[error("module `{module}` exports missing local provider `{provider}`")]
    MissingProviderExport {
        /// Module declaring the export.
        module: String,
        /// Provider missing from the module's provider list.
        provider: String,
    },

    /// A module declares a local provider that is also exported by one of its imports.
    #[error(
        "module `{module}` declares provider `{provider}` that conflicts with export from `{import}`"
    )]
    ProviderVisibilityConflict {
        /// Module with the conflicting provider visibility.
        module: String,
        /// Provider name declared locally and exported by an import.
        provider: String,
        /// Imported module exporting the same provider name.
        import: String,
    },

    /// A module can see multiple providers with the same name through imports.
    #[error(
        "module `{module}` has ambiguous provider `{provider}` from imports: {}",
        imports.join(", ")
    )]
    AmbiguousProvider {
        /// Module with ambiguous visibility.
        module: String,
        /// Provider name that is visible from more than one import.
        provider: String,
        /// Imports exporting the same provider.
        imports: Vec<String>,
    },

    /// A lifecycle startup hook failed after one or more hooks may have started.
    #[error("lifecycle startup failed: {source}")]
    LifecycleStartup {
        /// Original startup failure.
        #[source]
        source: Box<NidusError>,
        /// Shutdown failures encountered while rolling back already-started hooks.
        rollback_errors: Vec<NidusError>,
    },

    /// Application composition failed during high-level bootstrap.
    #[error("application build failed: {message}")]
    ApplicationBuild {
        /// Human-readable build failure.
        message: String,
    },
}
