use anyhow::bail;
use std::fmt::Display;

/// Hamelin language operators.
/// Display returns the Hamelin syntax representation.
/// For SQL syntax, convert to Operator using TryFrom.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
    Minus,
    Plus,
    Concat,
    Asterisk,
    Slash,
    Percent,
    Eq,
    Neq,
    Lt,
    Lte,
    Gt,
    Gte,
    Not,
    And,
    Or,
    In,
    NotIn,
    Is,
    IsNot,
    Like,
    Range,
    RangeInclusive,
}

impl Operator {
    /// Parse from Hamelin syntax
    pub fn of(expression: &str) -> anyhow::Result<Self> {
        match expression {
            "-" => Ok(Self::Minus),
            "+" => Ok(Self::Plus),
            "||" => Ok(Self::Concat),
            "*" => Ok(Self::Asterisk),
            "/" => Ok(Self::Slash),
            "%" => Ok(Self::Percent),
            "==" => Ok(Self::Eq),
            "!=" => Ok(Self::Neq),
            "<>" => Ok(Self::Neq),
            "<" => Ok(Self::Lt),
            "<=" => Ok(Self::Lte),
            ">" => Ok(Self::Gt),
            ">=" => Ok(Self::Gte),
            "NOT" => Ok(Self::Not),
            "AND" => Ok(Self::And),
            "OR" => Ok(Self::Or),
            "IN" => Ok(Self::In),
            "NOT IN" => Ok(Self::NotIn),
            "IS" => Ok(Self::Is),
            "IS NOT" => Ok(Self::IsNot),
            "LIKE" => Ok(Self::Like),
            ".." => Ok(Self::Range),
            "..=" => Ok(Self::RangeInclusive),
            _ => bail!("Unknown operator: {}", expression),
        }
    }

    pub fn str(&self) -> &'static str {
        match self {
            Self::Minus => "-",
            Self::Plus => "+",
            Self::Concat => "||",
            Self::Asterisk => "*",
            Self::Slash => "/",
            Self::Percent => "%",
            Self::Eq => "==",
            Self::Neq => "!=",
            Self::Lt => "<",
            Self::Lte => "<=",
            Self::Gt => ">",
            Self::Gte => ">=",
            Self::Not => "NOT",
            Self::And => "AND",
            Self::Or => "OR",
            Self::In => "IN",
            Self::NotIn => "NOT IN",
            Self::Is => "IS",
            Self::IsNot => "IS NOT",
            Self::Like => "LIKE",
            Self::Range => "..",
            Self::RangeInclusive => "..=",
        }
    }
}

/// Display returns Hamelin syntax
impl Display for Operator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.str())
    }
}
