use crate::*;
use std::fmt;
// use std::error::Error as StdError;

// #[repr(u8)]
// pub enum SwitchboardFunctionError {
//     /// The function account has invalid permissions for its queue
//     InvalidPermissions = 250,
//     /// Failed to find the function result in the emitted container logs
//     FunctionResultNotFound = 253,
//     /// Failed to execute the function's callback
//     FunctionCallbackError = 254,
//     /// Function failed to emit a result before the oracles timeout
//     FunctionTimeout = 255,
// }

use serde::ser::StdError;
type ParentError = Arc<dyn StdError + Send + Sync + 'static>;

#[derive(Clone, Debug)]
pub enum OnDemandError {
    // Generics
    Generic,
    Message(&'static str),
    CustomMessage(String),
    CustomError {
        message: String,
        source: ParentError,
    },
    Unexpected,
    // Environment Errors
    EnvVariableMissing(String),
    InvalidKeypairFile,
    KeyParseError,
    CheckSizeError,

    IoError(ParentError),

    // SGX Errors
    SgxError,
    SgxWriteError,

    // Network Errors
    NetworkError,

    // Quote Errors
    QuoteParseError,
    InvalidQuoteError,

    // QvnErrors
    QvnError(Arc<String>),

    // Docker/Container Errors
    DockerError,
    DockerFetchError,
    FunctionImageTooBigError,
    ContainerErrorMessage(String),
    ContainerError(ParentError),
    ContainerStartError(ParentError),
    ContainerCreateError(ParentError),
    ContainerNeedsUpdate,
    // ContainerCreateError,
    ContainerResultParseError,
    AttachError,
    ContainerTimeout,
    ContainerActive,
    ContainerBackoff(u64),
    FunctionErrorCountExceeded(u32),

    // Function Errors
    FunctionResultParseError,
    IllegalFunctionOutput,
    FunctionVerifyFailure,
    FunctionResultIllegalAccount,
    FunctionResultAccountsMismatch,
    FunctionResultInvalidData,
    FunctionResultInvalidPid,
    FunctionResultEmptyInstructions,

    // Transaction Errors
    TxFailure,
    TxCompileErr,
    TxDeserializationError,
    QvnTxSendFailure,
    InvalidInstructionError,

    // Chain specific Errors
    InvalidChain,
    AnchorParse,
    AnchorParseError,
    EvmError,

    // Misc
    IpfsParseError,
    IpfsNetworkError,
    HeartbeatRoutineFailure,
    EventListenerRoutineFailure,
    DecryptError,
    ParseError,
    MrEnclaveMismatch,
    FunctionResultIxIncorrectTargetChain,
    InvalidSignature,

    // Solana
    /// Failed to fetch a network resource
    SolanaFetchError(String),
    /// Failed to fetch a blockhash from the cluster
    SolanaBlockhashError,
    /// Failed to fetch a blockhash from the cluster
    SolanaBlockhashFetchError(ParentError),
    /// THe provided payer does not match the payer of the transaction
    /// Expected vs actual
    SolanaPayerMismatch(String, String),
    SolanaPayerSignerMissing(String),
    /// A required Solana signer is missing
    SolanaMissingSigner(String),
    SolanaSignError(ParentError, String),
    SolanaInstructionsEmpty,
    SolanaInstructionOverflow,
    FunctionResultIxMissingDiscriminator,
    FunctionResultError(&'static str),
    FunctionResultIxError(&'static str),
    // An error which should fail to send the user generated transaction and should emit an error code
    FunctionResultFailoverError(u8, ParentError),
    // An error which should not be retried and should be dropped by the QVN.
    FunctionResultNonRetryableError(ParentError),

    AccountNotFound,
    DecimalConversionError,
    IntegerOverflowError,
    InvalidNativeMint,
    PdaDerivationError,
    InvalidQuote,
}

impl StdError for OnDemandError {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            OnDemandError::CustomError { source, .. } => Some(source.as_ref()), // Handle other error variants as needed
            _ => None,
        }
    }
}
impl fmt::Display for OnDemandError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OnDemandError::EnvVariableMissing(message) => {
                write!(f, "Env variable missing: {}", message.as_str())
            }
            OnDemandError::Message(message) => write!(f, "error: {}", message),
            OnDemandError::CustomMessage(message) => write!(f, "error: {}", message.as_str()),
            OnDemandError::CustomError {
                message, source, ..
            } => write!(f, "error: {} - {:?}", message.as_str(), source),
            OnDemandError::FunctionResultError(message) => {
                write!(f, "error: FunctionResultError - {}", message)
            }
            OnDemandError::FunctionResultIxError(message) => {
                write!(f, "error: FunctionResultIxError - {}", message)
            }
            OnDemandError::FunctionResultFailoverError(code, source) => {
                write!(
                    f,
                    "error: FunctionResultFailoverError ({}) - {:?}",
                    code, source
                )
            }
            OnDemandError::FunctionResultNonRetryableError(source) => {
                write!(f, "error: FunctionResultNonRetryableError - {:?}", source)
            }
            OnDemandError::SolanaPayerMismatch(expected, actual) => {
                write!(
                    f,
                    "error: SolanaPayerMismatch - expected: {}, actual: {}",
                    expected, actual
                )
            }
            OnDemandError::SolanaMissingSigner(missing_signer) => {
                write!(f, "error: Missing required signer: {}", missing_signer)
            }
            OnDemandError::SolanaInstructionsEmpty => write!(
                f,
                "error: The attempted action requires at least one instruction but none were provided"
            ),
            OnDemandError::SolanaInstructionOverflow => write!(
                f,
                "error: The transaction exceeded the maximum number of instructions (10)"
            ),
            OnDemandError::SolanaPayerSignerMissing(payer) => write!(
                f,
                "error: The payer keypair is missing from the provided signers: {}",
                payer
            ),
            OnDemandError::SolanaBlockhashFetchError(source) => write!(
                f,
                "error: Failed to fetch blockhash from the cluster. Please try again. - {:?}", source
            ),
            // Handle other error variants as needed
            _ => write!(f, "{:#?}", self),
        }
    }
}
