//! ptyctl CLI — headless PTY controller.

use clap::Parser;

mod cli;
mod commands;
mod registry;

use cli::{Cli, Commands};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let cli = Cli::parse();

    match cli.command {
        Commands::Run {
            command,
            width,
            height,
            cwd,
            env,
            port,
            name,
            force,
            timeout,
            linger,
            quiet,
        } => {
            commands::run::run(
                command, width, height, cwd, env, port, name, force, timeout, linger, quiet,
            )
            .await?;
        }
        Commands::Send {
            target,
            keys,
            enter,
        } => {
            let url = target.to_url()?;
            commands::client::send(&url, &keys, enter).await?;
        }
        Commands::Screen {
            target,
            rows,
            cols,
            json: _,
            cursor,
            ansi: _,
            styled,
            html,
            full,
            line_numbers,
        } => {
            let url = target.to_url()?;
            let format = if styled {
                "styled"
            } else if html {
                "html"
            } else {
                "text"
            };
            commands::client::screen(
                &url,
                rows.as_deref(),
                cols.as_deref(),
                cursor,
                format,
                full,
                line_numbers,
            )
            .await?;
        }
        Commands::Status { target } => {
            let url = target.to_url()?;
            commands::client::status(&url).await?;
        }
        Commands::Stop { target } => {
            let url = target.to_url()?;
            commands::client::stop(&url).await?;
        }
        Commands::Resize { target, size } => {
            let url = target.to_url()?;
            commands::client::resize(&url, &size).await?;
        }
        Commands::Cursor { target } => {
            let url = target.to_url()?;
            commands::client::cursor(&url).await?;
        }
        Commands::Wait {
            target,
            text,
            regex,
            gone,
            stable_ms,
            timeout,
        } => {
            // Exit code contract: 0 matched, 1 timeout, 2 usage/connection errors.
            let exit = |code: i32| -> ! { std::process::exit(code) };
            let url = match target.to_url() {
                Ok(url) => url,
                Err(e) => {
                    eprintln!("Error: {e:#}");
                    exit(2);
                }
            };
            match commands::client::wait(
                &url,
                text.as_deref(),
                regex.as_deref(),
                gone.as_deref(),
                stable_ms,
                timeout,
            )
            .await
            {
                Ok(true) => {}
                Ok(false) => exit(1),
                Err(e) => {
                    eprintln!("Error: {e:#}");
                    exit(2);
                }
            }
        }
        Commands::List { json } => {
            let sessions = registry::list_sessions()?;
            if json {
                let mut items = Vec::new();
                for (name, info) in &sessions {
                    items.push(serde_json::json!({
                        "name": name,
                        "port": info.port,
                        "pid": info.pid,
                        "command": info.command,
                        "started_at": info.started_at,
                        // "server_alive", not "alive": /query/status's "alive" means child-alive, which diverges under --linger.
                        "server_alive": registry::server_alive(info.port).await,
                    }));
                }
                println!("{}", serde_json::to_string_pretty(&items)?);
            } else if sessions.is_empty() {
                println!("No active sessions");
            } else {
                println!(
                    "{:<16} {:<8} {:<8} {:<8} COMMAND",
                    "NAME", "PORT", "PID", "SERVER"
                );
                println!("{}", "-".repeat(60));
                for (name, info) in &sessions {
                    let pid = info
                        .pid
                        .map(|p| p.to_string())
                        .unwrap_or_else(|| "?".into());
                    let server = if registry::server_alive(info.port).await {
                        "live"
                    } else {
                        "dead"
                    };
                    println!(
                        "{:<16} {:<8} {:<8} {:<8} {}",
                        name,
                        info.port,
                        pid,
                        server,
                        info.command.join(" ")
                    );
                }
            }
        }
    }

    Ok(())
}
