// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! Test error handling
//!
//! This module defines error types for integration test failures and configuration issues.
//! It provides comprehensive error handling for Docker operations, service configuration,
//! and test execution problems.
//!
//! ## Primary types
//! - [`TestError`]: main error enum for test failures and configuration issues
//! - [`DockerError`]: error type for Docker operations
//! - [`CredentialRetrievalError`]: error type for credential retrieval operations
//! - [`UnavailableRuntimeError`]: error type for runtime availability issues

/// Error type for integration tests.
#[derive(thiserror::Error, Debug)]
pub enum TestError {
    /// Represents an startup error.
    #[error("Startup problem: {0}")]
    Startup(String),

    /// Represents a low-level I/O error.
    #[error("Low level I/O problem: {0}")]
    Io(#[from] std::io::Error),

    /// Represents a low-level Docker error.
    #[error("Low level Docker problem: {0}")]
    Docker(#[from] DockerError),

    /// Represents an error indicating a wrong service configuration.
    #[error("Configuration not supported: {0}")]
    NotSupportedConfig(String),

    /// Represents an error indicating that there is not a known service with the specified name.
    #[error("Unknown service of type `{0}`")]
    UnknownService(&'static str),

    /// Represents an error indicating that there is not a known service with the specified
    /// hostname.
    #[error("Unknown service hostname `{0}`")]
    UnknownServiceHostname(String),

    /// Represents an error indicating that the async runtime was not configured.
    #[error("Unavailable runtime: {0}")]
    UnavailableRuntime(#[from] UnavailableRuntimeError),

    /// Represents an error indicating that the underlying async runtime
    /// does not support multithreading.
    #[error("Multithread runtime required")]
    UnavailableMultiThread,

    /// Represents an error there are not ports available to bind services.
    #[error("Unable to pick new ports")]
    UnavailablePorts,

    /// Represents an error indicating that the Docker daemon is not available.
    #[error("Unable to connect to docker daemon.")]
    UnavailableDocker(DockerError),

    /// Represents an error indicating that credentials cannot be retrieved.
    #[error("Can not retrieve credentials. Reason: `{0}`")]
    CredentialRetrieval(#[from] CredentialRetrievalError),
}

/// A Credential loading error
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct CredentialRetrievalError(#[from] docker_credential::CredentialRetrievalError);

impl From<docker_credential::CredentialRetrievalError> for TestError {
    fn from(value: docker_credential::CredentialRetrievalError) -> Self {
        CredentialRetrievalError(value).into()
    }
}

/// A low level Docker error.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct DockerError(#[from] bollard::errors::Error);

impl From<bollard::errors::Error> for TestError {
    fn from(value: bollard::errors::Error) -> Self {
        DockerError(value).into()
    }
}

/// An error indicating that the async runtime was not configured.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct UnavailableRuntimeError(#[from] tokio::runtime::TryCurrentError);

impl From<tokio::runtime::TryCurrentError> for TestError {
    fn from(value: tokio::runtime::TryCurrentError) -> Self {
        UnavailableRuntimeError(value).into()
    }
}
