use std::{fmt, str::FromStr};

macro_rules! string_enum {
    ($name:ident { $($variant:ident => $value:literal),+ $(,)? }) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub enum $name {
            $($variant),+
        }

        impl $name {
            pub const fn as_str(self) -> &'static str {
                match self {
                    $(Self::$variant => $value),+
                }
            }
        }

        impl fmt::Display for $name {
            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str(self.as_str())
            }
        }

        impl FromStr for $name {
            type Err = String;

            fn from_str(value: &str) -> Result<Self, Self::Err> {
                match value {
                    $($value => Ok(Self::$variant),)+
                    _ => Err(format!("unsupported {} {value:?}", stringify!($name))),
                }
            }
        }
    };
}

string_enum!(CategoryKind {
    Standard => "standard",
    Date => "date",
    Numeric => "numeric",
});

string_enum!(ViewKind {
    List => "list",
    Category => "category",
    Upcoming => "upcoming",
    Done => "done",
    Datebook => "datebook",
    Trash => "trash",
});

string_enum!(SortKey {
    Manual => "manual",
    When => "when",
    Done => "done",
    Priority => "priority",
    Updated => "updated",
});

string_enum!(Aggregate {
    None => "none",
    Sum => "sum",
    Average => "avg",
    Count => "count",
    Minimum => "min",
    Maximum => "max",
});

impl Aggregate {
    pub const fn label(self) -> &'static str {
        match self {
            Self::None => "",
            Self::Sum => "Total",
            Self::Average => "Average",
            Self::Count => "Count",
            Self::Minimum => "Minimum",
            Self::Maximum => "Maximum",
        }
    }
}

string_enum!(RuleActionKind {
    None => "none",
    Assign => "assign",
    Exclude => "exclude",
    Remove => "remove",
    Priority => "priority",
    Value => "value",
    Numeric => "numeric",
    Date => "date",
    When => "when",
    Alarm => "alarm",
    Repeat => "repeat",
    Done => "done",
    Export => "export",
    Discard => "discard",
});

string_enum!(TrashPolicy {
    OnDemand => "on-demand",
    OnClose => "on-close",
    EndOfDay => "end-of-day",
    Immediate => "immediate",
});

string_enum!(DonePolicy {
    Keep => "keep",
    Trash => "trash",
});

string_enum!(DateOrder {
    YearMonthDay => "ymd",
    MonthDayYear => "mdy",
    DayMonthYear => "dmy",
});

string_enum!(WeekStart {
    Monday => "monday",
    Sunday => "sunday",
});
