// Copyright 2026 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: ISC

//! Stable exit codes via a top-level error type.

use std::process::ExitCode;

use vstorage::sync::{plan::PlanError, status::StatusError};

/// Top-level error type used to decide the process exit code.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Bad usage: {0}")]
    Usage(anyhow::Error),
    #[error("Configuration error: {0}")]
    Config(anyhow::Error),
    #[error("Fatal status database error: {0}")]
    StatusFatal(anyhow::Error),
    #[error("Aborted.")]
    UserAbort,
    #[error("Conflicts require manual resolution.")]
    Conflict,
    #[error(transparent)]
    Other(anyhow::Error),
}

impl From<Error> for ExitCode {
    /// Return the exit status associated with this error.
    ///
    /// Exit codes matching `sysexits.h` where a standard value exists.
    fn from(value: Error) -> Self {
        let value: u8 = match value {
            Error::Usage(_) => 64,       // EX_USAGE
            Error::StatusFatal(_) => 74, // EX_IOERR
            Error::Config(_) => 78,      // EX_CONFIG
            Error::UserAbort => 130,
            Error::Other(_) => 1,
            Error::Conflict => 3,
        };
        ExitCode::from(value)
    }
}

impl From<StatusError> for Error {
    fn from(error: StatusError) -> Self {
        Error::StatusFatal(error.into())
    }
}

impl From<PlanError> for Error {
    fn from(error: PlanError) -> Self {
        match error {
            PlanError::StatusDb(e) => Error::StatusFatal(e.into()),
            PlanError::FindStaleMappings(e) => Error::StatusFatal(e.into()),
            other => Error::Other(other.into()),
        }
    }
}
