use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeItem {
    pub name: String,
    pub tags: Vec<String>,
    pub last_used: Option<DateTime<Utc>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AppState {
    pub permanent_items: Vec<TimeItem>,
    pub recurring_items: Vec<TimeItem>,
    pub recent_items: Vec<TimeItem>,
    pub current_pane: usize,
    pub selected_indices: [usize; 3],
    #[serde(default)]
    pub current_column: usize,  // 0 = left, 1 = right
    pub active_timer: Option<(TimeItem, DateTime<Utc>)>,
}

impl Default for AppState {
    fn default() -> Self {
        Self {
            permanent_items: Vec::new(),
            recurring_items: Vec::new(),
            recent_items: Vec::new(),
            current_pane: 0,
            selected_indices: [0; 3],
            current_column: 0,
            active_timer: None,
        }
    }
}

impl AppState {
    pub fn config_dir() -> anyhow::Result<PathBuf> {
        let mut path =
            dirs::config_dir().ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?;
        path.push("wat");
        std::fs::create_dir_all(&path)?;
        Ok(path)
    }

    pub fn state_file() -> anyhow::Result<PathBuf> {
        let mut path = Self::config_dir()?;
        path.push("state.yaml");
        Ok(path)
    }

    pub fn config_file() -> anyhow::Result<PathBuf> {
        let mut path = Self::config_dir()?;
        path.push("config.yaml");
        Ok(path)
    }

    pub fn load() -> anyhow::Result<Self> {
        let path = Self::state_file()?;
        if !path.exists() {
            return Ok(Self::default());
        }
        let contents = std::fs::read_to_string(path)?;
        Ok(serde_yaml::from_str(&contents)?)
    }

    pub fn save(&self) -> anyhow::Result<()> {
        let path = Self::state_file()?;
        let contents = serde_yaml::to_string(&self)?;
        std::fs::write(path, contents)?;
        Ok(())
    }

    #[allow(dead_code)]
    pub fn add_recent_item(&mut self, item: TimeItem) {
        self.recent_items.insert(0, item);
        if self.recent_items.len() > 20 {
            self.recent_items.pop();
        }
    }

    pub fn start_timer(&mut self, item: TimeItem) -> anyhow::Result<()> {
        if let Some((_, _)) = self.active_timer {
            self.stop_timer()?;
        }

        let mut command = std::process::Command::new("watson");
        command.arg("start").arg(&item.name);
        for tag in &item.tags {
            let tag = tag.trim();
            if tag.is_empty() {
                continue;
            }
            if tag.starts_with('+') {
                command.arg(tag);
            } else {
                command.arg(format!("+{}", tag));
            }
        }

        let output = command.output()?;

        if !output.status.success() {
            anyhow::bail!(
                "Watson start failed: {}",
                String::from_utf8_lossy(&output.stderr).trim()
            );
        }

        self.active_timer = Some((item.clone(), Utc::now()));
        Ok(())
    }

    pub fn stop_timer(&mut self) -> anyhow::Result<()> {
        if let Some((ref item, _)) = self.active_timer {
            let output = std::process::Command::new("watson").arg("stop").output()?;

            if !output.status.success() {
                anyhow::bail!(
                    "Watson stop failed: {}",
                    String::from_utf8_lossy(&output.stderr).trim()
                );
            }

            let update_last_used = |items: &mut Vec<TimeItem>| {
                if let Some(target) = items.iter_mut().find(|i| i.name == item.name) {
                    target.last_used = Some(Utc::now());
                }
            };

            update_last_used(&mut self.permanent_items);
            update_last_used(&mut self.recurring_items);
            update_last_used(&mut self.recent_items);

            self.active_timer = None;
        }
        Ok(())
    }
}
