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

use crate::db;
use crate::editor;
use crate::ops;

pub struct Opts<'a> {
    pub status: Option<&'a str>,
    pub priority: Option<db::Priority>,
    pub effort: Option<db::Effort>,
    pub title: Option<&'a str>,
    pub desc: Option<&'a str>,
    pub json: bool,
}

pub fn run(root: &Path, id: &str, opts: Opts) -> Result<()> {
    let store = db::open(root)?;
    let task_id = db::resolve_task_id(&store, id, false)?;

    let parsed_status = opts.status.map(db::parse_status).transpose()?;

    // If no fields were supplied, open the editor so the user can revise the
    // task's title and description interactively.
    let editor_title;
    let editor_desc;
    let (title_override, desc_override) = if opts.status.is_none()
        && opts.priority.is_none()
        && opts.effort.is_none()
        && opts.title.is_none()
        && opts.desc.is_none()
    {
        let interactive = std::env::var("TD_FORCE_EDITOR").is_ok()
            || std::io::IsTerminal::is_terminal(&std::io::stdin());
        if interactive {
            // Load the current task so we can pre-populate the template.
            let task = store
                .get_task(&task_id, false)?
                .ok_or_else(|| anyhow!("task not found"))?;

            let template = format!(
                "{}\n\
                 \n\
                 {}\n\
                 \n\
                 TD: Edit the title and description above. The first line is the\n\
                 TD: title; everything after the blank line is the description.\n\
                 TD: Lines starting with 'TD: ' will be ignored. Saving an empty\n\
                 TD: message aborts the update.",
                task.title, task.description,
            );

            let (t, d) = editor::open(&template)?;
            editor_title = t;
            editor_desc = d;
            (Some(editor_title.as_str()), Some(editor_desc.as_str()))
        } else {
            return Err(anyhow!(
                "nothing to update; provide at least one flag or run interactively to open an editor"
            ));
        }
    } else {
        (opts.title, opts.desc)
    };

    let task = ops::update_task(
        &store,
        &task_id,
        ops::UpdateOpts {
            status: parsed_status,
            priority: opts.priority,
            effort: opts.effort,
            title: title_override.map(String::from),
            description: desc_override.map(String::from),
        },
    )?;

    if opts.json {
        println!("{}", serde_json::to_string(&task)?);
    } else {
        let c = crate::color::stdout_theme();
        println!("{}updated{} {}", c.green, c.reset, task_id);
    }

    Ok(())
}
