//! `lore tui` subcommand — delegates to the `lore-tui` binary.
//!
//! Resolves `lore-tui` via PATH and execs it, replacing the current process.
//! In robot mode, returns a structured JSON error (TUI is human-only).

use std::path::PathBuf;

use clap::Parser;

/// Launch the interactive TUI dashboard
#[derive(Parser, Debug)]
pub struct TuiArgs {
    /// Path to config file (forwarded to lore-tui)
    #[arg(long)]
    pub config: Option<String>,
}

/// Resolve the `lore-tui` binary via PATH lookup.
pub fn find_lore_tui() -> Option<PathBuf> {
    which::which("lore-tui").ok()
}

/// Run the TUI subcommand.
///
/// In robot mode this returns an error (TUI requires a terminal).
/// Otherwise it execs `lore-tui`, replacing the current process.
pub fn run_tui(args: &TuiArgs, robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
    if robot_mode {
        let err = serde_json::json!({
            "error": {
                "code": "TUI_NOT_AVAILABLE",
                "message": "The TUI requires an interactive terminal and cannot run in robot mode.",
                "suggestion": "Use `lore --robot <command>` for programmatic access.",
                "actions": []
            }
        });
        eprintln!("{err}");
        std::process::exit(2);
    }

    let binary = find_lore_tui().ok_or_else(|| {
        "Could not find `lore-tui` on PATH.\n\n\
         Install it with:\n  \
         cargo install --path crates/lore-tui\n\n\
         Or build the workspace:\n  \
         cargo build --release -p lore-tui"
            .to_string()
    })?;

    // Build the command with explicit arguments (no shell interpolation).
    let mut cmd = std::process::Command::new(&binary);
    if let Some(ref config) = args.config {
        cmd.arg("--config").arg(config);
    }

    // On Unix, exec() replaces the current process entirely.
    // This gives lore-tui direct terminal control (stdin/stdout/stderr).
    #[cfg(unix)]
    {
        use std::os::unix::process::CommandExt;
        let err = cmd.exec();
        // exec() only returns on error
        Err(format!("Failed to exec lore-tui at {}: {err}", binary.display()).into())
    }

    // On non-Unix, spawn and wait.
    #[cfg(not(unix))]
    {
        let status = cmd.status()?;
        if status.success() {
            Ok(())
        } else {
            std::process::exit(status.code().unwrap_or(1));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_find_lore_tui_does_not_panic() {
        // Just verify the lookup doesn't panic; it may or may not find the binary.
        let _ = find_lore_tui();
    }

    #[test]
    fn test_robot_mode_error_json_structure() {
        let err = serde_json::json!({
            "error": {
                "code": "TUI_NOT_AVAILABLE",
                "message": "The TUI requires an interactive terminal and cannot run in robot mode.",
                "suggestion": "Use `lore --robot <command>` for programmatic access.",
                "actions": []
            }
        });
        let parsed: serde_json::Value = serde_json::from_str(&err.to_string()).unwrap();
        assert_eq!(parsed["error"]["code"], "TUI_NOT_AVAILABLE");
    }

    #[test]
    fn test_tui_args_default() {
        let args = TuiArgs { config: None };
        assert!(args.config.is_none());
    }

    #[test]
    fn test_tui_args_with_config() {
        let args = TuiArgs {
            config: Some("/tmp/test.json".into()),
        };
        assert_eq!(args.config.as_deref(), Some("/tmp/test.json"));
    }

    #[test]
    fn test_binary_not_found_error_message() {
        let msg = "Could not find `lore-tui` on PATH.";
        assert!(msg.contains("lore-tui"));
    }
}
