//! Rhai wrappers for Process module functions
//!
//! This module provides Rhai wrappers for the functions in the Process module.

use super::{
    kill as process_kill, process_get as do_process_get, process_list as do_process_list, run,
    which as process_which, CommandResult, ProcessError, ProcessInfo, RunError,
};
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
use std::clone::Clone;

/// Register Process module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_process_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // Register types
    // register_process_types(engine)?; // Removed

    // Register CommandResult type and its methods
    engine.register_type_with_name::<CommandResult>("CommandResult");
    engine.register_get("stdout", |r: &mut CommandResult| r.stdout.clone());
    engine.register_get("stderr", |r: &mut CommandResult| r.stderr.clone());
    engine.register_get("success", |r: &mut CommandResult| r.success);
    engine.register_get("code", |r: &mut CommandResult| r.code);

    // Register ProcessInfo type and its methods
    engine.register_type_with_name::<ProcessInfo>("ProcessInfo");
    engine.register_get("pid", |p: &mut ProcessInfo| p.pid);
    engine.register_get("name", |p: &mut ProcessInfo| p.name.clone());
    engine.register_get("memory", |p: &mut ProcessInfo| p.memory);
    engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu);

    // Register CommandBuilder type and its methods
    engine.register_type_with_name::<RhaiCommandBuilder>("CommandBuilder");
    engine.register_fn("cmd", RhaiCommandBuilder::new_rhai); // Builder entry point (use cmd() for builder pattern)
    engine.register_fn("silent", RhaiCommandBuilder::silent); // Method on CommandBuilder
    engine.register_fn("ignore_error", RhaiCommandBuilder::ignore_error); // Method on CommandBuilder
    engine.register_fn("log", RhaiCommandBuilder::log); // Method on CommandBuilder
    engine.register_fn("execute", RhaiCommandBuilder::execute_command); // Method on CommandBuilder
    engine.register_fn("to_string", |b: &mut RhaiCommandBuilder| b.to_string());
    engine.register_fn("to_debug", |b: &mut RhaiCommandBuilder| format!("{:?}", b));

    // Simple run() function that executes immediately and returns output
    engine.register_fn("run", run_simple);

    // Register other process management functions
    engine.register_fn("which", which);
    engine.register_fn("kill", kill);
    engine.register_fn("process_list", process_list);
    engine.register_fn("process_get", process_get);

    // Register legacy functions for backward compatibility
    engine.register_fn("run_command", run_command);
    engine.register_fn("run_silent", run_silent);
    engine.register_fn("run", run_with_options);

    Ok(())
}

// Helper functions for error conversion
fn run_error_to_rhai_error<T>(result: Result<T, RunError>) -> Result<T, Box<EvalAltResult>> {
    result.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Run error: {}", e).into(),
            rhai::Position::NONE,
        ))
    })
}

/// Rhai-facing command builder struct
#[derive(Clone, Debug)]
pub struct RhaiCommandBuilder {
    command: String,
    die_on_error: bool,
    is_silent: bool,
    enable_log: bool,
}

impl std::fmt::Display for RhaiCommandBuilder {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "CommandBuilder(\"{}\")", self.command)
    }
}

impl RhaiCommandBuilder {
    // Constructor function for Rhai (registered as `run`)
    pub fn new_rhai(command: &str) -> Self {
        Self {
            command: command.to_string(),
            die_on_error: true, // Default: die on error
            is_silent: false,
            enable_log: false,
        }
    }

    // Rhai method: .silent()
    pub fn silent(mut self) -> Self {
        self.is_silent = true;
        self
    }

    // Rhai method: .ignore_error()
    pub fn ignore_error(mut self) -> Self {
        self.die_on_error = false;
        self
    }

    // Rhai method: .log()
    pub fn log(mut self) -> Self {
        self.enable_log = true;
        self
    }

    // Rhai method: .execute() - Execute the command
    pub fn execute_command(self) -> Result<CommandResult, Box<EvalAltResult>> {
        let builder = run(&self.command)
            .die(self.die_on_error)
            .silent(self.is_silent)
            .log(self.enable_log);

        // Execute the command
        run_error_to_rhai_error(builder.execute())
    }
}

fn process_error_to_rhai_error<T>(
    result: Result<T, ProcessError>,
) -> Result<T, Box<EvalAltResult>> {
    result.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Process error: {}", e).into(),
            rhai::Position::NONE,
        ))
    })
}

//
// Process Management Function Wrappers
//

/// Wrapper for process::which
///
/// Check if a command exists in PATH.
pub fn which(cmd: &str) -> Dynamic {
    match process_which(cmd) {
        Some(path) => path.into(),
        None => Dynamic::UNIT,
    }
}

/// Wrapper for process::kill
///
/// Kill processes matching a pattern.
pub fn kill(pattern: &str) -> Result<String, Box<EvalAltResult>> {
    process_error_to_rhai_error(process_kill(pattern))
}

/// Wrapper for process::process_list
///
/// List processes matching a pattern (or all if pattern is empty).
pub fn process_list(pattern: &str) -> Result<Array, Box<EvalAltResult>> {
    let processes = process_error_to_rhai_error(do_process_list(pattern))?;

    // Convert Vec<ProcessInfo> to Rhai Array
    let mut array = Array::new();
    for p in processes {
        array.push(Dynamic::from(p));
    }

    Ok(array)
}

/// Wrapper for process::process_get
///
/// Get a single process matching the pattern (error if 0 or more than 1 match).
pub fn process_get(pattern: &str) -> Result<ProcessInfo, Box<EvalAltResult>> {
    process_error_to_rhai_error(do_process_get(pattern))
}

/// Simple run function - executes command and returns stdout
///
/// This is the main function users should use for running commands.
/// Returns the stdout output as a string.
pub fn run_simple(cmd: &str) -> Result<String, Box<EvalAltResult>> {
    let result = run_error_to_rhai_error(run(cmd).execute())?;
    if result.success {
        Ok(result.stdout.trim().to_string())
    } else {
        Err(Box::new(EvalAltResult::ErrorRuntime(
            format!(
                "Command failed (exit code {}): {}",
                result.code,
                result.stderr.trim()
            )
            .into(),
            rhai::Position::NONE,
        )))
    }
}

/// Legacy wrapper for process::run
///
/// Run a command and return the result.
pub fn run_command(cmd: &str) -> Result<CommandResult, Box<EvalAltResult>> {
    run_error_to_rhai_error(run(cmd).execute())
}

/// Legacy wrapper for process::run with silent option
///
/// Run a command silently and return the result.
pub fn run_silent(cmd: &str) -> Result<CommandResult, Box<EvalAltResult>> {
    run_error_to_rhai_error(run(cmd).silent(true).execute())
}

/// Legacy wrapper for process::run with options
///
/// Run a command with options and return the result.
pub fn run_with_options(cmd: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> {
    let mut builder = run(cmd);

    // Apply options
    if let Some(silent) = options.get("silent") {
        if let Ok(silent_bool) = (*silent).clone().as_bool() {
            builder = builder.silent(silent_bool);
        }
    }

    if let Some(die) = options.get("die") {
        if let Ok(die_bool) = (*die).clone().as_bool() {
            builder = builder.die(die_bool);
        }
    }

    if let Some(log) = options.get("log") {
        if let Ok(log_bool) = (*log).clone().as_bool() {
            builder = builder.log(log_bool);
        }
    }

    run_error_to_rhai_error(builder.execute())
}
