//! spawn — orchestrator-driven task → prepared agent + ledger row.
//!
//! One public entry point: `spawn_from_task`. Given a task.toml and a
//! kit_root, it:
//!   1. Parses task.toml via `kei_agent_runtime::spawn::load_task`
//!   2. Composes `AgentInvocation` via `kei_agent_runtime::prepare::prepare`
//!      (auto-generates agent-id if absent)
//!   3. Copies the resolved agent-id back into the task and writes
//!      `tasks/<agent-id>/{prompt.md, task.toml}` via
//!      `kei_agent_runtime::spawn::prepare_agent`
//!   4. Computes spec_sha (SHA-256 of the task TOML content)
//!   5. Registers a running row in the ledger via `kei-ledger fork`
//!   6. Returns `SpawnOutput` — everything orchestrator needs to call
//!      Claude Code's `Agent` tool (serialised as JSON).
//!
//! Never invokes git. Never invokes the Agent tool. Per RULE 0.13.

use anyhow::{Context, Result};
use kei_agent_runtime::{prepare, spawn as runtime_spawn};
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};

use crate::ledger_sh;

/// The bundle orchestrator hands to Claude Code's Agent tool.
#[derive(Debug, Clone, Serialize)]
pub struct SpawnOutput {
    pub agent_id: String,
    pub dna: String,
    pub role: String,
    pub subagent_type: String,
    pub isolation: Option<String>,
    pub description: String,
    pub prompt: String,
    pub prompt_path: PathBuf,
    pub task_path: PathBuf,
    pub spec_sha: String,
    pub branch: String,
    pub verify_command: String,
    pub ledger_row: String,
    pub next_step: String,
}

/// Main spawn entry. See module doc for the 6-step pipeline.
pub fn spawn_from_task(task_path: &Path, kit_root: &Path) -> Result<SpawnOutput> {
    let mut task = runtime_spawn::load_task(task_path)
        .with_context(|| format!("load task {}", task_path.display()))?;
    let inv = prepare::prepare(&task, kit_root).context("compose AgentInvocation")?;
    // Propagate auto-generated agent-id back into the task so `prepare_agent`
    // can use it as the directory name and the ledger row keys by it.
    task.task.agent_id = inv.agent_id.clone();
    let prepared = runtime_spawn::prepare_agent(&task, kit_root).context("prepare_agent")?;
    let task_bytes = std::fs::read(&prepared.task_path)
        .with_context(|| format!("read written task {}", prepared.task_path.display()))?;
    let spec_sha = sha256_hex(&task_bytes);
    let branch = format!("agent/{}", inv.agent_id);
    let parent = task
        .task
        .parent_agent
        .as_deref()
        .filter(|s| !s.is_empty());
    ledger_sh::fork(
        &inv.agent_id,
        &branch,
        parent,
        &spec_sha,
        prepared.dir.to_str(),
        Some(&inv.dna),
    )
    .context("kei-ledger fork")?;
    Ok(build_output(inv, prepared, spec_sha, branch))
}

fn build_output(
    inv: prepare::AgentInvocation,
    prepared: runtime_spawn::PreparedAgent,
    spec_sha: String,
    branch: String,
) -> SpawnOutput {
    let next_step = format!(
        "Invoke Agent tool with subagent_type={}, isolation={}, prompt=<see prompt field or {}>",
        inv.subagent_type,
        inv.isolation.as_deref().unwrap_or("<none>"),
        prepared.prompt_path.display()
    );
    SpawnOutput {
        agent_id: inv.agent_id,
        dna: inv.dna,
        role: inv.role,
        subagent_type: inv.subagent_type,
        isolation: inv.isolation,
        description: inv.description,
        prompt: inv.prompt,
        prompt_path: prepared.prompt_path,
        task_path: prepared.task_path,
        spec_sha,
        branch,
        verify_command: inv.verify_command,
        ledger_row: inv.ledger_row,
        next_step,
    }
}

fn sha256_hex(bytes: &[u8]) -> String {
    let mut h = Sha256::new();
    h.update(bytes);
    let digest = h.finalize();
    let mut s = String::with_capacity(64);
    for b in digest {
        s.push_str(&format!("{:02x}", b));
    }
    s
}
