use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Category {
    Short,
    Medium,
    Long,
    Completed,
}

impl Category {
    pub fn as_str(&self) -> &'static str {
        match self {
            Category::Short => "Short",
            Category::Medium => "Medium",
            Category::Long => "Long",
            Category::Completed => "Completed",
        }
    }

    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "short" | "s" => Some(Category::Short),
            "medium" | "m" => Some(Category::Medium),
            "long" | "l" => Some(Category::Long),
            "completed" | "c" | "done" => Some(Category::Completed),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ColorTag {
    None,
    Red,
    Yellow,
    Green,
    Blue,
    Magenta,
    Cyan,
    Gray,
}

impl ColorTag {
    pub fn as_str(&self) -> &'static str {
        match self {
            ColorTag::None => "none",
            ColorTag::Red => "red",
            ColorTag::Yellow => "yellow",
            ColorTag::Green => "green",
            ColorTag::Blue => "blue",
            ColorTag::Magenta => "magenta",
            ColorTag::Cyan => "cyan",
            ColorTag::Gray => "gray",
        }
    }

    pub fn from_str(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "red" => ColorTag::Red,
            "yellow" => ColorTag::Yellow,
            "green" => ColorTag::Green,
            "blue" => ColorTag::Blue,
            "magenta" => ColorTag::Magenta,
            "cyan" => ColorTag::Cyan,
            "gray" => ColorTag::Gray,
            _ => ColorTag::None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubTask {
    pub id: u64,
    pub title: String,
    pub done: bool,
    pub created_at: i64,
    pub updated_at: i64,
    pub color: ColorTag,
    pub notes: Option<String>,
    #[serde(default)]
    pub subtasks: Vec<SubTask>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Todo {
    pub id: u64,
    pub title: String,
    pub notes: Option<String>,
    pub created_at: i64,
    pub updated_at: i64,
    pub color: ColorTag,
    pub subtasks: Vec<SubTask>,
}

#[derive(Debug, Clone, Default)]
pub struct State {
    pub next_id: u64,
    pub short: Vec<Todo>,
    pub medium: Vec<Todo>,
    pub long: Vec<Todo>,
    pub completed: Vec<Todo>,
}

impl State {
    pub fn new() -> Self {
        Self {
            next_id: 1,
            ..Default::default()
        }
    }

    pub fn get_category_mut(&mut self, cat: Category) -> &mut Vec<Todo> {
        match cat {
            Category::Short => &mut self.short,
            Category::Medium => &mut self.medium,
            Category::Long => &mut self.long,
            Category::Completed => &mut self.completed,
        }
    }

    pub fn get_category(&self, cat: Category) -> &Vec<Todo> {
        match cat {
            Category::Short => &self.short,
            Category::Medium => &self.medium,
            Category::Long => &self.long,
            Category::Completed => &self.completed,
        }
    }
    
    pub fn all_categories() -> [Category; 4] {
        [Category::Short, Category::Medium, Category::Long, Category::Completed]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_category_from_str() {
        assert_eq!(Category::from_str("short"), Some(Category::Short));
        assert_eq!(Category::from_str("s"), Some(Category::Short));
        assert_eq!(Category::from_str("MEDIUM"), Some(Category::Medium));
        assert_eq!(Category::from_str("l"), Some(Category::Long));
        assert_eq!(Category::from_str("done"), Some(Category::Completed));
        assert_eq!(Category::from_str("invalid"), None);
    }

    #[test]
    fn test_color_tag_from_str() {
        assert_eq!(ColorTag::from_str("red"), ColorTag::Red);
        assert_eq!(ColorTag::from_str("BLUE"), ColorTag::Blue);
        assert_eq!(ColorTag::from_str("none"), ColorTag::None);
        assert_eq!(ColorTag::from_str("invalid"), ColorTag::None);
    }
}

