use std::error::Error;
use std::fmt;

/// A custom error type for the configuration menu, encompassing various error scenarios that may occur during menu operation.
#[derive(Debug)]
pub enum ConfigEasyError {
    /// An error that occurred while interacting with the SQLite database.
    Database(rusqlite::Error),

    /// An error that occurred during input/output operations, such as reading from or writing to the terminal.
    Io(std::io::Error),

    /// An error indicating that a provided SQL identifier (e.g., table name, column name) is invalid.
    InvalidIdentifier(String),

    /// An error indicating that the user made an invalid selection in the menu (e.g. out of range).
    InvalidSelection,

    /// An error indicating that validation of a proposed setting value failed.
    ValidationFailed { key: String, message: String },

    /// An error indicating that a provided action key is invalid (e.g., empty or numeric).
    InvalidActionKey(String),

    /// A custom action attempted to use a reserved menu key.
    ReservedActionKey(String),

    /// A custom action attempted to use a key already used by another action.
    DuplicateActionKey(String),

    /// An error indicating that a custom action callback failed.
    ActionFailed { key: String, message: String },
}

impl fmt::Display for ConfigEasyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Database(error) => write!(f, "database error: {error}"),
            Self::Io(error) => write!(f, "I/O error: {error}"),
            Self::InvalidIdentifier(identifier) => {
                write!(f, "invalid SQL identifier: {identifier}")
            }
            Self::InvalidSelection => f.write_str("invalid menu selection"),
            Self::ValidationFailed { key, message } => {
                write!(f, "validation failed for '{key}': {message}")
            }
            Self::InvalidActionKey(key) => write!(f, "invalid action key: {key}"),
            Self::ReservedActionKey(key) => write!(f, "reserved action key: {key}"),
            Self::DuplicateActionKey(key) => write!(f, "duplicate action key: {key}"),
            Self::ActionFailed { key, message } => {
                write!(f, "action '{key}' failed: {message}")
            }
        }
    }
}

impl Error for ConfigEasyError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::Database(error) => Some(error),
            Self::Io(error) => Some(error),
            Self::InvalidIdentifier(_)
            | Self::InvalidSelection
            | Self::ValidationFailed { .. }
            | Self::InvalidActionKey(_)
            | Self::ReservedActionKey(_)
            | Self::DuplicateActionKey(_)
            | Self::ActionFailed { .. } => None,
        }
    }
}

impl From<rusqlite::Error> for ConfigEasyError {
    fn from(error: rusqlite::Error) -> Self {
        Self::Database(error)
    }
}

impl From<std::io::Error> for ConfigEasyError {
    fn from(error: std::io::Error) -> Self {
        Self::Io(error)
    }
}
