//! Pipe-mode CLI traits: NDJSON parser and command builder.

use serde_json::Value;

/// Unified event type from any AI CLI tool's NDJSON stream.
#[derive(Debug, Clone)]
pub enum CliEvent {
    /// Session initialized (model, session ID, tools list).
    SessionStart {
        session_id: String,
        model: String,
        tools: Vec<String>,
    },
    /// Assistant text (complete message or streaming delta).
    AssistantText {
        text: String,
        is_delta: bool,
    },
    /// Tool call initiated by the assistant.
    ToolCallStart {
        id: String,
        name: String,
        input: Value,
    },
    /// Tool call result returned.
    ToolCallResult {
        id: String,
        output: String,
        is_error: bool,
        duration_ms: Option<u64>,
    },
    /// Thinking/reasoning content.
    Thinking {
        text: String,
    },
    /// Turn completed with token usage.
    TurnComplete {
        input_tokens: u64,
        output_tokens: u64,
        cache_read_tokens: u64,
        cache_write_tokens: u64,
        reasoning_tokens: u64,
        /// Only Codex provides this natively from pipe output.
        context_window: Option<u64>,
        /// When true, values are cumulative session totals (Codex event_msg).
        /// Consumer should SET counters, not ADD to them.
        is_cumulative: bool,
    },
    /// Session ended.
    SessionEnd {
        result: String,
        cost_usd: Option<f64>,
        is_error: bool,
    },
    /// Error event.
    Error {
        message: String,
    },
}

/// Trait for parsing NDJSON lines into CliEvents.
pub trait NdjsonParser: Send {
    /// Parse a single JSON line into zero or more events.
    fn parse_line(&mut self, line: &str) -> Vec<CliEvent>;

    /// Get the session ID if known.
    fn session_id(&self) -> Option<&str>;

    /// Returns the CLI-native session ID if one has been observed.
    ///
    /// Default implementation delegates to `session_id()`. Parsers that use a
    /// CLI-specific session-ID format (e.g. OpenCode's `ses_XXXX` prefix) may
    /// override this to return a distinct value from their internal state.
    fn detected_session_id(&self) -> Option<&str> {
        self.session_id()
    }
}

/// Trait for building the `std::process::Command` that spawns a CLI agent.
///
/// Each CLI module implements this trait. The returned `Command` is a bare
/// command without `current_dir`, `stdin`, `stdout`, or `stderr` set — those
/// are configured by the caller (`pipe/process.rs`) after dispatch.
///
/// On Windows the caller wraps the command in `cmd /C` to handle PATH lookup
/// for node-based CLI tools installed via npm. The per-CLI impl returns the
/// logical (non-shell) argv; the Windows wrapper is applied by the caller.
pub trait CliCommandBuilder {
    /// Build the spawn command for the given options.
    fn build_command(&self, opts: &crate::transport::SpawnOptions) -> std::process::Command;
}
