mod create;
mod dep;
mod doctor;
mod done;
mod export;
mod import;
mod label;
mod list;
mod log;
mod next;
mod project;
mod ready;
mod reopen;
mod rm;
mod search;
mod show;
mod skill;
mod stats;
pub mod sync;
mod tidy;
mod update;
mod webui;

use crate::cli::{Cli, Command};
use crate::db;
use crate::model::{Effort, Priority};
use anyhow::Result;

fn require_root() -> Result<std::path::PathBuf> {
    std::env::current_dir().map_err(Into::into)
}

pub fn dispatch(cli: &Cli) -> Result<()> {
    if let Some(project) = &cli.project {
        std::env::set_var(db::PROJECT_ENV, project);
    }

    match &cli.command {
        Command::Project { action } => project::run(action, cli.json),
        Command::Create {
            title,
            priority,
            effort,
            task_type,
            desc,
            parent,
            labels,
        } => {
            let root = require_root()?;
            create::run(
                &root,
                create::Opts {
                    title: title.as_deref(),
                    priority: Priority::parse(priority)?,
                    effort: Effort::parse(effort)?,
                    task_type,
                    desc: desc.as_deref(),
                    parent: parent.as_deref(),
                    labels: labels.as_deref(),
                    json: cli.json,
                },
            )
        }
        Command::List {
            status,
            priority,
            effort,
            label,
            task_type,
            all,
        } => {
            let root = require_root()?;
            let pri = priority.as_deref().map(Priority::parse).transpose()?;
            let eff = effort.as_deref().map(Effort::parse).transpose()?;
            list::run(
                &root,
                list::Opts {
                    status: status.as_deref(),
                    priority: pri,
                    effort: eff,
                    label: label.as_deref(),
                    task_type: task_type.as_deref(),
                    all: *all,
                    json: cli.json,
                },
            )
        }
        Command::Show { id } => {
            let root = require_root()?;
            show::run(&root, id, cli.json)
        }
        Command::Log { id, message } => {
            let root = require_root()?;
            log::run(&root, id, message, cli.json)
        }
        Command::Update {
            id,
            status,
            priority,
            effort,
            title,
            desc,
            task_type,
            parent,
        } => {
            let root = require_root()?;
            let pri = priority.as_deref().map(Priority::parse).transpose()?;
            let eff = effort.as_deref().map(Effort::parse).transpose()?;
            // --parent "" means clear; --parent <id> means set; absent means don't change.
            let parent_opt =
                parent
                    .as_ref()
                    .map(|p| if p.is_empty() { None } else { Some(p.as_str()) });
            update::run(
                &root,
                id,
                update::Opts {
                    status: status.as_deref(),
                    priority: pri,
                    effort: eff,
                    title: title.as_deref(),
                    desc: desc.as_deref(),
                    task_type: task_type.as_deref(),
                    parent: parent_opt,
                    json: cli.json,
                },
            )
        }
        Command::Done { ids } => {
            let root = require_root()?;
            done::run(&root, ids, cli.json)
        }
        Command::Rm {
            force,
            recursive,
            ids,
        } => {
            let root = require_root()?;
            rm::run(&root, ids, *recursive, *force, cli.json)
        }
        Command::Reopen { ids } => {
            let root = require_root()?;
            reopen::run(&root, ids, cli.json)
        }
        Command::Dep { action } => {
            let root = require_root()?;
            dep::run(&root, action, cli.json)
        }
        Command::Label { action } => {
            let root = require_root()?;
            label::run(&root, action, cli.json)
        }
        Command::Search { query } => {
            let root = require_root()?;
            search::run(&root, query, cli.json)
        }
        Command::Ready => {
            let root = require_root()?;
            ready::run(&root, cli.json)
        }
        Command::Next {
            mode,
            verbose,
            limit,
        } => {
            let root = require_root()?;
            next::run(&root, mode, *verbose, *limit, cli.json)
        }
        Command::Stats => {
            let root = require_root()?;
            stats::run(&root)
        }
        Command::Doctor { fix } => {
            let root = require_root()?;
            doctor::run(&root, *fix, cli.json)
        }
        Command::Tidy => {
            let root = require_root()?;
            tidy::run(&root)
        }
        Command::Export => {
            let root = require_root()?;
            export::run(&root)
        }
        Command::Import { file } => {
            let root = require_root()?;
            import::run(&root, file)
        }
        Command::Sync { code } => {
            let root = require_root()?;
            sync::run(&root, code.as_deref(), cli.json)
        }
        Command::WebUi { host, port } => {
            let root = require_root()?;
            webui::run(&root, host, *port, cli.project.as_deref())
        }
        Command::Skill { dir } => skill::run(dir.as_deref()),
    }
}
