use anyhow::{anyhow, Result};
use std::collections::BTreeSet;
use std::path::Path;

use crate::cli::LabelAction;
use crate::db;
use crate::ops;

pub fn run(root: &Path, action: &LabelAction, json: bool) -> Result<()> {
    let store = db::open(root)?;

    match action {
        LabelAction::Add { id, label } => {
            let task_id = db::resolve_task_id(&store, id, false)?;
            ops::add_label(&store, &task_id, label)?;

            if json {
                println!("{}", serde_json::json!({"id": task_id, "label": label}));
            } else {
                let c = crate::color::stdout_theme();
                println!("{}added{} label {label}", c.green, c.reset);
            }
        }
        LabelAction::Rm { id, label } => {
            let task_id = db::resolve_task_id(&store, id, false)?;
            ops::remove_label(&store, &task_id, label)?;

            if !json {
                let c = crate::color::stdout_theme();
                println!("{}removed{} label {label}", c.green, c.reset);
            }
        }
        LabelAction::List { id } => {
            let task_id = db::resolve_task_id(&store, id, false)?;
            let task = store
                .get_task(&task_id, false)?
                .ok_or_else(|| anyhow!("task not found"))?;
            if json {
                println!("{}", serde_json::to_string(&task.labels)?);
            } else {
                for l in &task.labels {
                    println!("{l}");
                }
            }
        }
        LabelAction::ListAll => {
            let mut set = BTreeSet::new();
            for task in store.list_tasks()? {
                for label in task.labels {
                    set.insert(label);
                }
            }
            let labels: Vec<_> = set.into_iter().collect();
            if json {
                println!("{}", serde_json::to_string(&labels)?);
            } else {
                for l in &labels {
                    println!("{l}");
                }
            }
        }
    }

    Ok(())
}
