use std::sync::Arc;

use crate::HandleID;

/// A boxed error type for process handler return values.
/// This allows users to return any error type from their process handlers.
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;

/// Result type for process handler functions.
pub type ProcessResult = Result<(), BoxError>;

/// Error type for noprocess library operations.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// The process runner has terminated and is no longer accepting commands.
    #[error("runner terminated")]
    RunnerTerminated,
}

/// Information about a process execution failure.
/// Used in the on_error callback to provide details about what went wrong.
#[derive(Debug, Clone)]
pub struct ProcessFailure {
    pub handle_id: HandleID,
    pub kind: ProcessFailureKind,
}

/// The kind of process execution failure.
#[derive(Debug, Clone)]
pub enum ProcessFailureKind {
    /// The process returned an error.
    Returned(String),
    /// The process panicked.
    Panicked(String),
}

impl std::fmt::Display for ProcessFailure {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.kind {
            ProcessFailureKind::Returned(e) => {
                write!(f, "process {} returned error: {}", self.handle_id, e)
            }
            ProcessFailureKind::Panicked(e) => {
                write!(f, "process {} panicked: {}", self.handle_id, e)
            }
        }
    }
}

impl std::error::Error for ProcessFailure {}

/// Type alias for error handler callback.
pub type ErrorHandler = Arc<dyn Fn(ProcessFailure) + Send + Sync + 'static>;
