use actix_web::{App, HttpServer};
use clap::Parser;
use webterm::{validate_command, Analytics, WebTermExt};

use std::path::PathBuf;
use std::process::Command;

#[derive(Parser, Debug)]
#[command(name = "webterm-server")]
#[command(about = "Web terminal server based on xterm.js")]
struct Opt {
    /// The port to listen on
    #[arg(short, long, default_value = "8082")]
    port: u16,

    /// The host or IP to listen on
    #[arg(short = 'H', long, default_value = "localhost")]
    host: String,

    /// The command to execute
    #[arg(short, long, default_value = "/bin/sh")]
    command: String,

    /// Enable Umami analytics tracking
    #[arg(long, default_value = "true")]
    enable_analytics: bool,

    /// Umami instance endpoint
    #[arg(long, default_value = "http://unami.wittyoneoff.com/api/send")]
    umami_endpoint: String,

    /// Umami website ID
    #[arg(long, default_value = "caefa16f-86af-4835-8b82-c8649aea0e2a")]
    umami_website_id: String,

    /// Path to telemetry config file (for opt-out management)
    #[arg(long)]
    telemetry_config: Option<PathBuf>,
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init();

    let opt = Opt::parse();

    // Validate command for security before starting server
    if let Err(e) = validate_command(&opt.command, false) {
        eprintln!("Error: Invalid command '{}': {}", opt.command, e);
        eprintln!("Command failed security validation.");
        std::process::exit(1);
    }

    log::info!("Command validated: {}", opt.command);

    // Normalize common hostnames that sometimes resolve to IPv6-only addresses
    // which can cause platform-specific bind failures. Mapping `localhost` to
    // 127.0.0.1 makes behavior predictable on systems where `::1` would otherwise
    // be selected.
    let host = if opt.host == "localhost" {
        "127.0.0.1".to_string()
    } else {
        opt.host.clone()
    };

    let bind_addr = format!("{}:{}", host, opt.port);
    println!("Starting webterm server on http://{}", bind_addr);

    // Initialize analytics
    let analytics = if opt.enable_analytics {
        // Use default config path if not specified
        let config_path = opt.telemetry_config.unwrap_or_else(|| {
            dirs::config_dir()
                .unwrap_or_else(|| PathBuf::from("."))
                .join("webterm")
                .join("telemetry.json")
        });

        // Create config directory if it doesn't exist
        if let Some(parent) = config_path.parent() {
            std::fs::create_dir_all(parent).ok();
        }

        log::info!(
            "Analytics enabled: {} (website_id: {}, config: {:?})",
            opt.umami_endpoint,
            opt.umami_website_id,
            config_path
        );
        Analytics::new(
            opt.umami_website_id.clone(),
            opt.umami_endpoint.clone(),
            config_path,
        )
    } else {
        log::info!("Analytics disabled");
        Analytics::disabled()
    };

    let command = opt.command.clone();

    HttpServer::new(move || {
        let cmd = command.clone();
        let analytics_clone = analytics.clone();
        App::new()
            .service(actix_files::Files::new("/assets", "./static"))
            .service(actix_files::Files::new("/static", "./node_modules"))
            .webterm_socket("/websocket", move |_req| {
                let mut command = Command::new(&cmd);
                command.env("TERM", "xterm");
                command
            })
            .webterm_ui("/", "/websocket", "/static")
            .app_data(actix_web::web::Data::new(analytics_clone.clone()))
    })
    .bind(&bind_addr)?
    .run()
    .await
}
