//! Bot command I/O — the ONLY place in `service/bot_command/` that may call
//! transport handlers, load stores, spawn tasks, or interact with the agent
//! pool.
//!
//! Every function here is a thin adapter over the underlying matrix/timer/htop
//! handlers. No argument parsing or business logic lives here — that belongs in
//! `parse.rs` or `mod.rs`.

use crate::agents::AgentPool;
use std::path::Path;
use std::sync::Arc;

use super::parse::{AssignArgs, StartArgs};

/// Call the Matrix `assign` handler with pre-validated arguments.
pub(super) async fn call_assign(
    args: &AssignArgs,
    project_root: &Path,
    agents: &Arc<AgentPool>,
) -> String {
    crate::chat::transport::matrix::assign::handle_assign(
        "web-ui",
        &args.number,
        &args.model,
        project_root,
        agents,
    )
    .await
}

/// Call the Matrix `start` handler with pre-validated arguments.
pub(super) async fn call_start(
    args: &StartArgs,
    project_root: &Path,
    agents: &Arc<AgentPool>,
) -> String {
    crate::chat::transport::matrix::start::handle_start(
        "web-ui",
        &args.number,
        args.hint.as_deref(),
        project_root,
        agents,
    )
    .await
}

/// Call the Matrix `delete` handler with a pre-validated story number.
pub(super) async fn call_delete(
    number: &str,
    project_root: &Path,
    agents: &Arc<AgentPool>,
) -> String {
    crate::chat::transport::matrix::delete::handle_delete("web-ui", number, project_root, agents)
        .await
}

/// Call the Matrix `rmtree` handler with a pre-validated story number.
pub(super) async fn call_rmtree(
    number: &str,
    project_root: &Path,
    agents: &Arc<AgentPool>,
) -> String {
    crate::chat::transport::matrix::rmtree::handle_rmtree("web-ui", number, project_root, agents)
        .await
}

/// Parse and execute a `timer` command.
///
/// Returns `Err` with a usage string if the timer arguments cannot be parsed.
pub(super) async fn call_timer(args: &str, project_root: &Path) -> Result<String, String> {
    let synthetic = format!("__web_ui__ timer {args}");
    let timer_cmd = match crate::service::timer::extract_timer_command(
        &synthetic,
        "__web_ui__",
        "@__web_ui__:localhost",
    ) {
        Some(cmd) => cmd,
        None => {
            return Err(
                "Usage: `/timer list`, `/timer <number> <HH:MM>`, or `/timer cancel <number>`"
                    .to_string(),
            );
        }
    };
    let store =
        crate::service::timer::TimerStore::load(project_root.join(".huskies").join("timers.json"));
    Ok(crate::service::timer::handle_timer_command(timer_cmd, &store, project_root).await)
}

/// Build an `htop` snapshot for the web UI.
///
/// The web UI uses one-shot HTTP requests, so live-updating sessions are not
/// supported. `htop stop` returns a helpful explanation instead of an error.
pub(super) fn call_htop(args: &str, agents: &Arc<AgentPool>) -> String {
    use crate::chat::transport::matrix::htop::{HtopCommand, build_htop_message};

    let synthetic = if args.is_empty() {
        "__web_ui__ htop".to_string()
    } else {
        format!("__web_ui__ htop {args}")
    };

    match crate::chat::transport::matrix::htop::extract_htop_command(
        &synthetic,
        "__web_ui__",
        "@__web_ui__:localhost",
    ) {
        Some(HtopCommand::Stop) => "No active htop session in the web UI. \
             Live sessions are only supported in chat transports (Matrix, Slack, Discord)."
            .to_string(),
        Some(HtopCommand::Start { duration_secs }) => build_htop_message(agents, 0, duration_secs),
        None => build_htop_message(agents, 0, 300),
    }
}

/// Dispatch through the synchronous command registry.
///
/// Returns `Some(response)` if the command keyword is registered, or `None`
/// if the keyword is unknown.
pub(super) fn call_sync(
    cmd: &str,
    args: &str,
    project_root: &Path,
    agents: &Arc<AgentPool>,
) -> Option<String> {
    use crate::chat::commands::CommandDispatch;
    use crate::service::permission_router::{PendingPermReplies, ResponderRegistry};
    use std::collections::HashSet;
    use std::sync::Mutex;

    let bot_name = "__web_ui__";
    let bot_user_id = "@__web_ui__:localhost";
    let room_id = "__web_ui__";

    let services = Arc::new(crate::services::Services {
        project_root: project_root.to_path_buf(),
        agents: Arc::clone(agents),
        bot_name: bot_name.to_string(),
        bot_user_id: bot_user_id.to_string(),
        ambient_rooms: Arc::new(Mutex::new(HashSet::new())),
        permission_registry: ResponderRegistry::new(),
        pending_perm_replies: PendingPermReplies::new(),
        permission_timeout_secs: 120,
        status: Arc::new(crate::service::status::StatusBroadcaster::new()),
        chat_dispatcher: Arc::new(crate::chat::dispatcher::ChatDispatcher::new(1_500)),
    });

    let dispatch = CommandDispatch {
        services: &services,
        project_root,
        bot_user_id,
        room_id,
    };

    // Build a synthetic bot-addressed message so the registry parses it
    // identically to messages from chat transports.
    let synthetic = if args.is_empty() {
        format!("{bot_name} {cmd}")
    } else {
        format!("{bot_name} {cmd} {args}")
    };

    crate::chat::commands::try_handle_command(&dispatch, &synthetic)
}
