//! herodo - Interactive Rhai shell aggregating SAL packages
//!
//! Provides a readline-style interface with:
//! - Tab completion for all functions from core, os, and clients packages
//! - Command history
//! - Syntax highlighting
//! - Help hints

use colored::Colorize;
use rhai::{Engine, EvalAltResult, Scope, module_resolvers::FileModuleResolver};
use rustyline::completion::{Completer, Pair};
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::history::DefaultHistory;
use rustyline::validate::Validator;
use rustyline::{Config, Context, Editor, Helper};
use std::borrow::Cow;
use std::env;
use std::fs;
use std::io::{self, BufRead};
use std::path::PathBuf;

// ============================================================================
// Function Documentation - Core Package
// ============================================================================

/// Text functions from herolib-core
const TEXT_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "replacer_new()",
        "TextReplacerBuilder",
        "Create a new text replacer builder",
    ),
    (
        "template_open(path)",
        "TemplateBuilder",
        "Open a template file",
    ),
    ("name_fix(s)", "string", "Fix/normalize a name"),
    ("path_fix(s)", "string", "Fix/normalize a path"),
    ("dedent(s)", "string", "Remove common leading whitespace"),
    ("prefix(s, p)", "string", "Add prefix to each line"),
];

/// Text replacer builder methods
const TEXT_REPLACER_METHODS: &[(&str, &str)] = &[
    (".pattern(p)", "Set pattern to search for"),
    (".replacement(r)", "Set replacement text"),
    (".regex(yes)", "Use regex matching"),
    (".case_insensitive(yes)", "Case-insensitive matching"),
    (".and()", "Chain another replacement"),
    (".build()", "Build the TextReplacer"),
    (".replace(input)", "Apply replacements to text"),
    (".replace_file(path)", "Replace in file, return result"),
    (".replace_file_in_place(path)", "Replace file in place"),
];

/// Template builder methods
const TEMPLATE_METHODS: &[(&str, &str)] = &[
    (".add_var(name, value)", "Add variable"),
    (".add_vars(map)", "Add multiple variables"),
    (".render()", "Render template to string"),
    (".render_to_file(path)", "Render to file"),
];

/// Network functions from herolib-core
const NET_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("tcp_check(host, port)", "bool", "Check TCP connectivity"),
    ("http_check(url)", "bool", "Check HTTP endpoint"),
    ("ssh_check(host)", "bool", "Check SSH connectivity"),
];

/// HeroScript functions from herolib-core
const HEROSCRIPT_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("heroscript_parse(text)", "map", "Parse HeroScript text"),
    (
        "heroscript_parse_file(path)",
        "map",
        "Parse HeroScript file",
    ),
];

// ============================================================================
// Function Documentation - OS Package
// ============================================================================

/// File system functions from herolib-os
const OS_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("copy(src, dest)", "string", "Copy file or directory"),
    ("copy_bin(src)", "string", "Copy binary to bin directory"),
    ("exist(path)", "bool", "Check if path exists"),
    ("find_file(dir, pattern)", "string", "Find a file"),
    ("find_files(dir, pattern)", "array", "Find multiple files"),
    ("find_dir(dir, pattern)", "string", "Find a directory"),
    (
        "find_dirs(dir, pattern)",
        "array",
        "Find multiple directories",
    ),
    ("delete(path)", "string", "Delete file or directory"),
    ("mkdir(path)", "string", "Create directory"),
    ("file_size(path)", "i64", "Get file size in bytes"),
    ("file_read(path)", "string", "Read file contents"),
    ("file_write(path, content)", "string", "Write to file"),
    (
        "file_write_append(path, content)",
        "string",
        "Append to file",
    ),
    ("mv(src, dest)", "string", "Move file or directory"),
    ("rsync(src, dest)", "string", "Sync directories"),
    ("chdir(path)", "string", "Change directory"),
    ("chmod_exec(path)", "string", "Make file executable"),
    ("which(cmd)", "string", "Find command in PATH"),
    ("cmd_ensure_exists(cmds)", "string", "Ensure commands exist"),
    ("download(url, dest, min_kb)", "string", "Download file"),
    (
        "download_file(url, dest, min_kb)",
        "string",
        "Download to file",
    ),
    (
        "download_install(url, min_kb)",
        "string",
        "Download and install",
    ),
    ("package_install(pkg)", "string", "Install package"),
    ("package_remove(pkg)", "string", "Remove package"),
    ("package_update()", "string", "Update package lists"),
    ("package_upgrade()", "string", "Upgrade packages"),
    ("package_list()", "array", "List installed packages"),
    ("package_search(query)", "array", "Search packages"),
    ("package_is_installed(pkg)", "bool", "Check if installed"),
    ("package_platform()", "string", "Get platform name"),
    ("platform_is_osx()", "bool", "Check if macOS"),
    ("platform_is_linux()", "bool", "Check if Linux"),
    ("platform_is_arm()", "bool", "Check if ARM"),
    ("platform_is_x86()", "bool", "Check if x86"),
];

/// Process functions from herolib-os
const PROCESS_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("run(cmd)", "CommandBuilder", "Create command builder"),
    ("run_command(cmd)", "CommandResult", "Run command (legacy)"),
    ("run_silent(cmd)", "CommandResult", "Run silently (legacy)"),
    ("kill(pattern)", "string", "Kill processes by pattern"),
    ("process_list(pattern)", "array", "List matching processes"),
    ("process_get(pattern)", "ProcessInfo", "Get single process"),
];

/// Command builder methods
const BUILDER_METHODS: &[(&str, &str)] = &[
    (".silent()", "Suppress output"),
    (".ignore_error()", "Don't die on error"),
    (".log()", "Enable logging"),
    (".execute()", "Execute the command"),
];

/// Git functions from herolib-os
const GIT_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("git_tree_new(path)", "GitTree", "Create git tree manager"),
    ("git_clone(url)", "GitRepo", "Clone a repository"),
    (
        "parse_git_url_extended(url)",
        "ParsedGitUrl",
        "Parse git URL",
    ),
];

/// Virt - QCOW2 functions
const QCOW2_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "qcow2_create(path, size_gb)",
        "string",
        "Create qcow2 image",
    ),
    ("qcow2_info(path)", "map", "Get qcow2 image info"),
    (
        "qcow2_snapshot_create(path, name)",
        "void",
        "Create snapshot",
    ),
    (
        "qcow2_snapshot_delete(path, name)",
        "void",
        "Delete snapshot",
    ),
    ("qcow2_snapshot_list(path)", "array", "List snapshots"),
];

/// Virt - Buildah functions
const BUILDAH_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "bah(name, image)",
        "Bah",
        "Create container builder (fluent)",
    ),
    (
        "bah_new(name, image)",
        "Builder",
        "Create container builder",
    ),
];

/// Virt - Nerdctl functions
const NERDCTL_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "nerdctl_container_new(name)",
        "Container",
        "Create container",
    ),
    (
        "nerdctl_container_from_image(name, image)",
        "Container",
        "Create from image",
    ),
    ("nerdctl_run(image)", "CommandResult", "Run container"),
    (
        "nerdctl_run_with_name(image, name)",
        "CommandResult",
        "Run with name",
    ),
    (
        "nerdctl_exec(container, cmd)",
        "CommandResult",
        "Execute in container",
    ),
    ("nerdctl_stop(container)", "CommandResult", "Stop container"),
    (
        "nerdctl_remove(container)",
        "CommandResult",
        "Remove container",
    ),
    ("nerdctl_list(all)", "CommandResult", "List containers"),
    (
        "nerdctl_logs(container)",
        "CommandResult",
        "Get container logs",
    ),
    ("nerdctl_images()", "CommandResult", "List images"),
    ("nerdctl_image_pull(image)", "CommandResult", "Pull image"),
    (
        "nerdctl_image_remove(image)",
        "CommandResult",
        "Remove image",
    ),
];

// ============================================================================
// Function Documentation - Clients Package
// ============================================================================

/// Redis client functions
const REDIS_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("redis_new(url)", "RedisClient", "Create Redis client"),
    ("redis_get(key)", "string", "Get value by key"),
    ("redis_set(key, value)", "void", "Set key-value pair"),
    ("redis_del(key)", "void", "Delete key"),
    ("redis_exists(key)", "bool", "Check if key exists"),
    ("redis_keys(pattern)", "array", "Get keys matching pattern"),
    ("redis_hget(key, field)", "string", "Get hash field"),
    ("redis_hset(key, field, value)", "void", "Set hash field"),
    ("redis_hgetall(key)", "map", "Get all hash fields"),
];

/// PostgreSQL client functions
const POSTGRES_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "postgres_new(url)",
        "PostgresClient",
        "Create Postgres client",
    ),
    (
        "postgres_execute(sql, params)",
        "i64",
        "Execute SQL statement",
    ),
    (
        "postgres_query(sql, params)",
        "array",
        "Query and return rows",
    ),
    ("postgres_query_one(sql, params)", "map", "Query single row"),
];

/// MQTT client functions
const MQTT_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("mqtt_new(host, port)", "MqttClient", "Create MQTT client"),
    ("mqtt_connect()", "void", "Connect to broker"),
    ("mqtt_disconnect()", "void", "Disconnect from broker"),
    ("mqtt_publish(topic, payload)", "void", "Publish message"),
    ("mqtt_subscribe(topic)", "void", "Subscribe to topic"),
];

/// Mycelium client functions
const MYCELIUM_FUNCTIONS: &[(&str, &str, &str)] = &[
    ("mycelium_get_node_info()", "map", "Get node information"),
    ("mycelium_get_peers()", "array", "Get connected peers"),
];

/// Hetzner client functions
const HETZNER_FUNCTIONS: &[(&str, &str, &str)] = &[
    (
        "hetzner_new(token)",
        "HetznerClient",
        "Create Hetzner client",
    ),
    ("hetzner_server_list()", "array", "List all servers"),
    ("hetzner_server_get(id)", "map", "Get server by ID"),
    ("hetzner_server_create(spec)", "map", "Create new server"),
    ("hetzner_server_delete(id)", "void", "Delete server"),
    ("hetzner_server_power_on(id)", "void", "Power on server"),
    ("hetzner_server_power_off(id)", "void", "Power off server"),
    ("hetzner_server_reboot(id)", "void", "Reboot server"),
];

// ============================================================================
// REPL Commands
// ============================================================================

/// REPL commands
const REPL_COMMANDS: &[(&str, &str)] = &[
    ("/help", "Show this help"),
    ("/functions", "List all available functions"),
    ("/core", "Show core functions (text, net, heroscript)"),
    ("/text", "Show text functions"),
    ("/net", "Show network functions"),
    ("/os", "Show OS functions"),
    ("/process", "Show process functions"),
    ("/git", "Show git functions"),
    ("/virt", "Show virt functions (qcow2, buildah, nerdctl)"),
    (
        "/clients",
        "Show client functions (redis, postgres, mqtt, etc)",
    ),
    ("/redis", "Show Redis functions"),
    ("/postgres", "Show PostgreSQL functions"),
    ("/mqtt", "Show MQTT functions"),
    ("/hetzner", "Show Hetzner functions"),
    ("/builder", "Show command builder methods"),
    ("/scope", "Show current scope variables"),
    ("/clear", "Clear screen"),
    ("/load <file>", "Load and execute a .rhai script"),
    ("/quit", "Exit REPL (or Ctrl+D)"),
];

/// Rhai keywords for syntax highlighting
const RHAI_KEYWORDS: &[&str] = &[
    "let", "const", "if", "else", "while", "loop", "for", "in", "break", "continue", "return",
    "throw", "try", "catch", "fn", "private", "import", "export", "as", "true", "false", "null",
];

// ============================================================================
// REPL Helper Implementation
// ============================================================================

/// Helper struct for rustyline
#[derive(Helper)]
struct ReplHelper {
    hinter: HistoryHinter,
}

impl Completer for ReplHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &Context<'_>,
    ) -> rustyline::Result<(usize, Vec<Pair>)> {
        let mut completions = Vec::new();

        // Find the word being typed
        let line_to_cursor = &line[..pos];
        let word_start = line_to_cursor
            .rfind(|c: char| !c.is_alphanumeric() && c != '_')
            .map(|i| i + 1)
            .unwrap_or(0);
        let word = &line_to_cursor[word_start..];

        if word.is_empty() {
            return Ok((pos, completions));
        }

        // Check for REPL commands
        if word.starts_with('/') {
            for (cmd, desc) in REPL_COMMANDS {
                let cmd_name = cmd.split_whitespace().next().unwrap_or(cmd);
                if cmd_name.starts_with(word) {
                    completions.push(Pair {
                        display: format!("{} - {}", cmd, desc),
                        replacement: cmd_name.to_string(),
                    });
                }
            }
            return Ok((word_start, completions));
        }

        // Check for builder methods (after a dot)
        if word.starts_with('.') || line_to_cursor.ends_with('.') {
            let method_word = if word.starts_with('.') { word } else { "." };

            // Add all builder-style methods
            for (method, desc) in BUILDER_METHODS
                .iter()
                .chain(TEXT_REPLACER_METHODS.iter())
                .chain(TEMPLATE_METHODS.iter())
            {
                if method.starts_with(method_word) {
                    let replacement = method.split('(').next().unwrap_or(method);
                    completions.push(Pair {
                        display: format!("{} - {}", method, desc),
                        replacement: replacement.to_string(),
                    });
                }
            }
            let start = if word.starts_with('.') {
                word_start
            } else {
                pos
            };
            return Ok((start, completions));
        }

        // Complete all functions
        let all_functions: Vec<(&str, &str, &str)> = TEXT_FUNCTIONS
            .iter()
            .chain(NET_FUNCTIONS.iter())
            .chain(HEROSCRIPT_FUNCTIONS.iter())
            .chain(OS_FUNCTIONS.iter())
            .chain(PROCESS_FUNCTIONS.iter())
            .chain(GIT_FUNCTIONS.iter())
            .chain(QCOW2_FUNCTIONS.iter())
            .chain(BUILDAH_FUNCTIONS.iter())
            .chain(NERDCTL_FUNCTIONS.iter())
            .chain(REDIS_FUNCTIONS.iter())
            .chain(POSTGRES_FUNCTIONS.iter())
            .chain(MQTT_FUNCTIONS.iter())
            .chain(MYCELIUM_FUNCTIONS.iter())
            .chain(HETZNER_FUNCTIONS.iter())
            .copied()
            .collect();

        for (func, ret, desc) in all_functions {
            let func_name = func.split('(').next().unwrap_or(func);
            if func_name.starts_with(word) {
                completions.push(Pair {
                    display: format!("{} -> {} - {}", func, ret, desc),
                    replacement: func_name.to_string(),
                });
            }
        }

        // Complete Rhai keywords
        for kw in RHAI_KEYWORDS {
            if kw.starts_with(word) && !completions.iter().any(|p| p.replacement == *kw) {
                completions.push(Pair {
                    display: format!("{} (keyword)", kw),
                    replacement: kw.to_string(),
                });
            }
        }

        Ok((word_start, completions))
    }
}

impl Hinter for ReplHelper {
    type Hint = String;

    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
        // First try history hints
        if let Some(hint) = self.hinter.hint(line, pos, ctx) {
            return Some(hint);
        }

        // Then try function signature hints
        let line_to_cursor = &line[..pos];

        let all_functions: Vec<(&str, &str, &str)> = TEXT_FUNCTIONS
            .iter()
            .chain(NET_FUNCTIONS.iter())
            .chain(HEROSCRIPT_FUNCTIONS.iter())
            .chain(OS_FUNCTIONS.iter())
            .chain(PROCESS_FUNCTIONS.iter())
            .chain(GIT_FUNCTIONS.iter())
            .chain(QCOW2_FUNCTIONS.iter())
            .chain(BUILDAH_FUNCTIONS.iter())
            .chain(NERDCTL_FUNCTIONS.iter())
            .chain(REDIS_FUNCTIONS.iter())
            .chain(POSTGRES_FUNCTIONS.iter())
            .chain(MQTT_FUNCTIONS.iter())
            .chain(MYCELIUM_FUNCTIONS.iter())
            .chain(HETZNER_FUNCTIONS.iter())
            .copied()
            .collect();

        for (func, ret, desc) in all_functions {
            let func_name = func.split('(').next().unwrap_or(func);
            if line_to_cursor.ends_with(func_name) {
                let signature = &func[func_name.len()..];
                return Some(
                    format!("{} -> {} | {}", signature, ret, desc)
                        .dimmed()
                        .to_string(),
                );
            }
        }

        None
    }
}

impl Highlighter for ReplHelper {
    fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
        let mut result = String::with_capacity(line.len() * 2);
        let mut chars = line.chars().peekable();
        let mut current_word = String::new();

        while let Some(c) = chars.next() {
            if c.is_alphanumeric() || c == '_' {
                current_word.push(c);
            } else {
                if !current_word.is_empty() {
                    result.push_str(&highlight_word(&current_word));
                    current_word.clear();
                }
                // Highlight strings
                if c == '"' {
                    result.push_str(&format!("{}", "\"".green()));
                    let mut string_content = String::new();
                    while let Some(&next) = chars.peek() {
                        chars.next();
                        if next == '"' {
                            result.push_str(&format!("{}", string_content.green()));
                            result.push_str(&format!("{}", "\"".green()));
                            break;
                        } else if next == '\\' {
                            string_content.push(next);
                            if let Some(escaped) = chars.next() {
                                string_content.push(escaped);
                            }
                        } else {
                            string_content.push(next);
                        }
                    }
                }
                // Highlight comments
                else if c == '/' && chars.peek() == Some(&'/') {
                    result.push_str(&format!(
                        "{}",
                        format!("//{}", chars.collect::<String>()).dimmed()
                    ));
                    break;
                }
                // Highlight operators
                else if "=+-*/<>!&|".contains(c) {
                    result.push_str(&format!("{}", c.to_string().yellow()));
                }
                // Parentheses and brackets
                else if "()[]{}".contains(c) {
                    result.push_str(&format!("{}", c.to_string().cyan()));
                } else {
                    result.push(c);
                }
            }
        }

        if !current_word.is_empty() {
            result.push_str(&highlight_word(&current_word));
        }

        Cow::Owned(result)
    }

    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        _default: bool,
    ) -> Cow<'b, str> {
        Cow::Owned(format!("{}", prompt.cyan().bold()))
    }

    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Cow::Owned(format!("{}", hint.dimmed()))
    }

    fn highlight_char(
        &self,
        _line: &str,
        _pos: usize,
        _kind: rustyline::highlight::CmdKind,
    ) -> bool {
        true
    }
}

/// Highlight a word based on its type
fn highlight_word(word: &str) -> String {
    // Rhai keywords
    if RHAI_KEYWORDS.contains(&word) {
        return format!("{}", word.magenta().bold());
    }

    // Collect all function names
    let all_func_names: Vec<&str> = TEXT_FUNCTIONS
        .iter()
        .chain(NET_FUNCTIONS.iter())
        .chain(HEROSCRIPT_FUNCTIONS.iter())
        .chain(OS_FUNCTIONS.iter())
        .chain(PROCESS_FUNCTIONS.iter())
        .chain(GIT_FUNCTIONS.iter())
        .chain(QCOW2_FUNCTIONS.iter())
        .chain(BUILDAH_FUNCTIONS.iter())
        .chain(NERDCTL_FUNCTIONS.iter())
        .chain(REDIS_FUNCTIONS.iter())
        .chain(POSTGRES_FUNCTIONS.iter())
        .chain(MQTT_FUNCTIONS.iter())
        .chain(MYCELIUM_FUNCTIONS.iter())
        .chain(HETZNER_FUNCTIONS.iter())
        .map(|(f, _, _)| f.split('(').next().unwrap_or(f))
        .collect();

    if all_func_names.contains(&word) {
        return format!("{}", word.blue().bold());
    }

    // Numbers
    if word.chars().all(|c| c.is_numeric() || c == '.') {
        return format!("{}", word.yellow());
    }

    // Booleans
    if word == "true" || word == "false" {
        return format!("{}", word.yellow().bold());
    }

    word.to_string()
}

impl Validator for ReplHelper {}

// ============================================================================
// Print Functions
// ============================================================================

/// Print the welcome banner
fn print_banner() {
    println!(
        "{}",
        "╔═══════════════════════════════════════════════════════════╗".cyan()
    );
    println!(
        "{}",
        "║                  Herodo Interactive Shell                 ║".cyan()
    );
    println!(
        "{}",
        "║         SAL Aggregator: Core + OS + Clients               ║".cyan()
    );
    println!(
        "{}",
        "╠═══════════════════════════════════════════════════════════╣".cyan()
    );
    println!(
        "{}  {}",
        "║".cyan(),
        format!(
            "{:<56} {}",
            "Tab: completion | Up/Down: history | Ctrl+D: exit", "║"
        )
        .cyan()
    );
    println!(
        "{}  {}",
        "║".cyan(),
        format!(
            "{:<56} {}",
            "Type /help for commands, /functions for API", "║"
        )
        .cyan()
    );
    println!(
        "{}",
        "╚═══════════════════════════════════════════════════════════╝".cyan()
    );
    println!();
}

/// Print help
fn print_help() {
    println!("{}", "REPL Commands:".yellow().bold());
    for (cmd, desc) in REPL_COMMANDS {
        println!("  {:20} {}", cmd.cyan(), desc);
    }
    println!();
    println!("{}", "Tips:".yellow().bold());
    println!("  - Press {} to autocomplete function names", "Tab".cyan());
    println!("  - Use {} arrows for command history", "Up/Down".cyan());
    println!(
        "  - Multi-line input: end lines with {} or use {}",
        "\\".cyan(),
        "{ }".cyan()
    );
}

/// Print function group
fn print_function_group(title: &str, functions: &[(&str, &str, &str)]) {
    println!("{}", format!("=== {} ===", title).yellow().bold());
    for (func, ret, desc) in functions {
        println!(
            "  {} {} {} {}",
            func.blue(),
            "->".dimmed(),
            ret.green(),
            format!("- {}", desc).dimmed()
        );
    }
}

/// Print method group
fn print_method_group(title: &str, methods: &[(&str, &str)]) {
    println!("{}", format!("--- {} ---", title).cyan().bold());
    for (method, desc) in methods {
        println!("  {:30} {}", method.cyan(), desc);
    }
}

/// Print all functions
fn print_functions() {
    print_function_group("Core - Text Functions", TEXT_FUNCTIONS);
    print_method_group("TextReplacer Methods", TEXT_REPLACER_METHODS);
    print_method_group("Template Methods", TEMPLATE_METHODS);
    println!();
    print_function_group("Core - Network Functions", NET_FUNCTIONS);
    println!();
    print_function_group("Core - HeroScript Functions", HEROSCRIPT_FUNCTIONS);
    println!();
    print_function_group("OS Functions", OS_FUNCTIONS);
    println!();
    print_function_group("Process Functions", PROCESS_FUNCTIONS);
    print_method_group("Command Builder Methods", BUILDER_METHODS);
    println!();
    print_function_group("Git Functions", GIT_FUNCTIONS);
    println!();
    print_function_group("Virt - QCOW2 Functions", QCOW2_FUNCTIONS);
    print_function_group("Virt - Buildah Functions", BUILDAH_FUNCTIONS);
    print_function_group("Virt - Nerdctl Functions", NERDCTL_FUNCTIONS);
    println!();
    print_function_group("Redis Functions", REDIS_FUNCTIONS);
    println!();
    print_function_group("PostgreSQL Functions", POSTGRES_FUNCTIONS);
    println!();
    print_function_group("MQTT Functions", MQTT_FUNCTIONS);
    println!();
    print_function_group("Mycelium Functions", MYCELIUM_FUNCTIONS);
    println!();
    print_function_group("Hetzner Functions", HETZNER_FUNCTIONS);
}

/// Print core functions
fn print_core_functions() {
    print_function_group("Text Functions", TEXT_FUNCTIONS);
    print_method_group("TextReplacer Methods", TEXT_REPLACER_METHODS);
    print_method_group("Template Methods", TEMPLATE_METHODS);
    println!();
    print_function_group("Network Functions", NET_FUNCTIONS);
    println!();
    print_function_group("HeroScript Functions", HEROSCRIPT_FUNCTIONS);
}

/// Print client functions
fn print_client_functions() {
    print_function_group("Redis Functions", REDIS_FUNCTIONS);
    println!();
    print_function_group("PostgreSQL Functions", POSTGRES_FUNCTIONS);
    println!();
    print_function_group("MQTT Functions", MQTT_FUNCTIONS);
    println!();
    print_function_group("Mycelium Functions", MYCELIUM_FUNCTIONS);
    println!();
    print_function_group("Hetzner Functions", HETZNER_FUNCTIONS);
}

/// Print virt functions
fn print_virt_functions() {
    print_function_group("QCOW2 Functions", QCOW2_FUNCTIONS);
    println!();
    print_function_group("Buildah Functions", BUILDAH_FUNCTIONS);
    println!();
    print_function_group("Nerdctl Functions", NERDCTL_FUNCTIONS);
}

/// Print current scope
fn print_scope(scope: &Scope) {
    if scope.is_empty() {
        println!("{}", "Scope is empty.".dimmed());
        return;
    }
    println!("{}", "Current scope:".yellow().bold());
    for (name, _constant, value) in scope.iter() {
        println!("  {} = {:?}", name.cyan(), value);
    }
}

// ============================================================================
// Engine Creation
// ============================================================================

/// Create and configure the Rhai engine with all SAL modules
fn create_engine() -> Result<Engine, Box<EvalAltResult>> {
    create_engine_with_base_path(None)
}

/// Create and configure the Rhai engine with a base path for module resolution
fn create_engine_with_base_path(
    base_path: Option<&std::path::Path>,
) -> Result<Engine, Box<EvalAltResult>> {
    let mut engine = Engine::new();

    // Register all modules from aggregated packages
    herolib_core::rhai::register_core_module(&mut engine)?;
    herolib_os::rhai::register_system_module(&mut engine)?;
    herolib_clients::rhai::register_clients_module(&mut engine)?;
    herolib_virt::rhai::register_kubernetes_module(&mut engine)?;

    // Set up module resolver for imports
    // If base_path is provided, use it; otherwise use current directory
    let resolver = if let Some(path) = base_path {
        FileModuleResolver::new_with_path(path)
    } else {
        FileModuleResolver::new_with_path(
            std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
        )
    };
    engine.set_module_resolver(resolver);

    // Configure print/debug callbacks
    engine.on_print(|s| {
        println!("{}", s);
    });

    engine.on_debug(|s, source, pos| {
        let location = match source {
            Some(src) => format!("[{}:{}] ", src, pos),
            None if !pos.is_none() => format!("[{}] ", pos),
            None => String::new(),
        };
        println!("{} {}{}", "[DEBUG]".dimmed(), location.dimmed(), s);
    });

    Ok(engine)
}

// ============================================================================
// Script Execution
// ============================================================================

/// Load and execute a script file
fn load_script(file_path: &str, scope: &mut Scope) -> Result<(), String> {
    let expanded_path = if file_path.starts_with("~/") {
        dirs::home_dir()
            .map(|h| h.join(&file_path[2..]))
            .unwrap_or_else(|| PathBuf::from(file_path))
    } else {
        PathBuf::from(file_path)
    };

    if !expanded_path.exists() {
        return Err(format!("File not found: {}", expanded_path.display()));
    }

    println!("{} {}", "Loading:".green(), expanded_path.display());

    // Use the script's directory as the base path for module resolution
    let base_path = expanded_path
        .parent()
        .unwrap_or_else(|| std::path::Path::new("."));
    let engine = create_engine_with_base_path(Some(base_path))
        .map_err(|e| format!("Failed to create engine: {}", e))?;

    let script =
        fs::read_to_string(&expanded_path).map_err(|e| format!("Failed to read file: {}", e))?;

    engine
        .run_with_scope(scope, &script)
        .map_err(|e| format!("{}", e))
}

/// Run a script from a file
fn run_script_file(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
    // Use the script's directory as the base path for module resolution
    let base_path = path.parent().unwrap_or_else(|| std::path::Path::new("."));
    let engine = create_engine_with_base_path(Some(base_path))?;
    let mut scope = Scope::new();

    let script = fs::read_to_string(path)?;

    match engine.run_with_scope(&mut scope, &script) {
        Ok(_) => Ok(()),
        Err(e) => {
            eprintln!("{}: {}", "Script error".red().bold(), e);
            Err(e.into())
        }
    }
}

/// Run a script from stdin
fn run_from_stdin() -> Result<(), Box<dyn std::error::Error>> {
    let engine = create_engine()?;
    let mut scope = Scope::new();

    let stdin = io::stdin();
    let script: String = stdin
        .lock()
        .lines()
        .filter_map(|l| l.ok())
        .collect::<Vec<_>>()
        .join("\n");

    match engine.run_with_scope(&mut scope, &script) {
        Ok(_) => Ok(()),
        Err(e) => {
            eprintln!("{}: {}", "Script error".red().bold(), e);
            Err(e.into())
        }
    }
}

// ============================================================================
// Interactive REPL
// ============================================================================

/// Run the interactive REPL
fn run_repl() -> Result<(), Box<dyn std::error::Error>> {
    print_banner();

    let engine = create_engine()?;
    let mut scope = Scope::new();

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(rustyline::CompletionType::List)
        .edit_mode(rustyline::EditMode::Emacs)
        .build();

    let helper = ReplHelper {
        hinter: HistoryHinter::new(),
    };

    let mut rl: Editor<ReplHelper, DefaultHistory> = Editor::with_config(config)?;
    rl.set_helper(Some(helper));

    // Load history
    let history_path = dirs::home_dir()
        .map(|h| h.join(".herodo_history"))
        .unwrap_or_else(|| PathBuf::from(".herodo_history"));
    let _ = rl.load_history(&history_path);

    let mut multiline_buffer = String::new();

    loop {
        let prompt = if multiline_buffer.is_empty() {
            "herodo> "
        } else {
            "  ... "
        };

        match rl.readline(prompt) {
            Ok(line) => {
                let line = line.trim();

                // Handle REPL commands (only when not in multiline mode)
                if multiline_buffer.is_empty() {
                    match line {
                        "/help" | "/h" => {
                            print_help();
                            continue;
                        }
                        "/functions" | "/fn" => {
                            print_functions();
                            continue;
                        }
                        "/core" => {
                            print_core_functions();
                            continue;
                        }
                        "/text" => {
                            print_function_group("Text Functions", TEXT_FUNCTIONS);
                            continue;
                        }
                        "/net" => {
                            print_function_group("Network Functions", NET_FUNCTIONS);
                            continue;
                        }
                        "/os" => {
                            print_function_group("OS Functions", OS_FUNCTIONS);
                            continue;
                        }
                        "/process" | "/proc" => {
                            print_function_group("Process Functions", PROCESS_FUNCTIONS);
                            print_method_group("Command Builder Methods", BUILDER_METHODS);
                            continue;
                        }
                        "/git" => {
                            print_function_group("Git Functions", GIT_FUNCTIONS);
                            continue;
                        }
                        "/virt" => {
                            print_virt_functions();
                            continue;
                        }
                        "/clients" => {
                            print_client_functions();
                            continue;
                        }
                        "/redis" => {
                            print_function_group("Redis Functions", REDIS_FUNCTIONS);
                            continue;
                        }
                        "/postgres" => {
                            print_function_group("PostgreSQL Functions", POSTGRES_FUNCTIONS);
                            continue;
                        }
                        "/mqtt" => {
                            print_function_group("MQTT Functions", MQTT_FUNCTIONS);
                            continue;
                        }
                        "/hetzner" => {
                            print_function_group("Hetzner Functions", HETZNER_FUNCTIONS);
                            continue;
                        }
                        "/builder" => {
                            print_method_group("Command Builder Methods", BUILDER_METHODS);
                            print_method_group("TextReplacer Methods", TEXT_REPLACER_METHODS);
                            print_method_group("Template Methods", TEMPLATE_METHODS);
                            continue;
                        }
                        "/scope" => {
                            print_scope(&scope);
                            continue;
                        }
                        "/clear" => {
                            print!("\x1B[2J\x1B[1;1H");
                            print_banner();
                            continue;
                        }
                        "/quit" | "/exit" | "/q" => {
                            println!("{}", "Goodbye!".cyan());
                            break;
                        }
                        "" => continue,
                        _ if line.starts_with("/load ") => {
                            let file_path = line.strip_prefix("/load ").unwrap().trim();
                            
                            // Check if the file has .rhai extension
                            if !file_path.ends_with(".rhai") {
                                println!("{}: File must have .rhai extension, got: {}", "Error".red().bold(), file_path);
                                println!();
                                continue;
                            }
                            
                            match load_script(file_path, &mut scope) {
                                Ok(_) => println!("{}", "Script executed successfully.".green()),
                                Err(e) => println!("{}: {}", "Error".red().bold(), e),
                            }
                            println!();
                            continue;
                        }
                        _ if line.starts_with('/') => {
                            println!(
                                "{}: Unknown command '{}'. Type /help for commands.",
                                "Error".red().bold(),
                                line
                            );
                            continue;
                        }
                        _ => {}
                    }
                }

                // Add to multiline buffer
                multiline_buffer.push_str(line);
                multiline_buffer.push('\n');

                // Check if we should continue multiline input
                let trimmed = line.trim_end();
                if trimmed.ends_with('\\') {
                    multiline_buffer = multiline_buffer
                        .trim_end()
                        .strip_suffix('\\')
                        .unwrap_or(&multiline_buffer)
                        .to_string();
                    multiline_buffer.push('\n');
                    continue;
                }

                // Check for unclosed braces/brackets
                let open_braces = multiline_buffer.matches('{').count();
                let close_braces = multiline_buffer.matches('}').count();
                let open_brackets = multiline_buffer.matches('[').count();
                let close_brackets = multiline_buffer.matches(']').count();
                let open_parens = multiline_buffer.matches('(').count();
                let close_parens = multiline_buffer.matches(')').count();

                if open_braces > close_braces
                    || open_brackets > close_brackets
                    || open_parens > close_parens
                {
                    continue;
                }

                // Execute the script
                let script = std::mem::take(&mut multiline_buffer);
                let script = script.trim();

                if script.is_empty() {
                    continue;
                }

                // Add to history
                let _ = rl.add_history_entry(script);

                // Execute
                match engine.eval_with_scope::<rhai::Dynamic>(&mut scope, script) {
                    Ok(result) => {
                        if !result.is_unit() {
                            println!("{} {}", "=>".green(), result);
                        }
                    }
                    Err(e) => {
                        println!("{}: {}", "Error".red().bold(), e);
                    }
                }
                println!();
            }
            Err(ReadlineError::Interrupted) => {
                // Ctrl+C - clear current input
                if !multiline_buffer.is_empty() {
                    multiline_buffer.clear();
                    println!("{}", "^C (input cleared)".dimmed());
                } else {
                    println!("{}", "^C (use /quit or Ctrl+D to exit)".dimmed());
                }
            }
            Err(ReadlineError::Eof) => {
                // Ctrl+D
                println!("{}", "Goodbye!".cyan());
                break;
            }
            Err(err) => {
                println!("{}: {:?}", "Error".red(), err);
                break;
            }
        }
    }

    // Save history
    let _ = rl.save_history(&history_path);

    Ok(())
}

// ============================================================================
// Usage and Main
// ============================================================================

/// Print usage information
fn print_usage(program: &str) {
    println!(
        "{}",
        "herodo - Interactive Rhai shell aggregating SAL packages"
            .cyan()
            .bold()
    );
    println!();
    println!("{}", "USAGE:".yellow().bold());
    println!("    {}                    Start interactive REPL", program);
    println!("    {} <script.rhai>      Run a .rhai script file (must end with .rhai)", program);
    println!("    {} --ui               Start interactive REPL", program);
    println!("    {} -i                 Read script from stdin", program);
    println!("    {} --help             Show this help", program);
    println!();
    println!("{}", "MODULES:".yellow().bold());
    println!(
        "    {} - Text processing, networking, HeroScript",
        "herolib-core".blue()
    );
    println!(
        "    {} - OS operations, process, git, virt",
        "herolib-os".blue()
    );
    println!(
        "    {} - Redis, PostgreSQL, MQTT, Mycelium, Hetzner",
        "herolib-clients".blue()
    );
    println!();
    println!("{}", "EXAMPLES:".yellow().bold());
    println!("    {}", format!("{} examples/test.rhai", program).dimmed());
    println!("    {}", format!("{} --ui", program).dimmed());
    println!(
        "    {}",
        format!("echo 'print(\"hello\")' | {} -i", program).dimmed()
    );
}

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() < 2 {
        // Default to REPL mode
        if let Err(e) = run_repl() {
            eprintln!("{}: {}", "Error".red().bold(), e);
            std::process::exit(1);
        }
        return;
    }

    match args[1].as_str() {
        "-i" | "--stdin" => {
            if let Err(e) = run_from_stdin() {
                eprintln!("{}: {}", "Error".red().bold(), e);
                std::process::exit(1);
            }
        }
        "--ui" | "--repl" => {
            if let Err(e) = run_repl() {
                eprintln!("{}: {}", "Error".red().bold(), e);
                std::process::exit(1);
            }
        }
        "-h" | "--help" | "help" => {
            print_usage(&args[0]);
        }
        arg => {
            let script_path = PathBuf::from(arg);
            
            // Check if the file has .rhai extension
            if !script_path.to_string_lossy().ends_with(".rhai") {
                eprintln!("{}: File must have .rhai extension, got: {}", "Error".red().bold(), arg);
                println!();
                print_usage(&args[0]);
                std::process::exit(1);
            }
            
            if script_path.exists() {
                if run_script_file(&script_path).is_err() {
                    std::process::exit(1);
                }
            } else {
                eprintln!("{}: File not found: {}", "Error".red().bold(), arg);
                println!();
                print_usage(&args[0]);
                std::process::exit(1);
            }
        }
    }
}
