use clap::CommandFactory;
use serde_json::Value;
use crate::cli::{Cli, Sub};
use crate::{client, mapping};

/// Entry point: returns the process exit code.
pub async fn run(cli: Cli) -> i32 {
    // Completions are local — no daemon.
    if let Sub::Completions { shell } = cli.cmd {
        let mut cmd = Cli::command();
        clap_complete::generate(shell, &mut cmd, "spacesh", &mut std::io::stdout());
        return 0;
    }

    // notify is best-effort: never fails the caller.
    if let Sub::Notify { .. } = &cli.cmd {
        let _ = client::notify(mapping::to_cmd(cli.cmd)).await;
        return 0;
    }

    let is_status = matches!(cli.cmd, Sub::Status);
    let cmd = mapping::to_cmd(cli.cmd);
    match client::request(cmd).await {
        Ok(data) => {
            if cli.json {
                println!("{}", serde_json::to_string_pretty(&data).unwrap_or_else(|_| "null".into()));
            } else if is_status {
                print_status(&data);
            } else {
                print_human(&data);
            }
            0
        }
        Err(e) => {
            if cli.json {
                println!("{}", serde_json::json!({ "ok": false, "error": e.to_string() }));
            } else {
                eprintln!("{e}");
            }
            1
        }
    }
}

/// Human render for non-status commands: surface the salient id, else "ok".
fn print_human(data: &Value) {
    if let Some(id) = data.get("workspace_id").and_then(|v| v.as_str()) {
        println!("{id}");
    } else if let Some(id) = data.get("surface_id").and_then(|v| v.as_str()) {
        println!("{id}");
    } else if let Some(id) = data.get("group_id").and_then(|v| v.as_str()) {
        println!("{id}");
    } else if let Some(ids) = data.get("surface_ids").and_then(|v| v.as_array()) {
        for id in ids {
            if let Some(s) = id.as_str() { println!("{s}"); }
        }
    } else {
        println!("ok");
    }
}

/// Compact table for `status`.
fn print_status(data: &Value) {
    let empty = vec![];
    let workspaces = data.get("workspaces").and_then(|v| v.as_array()).unwrap_or(&empty);
    if workspaces.is_empty() {
        println!("(no workspaces)");
        return;
    }
    for w in workspaces {
        let name = w.get("name").and_then(|v| v.as_str()).unwrap_or("?");
        let id = w.get("id").and_then(|v| v.as_str()).unwrap_or("?");
        let unread = w.get("unread").and_then(|v| v.as_bool()).unwrap_or(false);
        println!("{} ({}){}", name, id, if unread { " *" } else { "" });
        if let Some(surfaces) = w.get("surfaces").and_then(|v| v.as_object()) {
            for (sid, sv) in surfaces {
                let running = sv.get("running").and_then(|v| v.as_bool()).unwrap_or(false);
                let state = sv.get("state").and_then(|v| v.as_str()).unwrap_or("idle");
                let agent = sv.get("spec").and_then(|s| s.get("agent_label")).and_then(|v| v.as_str()).unwrap_or("shell");
                let life = if running { "running" } else { "stopped" };
                println!("  {sid}  {agent:<8} {life:<8} {state}");
            }
        }
    }
}
