//
// Syd: rock-solid application kernel
// src/utils/syd-env.rs: Run a command with the environment of the process with the given PID.
//
// Copyright (c) 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    collections::VecDeque,
    os::unix::process::CommandExt,
    process::{exit, Command, ExitCode, Stdio},
    time::Duration,
};

use nix::{
    fcntl::{open, OFlag},
    libc::pid_t,
    sys::stat::Mode,
    unistd::Pid,
};
use syd::{path::XPathBuf, proc::proc_environ_read, syslog::LogLevel, wordexp::WordExp};

// Set global allocator to mimalloc.
#[cfg(all(not(feature = "prof"), target_pointer_width = "64"))]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

// Set global allocator to tcmalloc if profiling is enabled.
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;

syd::main! {
    syd::set_sigpipe_dfl()?;

    // Initialize logging.
    syd::log::log_init_simple(LogLevel::Warn)?;

    let mut args: VecDeque<_> = std::env::args().skip(1).collect();
    let pid  = match args.pop_front().as_deref() {
        None | Some("-h") => {
            help();
            return Ok(ExitCode::SUCCESS);
        }
        Some("-e") => {
            let var = if let Some(var) = args.pop_front() {
                var
            } else {
                eprintln!("Error: -e requires an argument!");
                return Ok(ExitCode::FAILURE);
            };
            match WordExp::expand(&var, true, Duration::from_secs(3)) {
                Ok(val) => {
                    print!("{val}");
                    return Ok(ExitCode::SUCCESS);
                }
                Err(err) => {
                    eprintln!("Error: {err}");
                    exit(err.into());
                }
            };
        }
        Some(pid) => match pid.parse::<pid_t>().map(Pid::from_raw) {
            Ok(pid) => pid,
            Err(error) => {
                eprintln!("Invalid PID: {error}");
                return Ok(ExitCode::FAILURE);
            }
        },
    };

    let mut pfd = XPathBuf::from("/proc");
    pfd.push_pid(pid);
    pfd.push(b"environ");
    #[expect(clippy::disallowed_methods)]
    let pfd = open(&pfd, OFlag::O_RDONLY, Mode::empty())?;

    let environ = match proc_environ_read(pfd) {
        Ok(environ) => environ,
        Err(error) => {
            eprintln!("syd-env: {error}");
            return Ok(ExitCode::FAILURE);
        }
    };

    let error = Command::new("env")
        .args(args)
        .env_clear()
        .envs(&environ)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .exec();
    eprintln!("syd-env: {error}");
    Ok(ExitCode::FAILURE)
}

fn help() {
    println!("Usage: syd-env pid [-i] [name=value]... {{command [arg...]}}");
    println!("Run a command with the environment of the process with the given PID.");
}
