//! CLI dispatch helpers — one function per subcommand.
//!
//! Constructor Pattern: extracted from `main.rs` to keep the entry point
//! under the 200-LOC cap. Each fn returns `ExitCode` directly so `main`
//! stays a flat match.

use crate::{descendants, ledger};
use rusqlite::Connection;
use std::path::Path;
use std::process::ExitCode;

pub fn err(msg: &str) -> ExitCode {
    eprintln!("kei-ledger: {msg}");
    ExitCode::from(1)
}

pub fn cmd_list(conn: &Connection, status: Option<&str>) -> ExitCode {
    match ledger::list(conn, status) {
        Ok(rows) => {
            if rows.is_empty() {
                println!("(no agents)");
            }
            for r in &rows {
                println!(
                    "{}\t{}\t{}\t{}\tparent={}\tspec={}",
                    r.id,
                    r.status,
                    r.branch,
                    r.started_ts,
                    r.parent_branch.as_deref().unwrap_or("-"),
                    &r.spec_sha[..r.spec_sha.len().min(12)]
                );
            }
            ExitCode::SUCCESS
        }
        Err(e) => err(&format!("list failed: {e}")),
    }
}

pub fn cmd_tree(conn: &Connection, id: &str) -> ExitCode {
    match ledger::tree(conn, id) {
        Ok(rows) if rows.is_empty() => err(&format!("no agent with id {id}")),
        Ok(rows) => {
            for r in &rows {
                let indent = if r.id == id { "" } else { "  " };
                println!("{}{} [{}] branch={}", indent, r.id, r.status, r.branch);
            }
            ExitCode::SUCCESS
        }
        Err(e) => err(&format!("tree failed: {e}")),
    }
}

pub fn cmd_validate(branch: &str, repo_root: &Path) -> ExitCode {
    // branch naming convention: agent/<kind>-<ts>  OR  inline-<ts>
    // ledger artefact dir uses the raw agent id, which the caller passes as branch.
    let agent_id = branch.strip_prefix("agent/").unwrap_or(branch);
    let missing = ledger::validate(repo_root, agent_id);
    if missing.is_empty() {
        println!("OK: all 6 artefacts present for {agent_id}");
        ExitCode::SUCCESS
    } else {
        eprintln!("MISSING for {agent_id}:");
        for m in &missing {
            eprintln!("  - {m}");
        }
        ExitCode::from(2)
    }
}

pub fn cmd_descendants(conn: &Connection, dna: &str) -> ExitCode {
    match descendants::descendants(conn, dna) {
        Ok(rows) => {
            if rows.is_empty() {
                println!("(no descendants for {dna})");
            }
            for r in &rows {
                let relation = if r.fork_parent_id.as_deref() == Some(dna) {
                    "fork"
                } else {
                    "spawn"
                };
                println!("{}\t{}\t{}\t{}", r.id, relation, r.status, r.branch);
            }
            ExitCode::SUCCESS
        }
        Err(e) => err(&format!("descendants failed: {e}")),
    }
}
