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

use super::super::os::package::which;
use super::mgmt::ProcessKiller;
use super::{
    CommandResult, ProcessError, ProcessInfo, RunError, execute_script,
    process_get as do_process_get, process_list as do_process_list, run,
};
use rhai::{Array, Dynamic, Engine, EvalAltResult, Position};
use std::clone::Clone;

/// A trait for converting a Result to a Rhai-compatible error
pub trait ToRhaiError<T> {
    fn to_rhai_error(self) -> Result<T, Box<EvalAltResult>>;
}

impl<T, E: std::error::Error> ToRhaiError<T> for Result<T, E> {
    fn to_rhai_error(self) -> Result<T, Box<EvalAltResult>> {
        self.map_err(|e| {
            Box::new(EvalAltResult::ErrorRuntime(
                e.to_string().into(),
                Position::NONE,
            ))
        })
    }
}

/// 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 Executor type and its methods (unified command/script executor)
    engine.register_type_with_name::<RhaiExecutor>("Executor");
    engine.register_fn("execute", RhaiExecutor::new); // Builder entry point
    engine.register_fn("silent", RhaiExecutor::silent); // Method: suppress output
    engine.register_fn("ignore_error", RhaiExecutor::ignore_error); // Method: don't die on error
    engine.register_fn("log", RhaiExecutor::log); // Method: enable logging
    engine.register_fn("env", RhaiExecutor::env); // Method: add environment variable
    engine.register_fn("timeout", RhaiExecutor::timeout); // Method: set timeout in seconds
    engine.register_fn("run", RhaiExecutor::run); // Method: execute the command/script
    engine.register_fn("to_string", |b: &mut RhaiExecutor| b.to_string());

    // Register ProcessKiller builder type and its methods
    engine.register_type_with_name::<RhaiProcessKiller>("ProcessKiller");
    engine.register_fn("process_killer", RhaiProcessKiller::new); // Builder entry point
    engine.register_fn("pattern", RhaiProcessKiller::pattern); // Method on ProcessKiller
    engine.register_fn("pid", RhaiProcessKiller::pid); // Method on ProcessKiller
    engine.register_fn("recursive", RhaiProcessKiller::recursive); // Method on ProcessKiller
    engine.register_fn("kill", RhaiProcessKiller::kill); // Method on ProcessKiller
    engine.register_fn("to_string", |b: &mut RhaiProcessKiller| b.to_string());

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

    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 unified command/script executor builder
///
/// Handles both single-line commands and multi-line scripts.
/// Scripts (containing newlines) are automatically written to temporary files and executed.
#[derive(Clone, Debug)]
pub struct RhaiExecutor {
    command: String,
    die_on_error: bool,
    is_silent: bool,
    enable_log: bool,
    timeout_secs: Option<u64>,
    env_vars: Vec<(String, String)>,
}

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

impl RhaiExecutor {
    /// Create a new executor for a command or script
    pub fn new(command: &str) -> Self {
        Self {
            command: command.to_string(),
            die_on_error: true,   // Default: die on error
            is_silent: false,     // Default: show output
            enable_log: false,    // Default: no logging
            timeout_secs: None,   // Default: no timeout
            env_vars: Vec::new(), // Default: no env vars
        }
    }

    /// Suppress output (don't print stdout/stderr)
    pub fn silent(mut self) -> Self {
        self.is_silent = true;
        self
    }

    /// Don't throw error on non-zero exit code
    pub fn ignore_error(mut self) -> Self {
        self.die_on_error = false;
        self
    }

    /// Enable execution logging
    pub fn log(mut self) -> Self {
        self.enable_log = true;
        self
    }

    /// Set timeout in seconds (command will be killed if it exceeds this)
    pub fn timeout(mut self, secs: i64) -> Self {
        self.timeout_secs = Some(secs.max(0) as u64);
        self
    }

    /// Add an environment variable for this execution
    pub fn env(mut self, key: &str, value: &str) -> Self {
        self.env_vars.push((key.to_string(), value.to_string()));
        self
    }

    /// Execute the command or script
    pub fn run(self) -> Result<CommandResult, Box<EvalAltResult>> {
        // If environment variables are provided, use execute_script for proper shell support
        if !self.env_vars.is_empty() {
            // Build the command with environment variables prepended
            let mut cmd = String::new();
            for (key, value) in &self.env_vars {
                cmd.push_str(&format!("{}=\"{}\" ", key, value));
            }
            cmd.push_str(&self.command);

            return run_error_to_rhai_error(execute_script(&cmd));
        }

        let mut builder = run(&self.command)
            .die(self.die_on_error)
            .silent(self.is_silent)
            .log(self.enable_log);

        // Apply timeout if set
        if let Some(secs) = self.timeout_secs {
            builder = builder.timeout(secs);
        }

        // 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 Killer Builder for RHAI
//

/// Rhai-facing process killer builder struct
#[derive(Clone, Debug)]
pub struct RhaiProcessKiller {
    killer: ProcessKiller,
}

impl std::fmt::Display for RhaiProcessKiller {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "ProcessKiller")
    }
}

impl RhaiProcessKiller {
    /// Create a new process killer builder
    pub fn new() -> Self {
        Self {
            killer: ProcessKiller::new(),
        }
    }

    /// Set the process pattern to match
    pub fn pattern(mut self, pattern: &str) -> Self {
        self.killer = self.killer.pattern(pattern);
        self
    }

    /// Set the process ID to kill
    pub fn pid(mut self, pid: i64) -> Self {
        self.killer = self.killer.pid(pid);
        self
    }

    /// Enable recursive killing of child processes
    pub fn recursive(mut self, recursive: bool) -> Self {
        self.killer = self.killer.recursive(recursive);
        self
    }

    /// Execute the kill operation
    pub fn kill(self) -> Result<String, Box<EvalAltResult>> {
        process_error_to_rhai_error(self.killer.kill())
    }
}

//
// Process Management Function Wrappers
//

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

/// 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))
}
