//! Lux - A functional programming language with first-class effects

mod analysis;
mod ast;
mod codegen;
mod debugger;
mod diagnostics;
mod exhaustiveness;
mod formatter;
mod interpreter;
mod lexer;
mod linter;
mod lsp;
mod modules;
mod package;
mod parser;
mod registry;
mod schema;
mod symbol_table;
mod typechecker;
mod types;

use diagnostics::{render, c, bc, colors};
use interpreter::Interpreter;
use parser::Parser;
use rustyline::completion::{Completer, Pair};
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::history::DefaultHistory;
use rustyline::validate::Validator;
use rustyline::{Config, Editor, Helper};
use std::borrow::Cow;
use std::collections::HashSet;
use typechecker::TypeChecker;

const VERSION: &str = "0.1.0";

const HELP: &str = r#"
Lux - A functional language with first-class effects

Commands:
  :help, :h       Show this help
  :quit, :q       Exit the REPL
  :type <expr>    Show the type of an expression
  :info <name>    Show info about a binding
  :doc <name>     Show documentation for a function/type
  :browse <mod>   List exports of a module (List, String, Option, etc.)
  :env            Show user-defined bindings
  :clear          Clear the environment
  :load <file>    Load and execute a file
  :reload, :r     Reload the last loaded file
  :trace on/off   Enable/disable effect tracing
  :traces         Show recorded effect traces
  :ast <expr>     Show the AST of an expression (for debugging)

Keyboard:
  Tab             Autocomplete
  Ctrl-C          Cancel current input
  Ctrl-D          Exit
  Up/Down         Browse history
  Ctrl-R          Search history

Effects:
  All code in the REPL runs with Console, File, and other standard effects.
  Use :trace on to see effect invocations during execution.

Examples:
  > let x = 42
  > x + 1
  43

  > fn double(n: Int): Int = n * 2
  > :type double
  double : fn(Int) -> Int

  > :load myfile.lux
  > :reload
  > double(21)
  42

  > match Some(5) { Some(x) => x, None => 0 }
  5
"#;

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

    if args.len() > 1 {
        match args[1].as_str() {
            "--lsp" => {
                // Run LSP server
                if let Err(e) = lsp::LspServer::run() {
                    eprintln!("LSP server error: {}", e);
                    std::process::exit(1);
                }
            }
            "--help" | "-h" => {
                print_help();
            }
            "--version" | "-v" => {
                println!("{}", bc(colors::GREEN, &format!("Lux {}", VERSION)));
            }
            "fmt" | "f" => {
                // Format files (auto-discovers if no file specified)
                format_files(&args[2..]);
            }
            "lint" | "l" => {
                // Lint files
                lint_files(&args[2..]);
            }
            "test" | "t" => {
                // Run tests
                run_tests(&args[2..]);
            }
            "watch" => {
                // Watch mode
                if args.len() < 3 {
                    eprintln!("Usage: lux watch <file.lux>");
                    std::process::exit(1);
                }
                watch_file(&args[2]);
            }
            "init" => {
                // Initialize a new project
                init_project(args.get(2).map(|s| s.as_str()));
            }
            "check" | "k" => {
                // Type check files (auto-discovers if no file specified)
                check_files(&args[2..]);
            }
            "debug" => {
                // Start debugger
                if args.len() < 3 {
                    eprintln!("Usage: lux debug <file.lux>");
                    std::process::exit(1);
                }
                if let Err(e) = debugger::Debugger::run(&args[2]) {
                    eprintln!("Debugger error: {}", e);
                    std::process::exit(1);
                }
            }
            "pkg" => {
                // Package manager
                handle_pkg_command(&args[2..]);
            }
            "registry" => {
                // Run package registry server
                let storage = args.iter()
                    .position(|a| a == "--storage" || a == "-s")
                    .and_then(|i| args.get(i + 1))
                    .map(|s| s.as_str())
                    .unwrap_or("./lux-registry");

                let bind = args.iter()
                    .position(|a| a == "--bind" || a == "-b")
                    .and_then(|i| args.get(i + 1))
                    .map(|s| s.as_str())
                    .unwrap_or("127.0.0.1:8080");

                if let Err(e) = registry::run_registry_server(storage, bind) {
                    eprintln!("Registry server error: {}", e);
                    std::process::exit(1);
                }
            }
            "serve" | "s" => {
                // Static file server (like python -m http.server)
                let port = args.iter()
                    .position(|a| a == "--port" || a == "-p")
                    .and_then(|i| args.get(i + 1))
                    .and_then(|s| s.parse::<u16>().ok())
                    .unwrap_or(8080);

                let dir = args.get(2)
                    .filter(|a| !a.starts_with('-'))
                    .map(|s| s.as_str())
                    .unwrap_or(".");

                serve_static_files(dir, port);
            }
            "compile" | "c" => {
                // Compile to native binary or JavaScript
                if args.len() < 3 {
                    eprintln!("Usage: lux compile <file.lux> [-o binary]");
                    eprintln!("       lux compile <file.lux> --run");
                    eprintln!("       lux compile <file.lux> --emit-c [-o file.c]");
                    eprintln!("       lux compile <file.lux> --target js [-o file.js]");
                    std::process::exit(1);
                }
                let run_after = args.iter().any(|a| a == "--run");
                let emit_c = args.iter().any(|a| a == "--emit-c");
                let target_js = args.iter()
                    .position(|a| a == "--target")
                    .and_then(|i| args.get(i + 1))
                    .map(|s| s.as_str() == "js")
                    .unwrap_or(false);
                let output_path = args.iter()
                    .position(|a| a == "-o")
                    .and_then(|i| args.get(i + 1))
                    .map(|s| s.as_str());

                if target_js {
                    compile_to_js(&args[2], output_path, run_after);
                } else {
                    compile_to_c(&args[2], output_path, run_after, emit_c);
                }
            }
            "doc" => {
                // Generate API documentation
                generate_docs(&args[2..]);
            }
            cmd => {
                // Check if it looks like a command typo
                if !std::path::Path::new(cmd).exists() && !cmd.starts_with('-') && !cmd.contains('.') && !cmd.contains('/') {
                    let known_commands = vec![
                        "fmt", "lint", "test", "watch", "init", "check", "debug",
                        "pkg", "registry", "serve", "compile", "doc",
                    ];
                    let suggestions = diagnostics::find_similar_names(cmd, known_commands.into_iter(), 2);
                    if !suggestions.is_empty() {
                        if let Some(hint) = diagnostics::format_did_you_mean(&suggestions) {
                            eprintln!("{} Unknown command '{}'. {}", c(colors::YELLOW, "warning:"), cmd, c(colors::YELLOW, &hint));
                            std::process::exit(1);
                        }
                    }
                }
                // Run as file
                run_file(cmd);
            }
        }
    } else {
        // Start REPL
        run_repl();
    }
}

fn print_help() {
    println!("{}", bc(colors::GREEN, &format!("Lux {}", VERSION)));
    println!("{}", c(colors::DIM, "A functional language with first-class effects"));
    println!();
    println!("{}", bc("", "Usage:"));
    println!();
    println!("  {}                        Start the REPL", bc(colors::CYAN, "lux"));
    println!("  {} {}             Run a file (interpreter)", bc(colors::CYAN, "lux"), c(colors::YELLOW, "<file.lux>"));
    println!("  {} {} {}   Compile to native binary", bc(colors::CYAN, "lux"), bc(colors::CYAN, "compile"), c(colors::YELLOW, "<file.lux>"));
    println!("  {} {} {} {}   Compile with output name", bc(colors::CYAN, "lux"), bc(colors::CYAN, "compile"), c(colors::YELLOW, "<f>"), c(colors::YELLOW, "-o app"));
    println!("  {} {} {} {}    Compile and execute", bc(colors::CYAN, "lux"), bc(colors::CYAN, "compile"), c(colors::YELLOW, "<f>"), c(colors::YELLOW, "--run"));
    println!("  {} {} {} {} Output C code", bc(colors::CYAN, "lux"), bc(colors::CYAN, "compile"), c(colors::YELLOW, "<f>"), c(colors::YELLOW, "--emit-c"));
    println!("  {} {} {} {}  Compile to JavaScript", bc(colors::CYAN, "lux"), bc(colors::CYAN, "compile"), c(colors::YELLOW, "<f>"), c(colors::YELLOW, "--target js"));
    println!("  {} {} {}   Format files {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "fmt"), c(colors::YELLOW, "[file] [--check]"),
        c(colors::DIM, "(alias: f)"));
    println!("  {} {} {}        Lint files {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "lint"), c(colors::YELLOW, "[file]"),
        c(colors::DIM, "(alias: l)"));
    println!("  {} {} {}         Type check files {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "check"), c(colors::YELLOW, "[file]"),
        c(colors::DIM, "(alias: k)"));
    println!("  {} {} {}      Run tests {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "test"), c(colors::YELLOW, "[pattern]"),
        c(colors::DIM, "(alias: t)"));
    println!("  {} {} {}     Watch and re-run on changes",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "watch"), c(colors::YELLOW, "<file.lux>"));
    println!("  {} {} {}     Start interactive debugger",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "debug"), c(colors::YELLOW, "<file.lux>"));
    println!("  {} {} {}          Initialize a new project",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "init"), c(colors::YELLOW, "[name]"));
    println!("  {} {} {}        Package manager {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "pkg"), c(colors::YELLOW, "<command>"),
        c(colors::DIM, "(install, add, remove, list, update)"));
    println!("  {} {}             Start package registry server",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "registry"));
    println!("  {} {} {}          Start static file server {}",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "serve"), c(colors::YELLOW, "[dir]"),
        c(colors::DIM, "(alias: s)"));
    println!("  {} {} {}  Generate API documentation",
        bc(colors::CYAN, "lux"), bc(colors::CYAN, "doc"), c(colors::YELLOW, "[file] [-o dir]"));
    println!("  {} {}                Start LSP server",
        bc(colors::CYAN, "lux"), c(colors::YELLOW, "--lsp"));
    println!("  {} {}               Show this help",
        bc(colors::CYAN, "lux"), c(colors::YELLOW, "--help"));
    println!("  {} {}            Show version",
        bc(colors::CYAN, "lux"), c(colors::YELLOW, "--version"));
}

fn format_files(args: &[String]) {
    use formatter::{format, FormatConfig};
    use std::path::Path;
    use std::time::Instant;

    let start = Instant::now();
    let check_only = args.iter().any(|a| a == "--check");
    let pattern = args.iter().find(|a| !a.starts_with('-')).map(|s| s.as_str());

    // Collect files to format
    let mut files_to_format = Vec::new();

    // If a specific file is given, use it
    if let Some(p) = pattern {
        if Path::new(p).is_file() {
            files_to_format.push(std::path::PathBuf::from(p));
        }
    }

    // If no specific file found, auto-discover
    if files_to_format.is_empty() {
        // Current directory (non-recursive for src/)
        if Path::new("src").is_dir() {
            collect_lux_files("src", pattern, &mut files_to_format);
        }

        // Also check current directory for top-level files
        collect_lux_files_nonrecursive(".", pattern, &mut files_to_format);

        // examples/ subdirectory
        if Path::new("examples").is_dir() {
            collect_lux_files("examples", pattern, &mut files_to_format);
        }

        // tests/ subdirectory
        if Path::new("tests").is_dir() {
            collect_lux_files("tests", pattern, &mut files_to_format);
        }
    }

    if files_to_format.is_empty() {
        println!("No .lux files found.");
        println!("{}", c(colors::DIM, "Looking in: ., src/, examples/, tests/"));
        return;
    }

    // Sort for consistent output
    files_to_format.sort();

    let config = FormatConfig::default();
    let mut formatted_count = 0;
    let mut unchanged_count = 0;
    let mut error_count = 0;
    let mut would_reformat = Vec::new();

    for file_path in &files_to_format {
        let path = file_path.to_string_lossy().to_string();

        let source = match std::fs::read_to_string(file_path) {
            Ok(s) => s,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                error_count += 1;
                continue;
            }
        };

        let formatted = match format(&source, &config) {
            Ok(f) => f,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                error_count += 1;
                continue;
            }
        };

        if check_only {
            if source != formatted {
                would_reformat.push(path);
            } else {
                unchanged_count += 1;
            }
        } else if source != formatted {
            if let Err(e) = std::fs::write(file_path, &formatted) {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                error_count += 1;
                continue;
            }
            println!("  {} {}", c(colors::CYAN, "Formatted"), path);
            formatted_count += 1;
        } else {
            unchanged_count += 1;
        }
    }

    let elapsed = start.elapsed();
    let time_str = c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64()));

    println!();
    if check_only {
        if !would_reformat.is_empty() {
            println!("Files that would be reformatted:");
            for path in &would_reformat {
                println!("  {}", c(colors::YELLOW, path));
            }
            println!();
            println!("{} {} would be reformatted, {} already formatted {}",
                c(colors::YELLOW, "\u{2717}"), would_reformat.len(), unchanged_count, time_str);
            std::process::exit(1);
        } else {
            println!("{} All {} files are correctly formatted {}",
                c(colors::GREEN, "\u{2713}"), unchanged_count, time_str);
        }
    } else if error_count > 0 {
        let mut parts = Vec::new();
        if formatted_count > 0 { parts.push(format!("{} formatted", formatted_count)); }
        if unchanged_count > 0 { parts.push(format!("{} unchanged", unchanged_count)); }
        parts.push(format!("{} errors", error_count));
        println!("{} {} {}", c(colors::RED, "\u{2717}"), parts.join(", "), time_str);
        std::process::exit(1);
    } else if formatted_count > 0 {
        println!("{} {} formatted, {} unchanged {}",
            c(colors::GREEN, "\u{2713}"), formatted_count, unchanged_count, time_str);
    } else {
        println!("{} All {} files already formatted {}",
            c(colors::GREEN, "\u{2713}"), unchanged_count, time_str);
    }
}

fn lint_files(args: &[String]) {
    use linter::{LintConfig, LintLevel, Linter};
    use std::path::Path;
    use std::time::Instant;

    let start = Instant::now();
    let explain = args.iter().any(|a| a == "--explain");
    let _fix = args.iter().any(|a| a == "--fix");
    let pattern = args
        .iter()
        .find(|a| !a.starts_with('-'))
        .map(|s| s.as_str());

    // Collect files to lint
    let mut files_to_lint = Vec::new();

    if let Some(p) = pattern {
        if Path::new(p).is_file() {
            files_to_lint.push(std::path::PathBuf::from(p));
        }
    }

    if files_to_lint.is_empty() {
        if Path::new("src").is_dir() {
            collect_lux_files("src", pattern, &mut files_to_lint);
        }
        collect_lux_files_nonrecursive(".", pattern, &mut files_to_lint);
        if Path::new("examples").is_dir() {
            collect_lux_files("examples", pattern, &mut files_to_lint);
        }
        if Path::new("tests").is_dir() {
            collect_lux_files("tests", pattern, &mut files_to_lint);
        }
    }

    if files_to_lint.is_empty() {
        println!("No .lux files found.");
        println!("{}", c(colors::DIM, "Looking in: ., src/, examples/, tests/"));
        return;
    }

    files_to_lint.sort();

    let config = LintConfig::default();
    let mut total_warnings = 0;
    let mut total_errors = 0;
    let mut files_clean = 0;

    for file_path in &files_to_lint {
        let path = file_path.to_string_lossy().to_string();
        let source = match std::fs::read_to_string(file_path) {
            Ok(s) => s,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                total_errors += 1;
                continue;
            }
        };

        let program = match Parser::parse_source(&source) {
            Ok(p) => p,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, c(colors::RED, &format!("parse error: {}", e)));
                total_errors += 1;
                continue;
            }
        };

        let linter = Linter::new(config.clone(), &source);
        let lints = linter.lint(&program);

        if lints.is_empty() {
            files_clean += 1;
        } else {
            let errors = lints.iter().filter(|l| l.level == LintLevel::Deny).count();
            let warnings = lints
                .iter()
                .filter(|l| l.level == LintLevel::Warn)
                .count();
            total_errors += errors;
            total_warnings += warnings;

            if explain {
                eprint!("{}", linter::render_lints(&lints, &source, Some(&path)));
            } else {
                // Compact output: one line per lint
                for lint in &lints {
                    let level_str = match lint.level {
                        LintLevel::Deny => c(colors::RED, "error"),
                        LintLevel::Warn => c(colors::YELLOW, "warning"),
                        LintLevel::Allow => continue,
                    };
                    let lint_code = c(
                        colors::DIM,
                        &format!("[{}/{}]", lint.id.category().category_name(), lint.id.name()),
                    );
                    if lint.span.start > 0 || lint.span.end > 0 {
                        let (line, col) =
                            diagnostics::offset_to_line_col(&source, lint.span.start);
                        eprintln!(
                            "  {}{} {}:{}:{}: {}",
                            level_str, lint_code, path, line, col, lint.message
                        );
                    } else {
                        eprintln!("  {}{} {}: {}", level_str, lint_code, path, lint.message);
                    }
                }
            }
        }
    }

    let elapsed = start.elapsed();
    let time_str = c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64()));

    println!();
    if total_errors > 0 || total_warnings > 0 {
        let mut parts = Vec::new();
        if total_errors > 0 {
            parts.push(c(colors::RED, &format!("{} errors", total_errors)));
        }
        if total_warnings > 0 {
            parts.push(c(
                colors::YELLOW,
                &format!("{} warnings", total_warnings),
            ));
        }
        parts.push(format!("{} files clean", files_clean));
        let icon = if total_errors > 0 {
            c(colors::RED, "\u{2717}")
        } else {
            c(colors::YELLOW, "\u{26a0}")
        };
        println!("{} {} {}", icon, parts.join(", "), time_str);
        if total_errors > 0 {
            std::process::exit(1);
        }
    } else {
        println!(
            "{} {} files clean {}",
            c(colors::GREEN, "\u{2713}"),
            files_clean,
            time_str
        );
    }
}

fn check_files(args: &[String]) {
    use linter::{LintConfig, LintLevel, Linter};
    use modules::ModuleLoader;
    use std::path::Path;
    use std::time::Instant;

    let start = Instant::now();
    let pattern = args.first().map(|s| s.as_str());

    // Collect files to check
    let mut files_to_check = Vec::new();

    // If a specific file is given, use it
    if let Some(p) = pattern {
        if Path::new(p).is_file() {
            files_to_check.push(std::path::PathBuf::from(p));
        }
    }

    // If no specific file found, auto-discover
    if files_to_check.is_empty() {
        // Current directory (non-recursive for src/)
        if Path::new("src").is_dir() {
            collect_lux_files("src", pattern, &mut files_to_check);
        }

        // Also check current directory for top-level files
        collect_lux_files_nonrecursive(".", pattern, &mut files_to_check);

        // examples/ subdirectory
        if Path::new("examples").is_dir() {
            collect_lux_files("examples", pattern, &mut files_to_check);
        }

        // tests/ subdirectory
        if Path::new("tests").is_dir() {
            collect_lux_files("tests", pattern, &mut files_to_check);
        }
    }

    if files_to_check.is_empty() {
        println!("No .lux files found.");
        println!("{}", c(colors::DIM, "Looking in: ., src/, examples/, tests/"));
        return;
    }

    // Sort for consistent output
    files_to_check.sort();

    let mut passed = 0;
    let mut failed = 0;
    let mut total_warnings = 0;

    let lint_config = LintConfig::default();

    for file_path in &files_to_check {
        let path = file_path.to_string_lossy().to_string();
        let source = match std::fs::read_to_string(file_path) {
            Ok(s) => s,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                failed += 1;
                continue;
            }
        };

        let mut loader = ModuleLoader::new();
        if let Some(parent) = file_path.parent() {
            loader.add_search_path(parent.to_path_buf());
        }

        let program = match loader.load_source(&source, Some(file_path)) {
            Ok(p) => p,
            Err(e) => {
                eprintln!("{} {}: {}", c(colors::RED, "\u{2717}"), path, e);
                failed += 1;
                continue;
            }
        };

        let mut checker = TypeChecker::new();
        if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
            eprintln!("{} {}", c(colors::RED, "\u{2717}"), path);
            for error in errors {
                let diagnostic = error.to_diagnostic();
                eprint!("{}", render(&diagnostic, &source, Some(&path)));
            }
            failed += 1;
        } else {
            // Type check passed — also run lints
            let linter = Linter::new(lint_config.clone(), &source);
            let lints = linter.lint(&program);
            let warnings = lints.iter().filter(|l| l.level == LintLevel::Warn).count();
            let errors = lints.iter().filter(|l| l.level == LintLevel::Deny).count();

            if errors > 0 {
                eprintln!("{} {}", c(colors::RED, "\u{2717}"), path);
                for lint in &lints {
                    if lint.level == LintLevel::Deny {
                        let lint_code = c(
                            colors::DIM,
                            &format!("[{}/{}]", lint.id.category().category_name(), lint.id.name()),
                        );
                        if lint.span.start > 0 || lint.span.end > 0 {
                            let (line, col) = diagnostics::offset_to_line_col(&source, lint.span.start);
                            eprintln!("  {}{} {}:{}:{}: {}",
                                c(colors::RED, "error"), lint_code, path, line, col, lint.message);
                        } else {
                            eprintln!("  {}{} {}: {}",
                                c(colors::RED, "error"), lint_code, path, lint.message);
                        }
                    }
                }
                failed += 1;
            } else if warnings > 0 {
                println!("{} {} {}", c(colors::YELLOW, "\u{26a0}"), path,
                    c(colors::DIM, &format!("({} lint warnings)", warnings)));
                total_warnings += warnings;
                passed += 1;
            } else {
                println!("{} {}", c(colors::GREEN, "\u{2713}"), path);
                passed += 1;
            }
        }
    }

    let elapsed = start.elapsed();
    let time_str = c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64()));

    println!();
    if failed > 0 {
        let mut summary = format!("{} {} passed, {} failed",
            c(colors::RED, "\u{2717}"), passed, failed);
        if total_warnings > 0 {
            summary.push_str(&format!(", {}", c(colors::YELLOW, &format!("{} warnings", total_warnings))));
        }
        println!("{} {}", summary, time_str);
        std::process::exit(1);
    } else {
        let mut summary = format!("{} {} passed",
            c(colors::GREEN, "\u{2713}"), passed);
        if total_warnings > 0 {
            summary.push_str(&format!(", {}", c(colors::YELLOW, &format!("{} warnings", total_warnings))));
        }
        println!("{} {}", summary, time_str);
    }
}

fn collect_lux_files_nonrecursive(dir: &str, pattern: Option<&str>, files: &mut Vec<std::path::PathBuf>) {
    use std::fs;

    for entry in fs::read_dir(dir).into_iter().flatten().flatten() {
        let path = entry.path();
        if path.is_file() && path.extension().map(|e| e == "lux").unwrap_or(false) {
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                if pattern.map(|p| name.contains(p)).unwrap_or(true) {
                    files.push(path);
                }
            }
        }
    }
}

fn compile_to_c(path: &str, output_path: Option<&str>, run_after: bool, emit_c: bool) {
    use codegen::c_backend::CBackend;
    use modules::ModuleLoader;
    use std::path::Path;
    use std::process::Command;
    use std::time::Instant;

    let start = Instant::now();
    let file_path = Path::new(path);
    let source = match std::fs::read_to_string(file_path) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("{} Error reading file '{}': {}", c(colors::RED, "error:"), path, e);
            std::process::exit(1);
        }
    };

    // Parse with module loading
    let mut loader = ModuleLoader::new();
    if let Some(parent) = file_path.parent() {
        loader.add_search_path(parent.to_path_buf());
    }

    let program = match loader.load_source(&source, Some(file_path)) {
        Ok(p) => p,
        Err(e) => {
            eprintln!("{} {}", c(colors::RED, "error:"), e);
            std::process::exit(1);
        }
    };

    // Type check
    let mut checker = TypeChecker::new();
    if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
        for error in errors {
            let diagnostic = error.to_diagnostic();
            eprint!("{}", render(&diagnostic, &source, Some(path)));
        }
        std::process::exit(1);
    }

    // Generate C code
    let mut backend = CBackend::new();
    let c_code = match backend.generate(&program) {
        Ok(code) => code,
        Err(e) => {
            eprintln!("{} C codegen: {}", c(colors::RED, "error:"), e);
            eprintln!();
            eprintln!("{}", c(colors::DIM, "Note: The C backend supports functions, closures, ADTs,"));
            eprintln!("{}", c(colors::DIM, "pattern matching, lists, and Console.print."));
            eprintln!("{}", c(colors::DIM, "Not yet supported: other effects, some advanced features."));
            std::process::exit(1);
        }
    };

    // Handle --emit-c: output C code instead of binary
    if emit_c {
        if let Some(out_path) = output_path {
            if let Err(e) = std::fs::write(out_path, &c_code) {
                eprintln!("{} writing '{}': {}", c(colors::RED, "error:"), out_path, e);
                std::process::exit(1);
            }
            let elapsed = start.elapsed();
            eprintln!("{} Emitted C to {} {}",
                c(colors::GREEN, "\u{2713}"), bc(colors::CYAN, out_path),
                c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64())));
        } else {
            println!("{}", c_code);
        }
        return;
    }

    // Default: compile to native binary
    let temp_c = std::env::temp_dir().join("lux_output.c");

    // Determine output binary name
    let output_bin = if let Some(out) = output_path {
        Path::new(out).to_path_buf()
    } else {
        // Derive from source filename: foo.lux -> ./foo
        let stem = file_path.file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("a.out");
        Path::new(".").join(stem)
    };

    if let Err(e) = std::fs::write(&temp_c, &c_code) {
        eprintln!("{} writing temp file: {}", c(colors::RED, "error:"), e);
        std::process::exit(1);
    }

    // Find C compiler
    let cc = std::env::var("CC").unwrap_or_else(|_| "cc".to_string());

    let compile_result = Command::new(&cc)
        .args(["-O2", "-o"])
        .arg(&output_bin)
        .arg(&temp_c)
        .output();

    match compile_result {
        Ok(output) => {
            if !output.status.success() {
                eprintln!("{} C compilation failed:", c(colors::RED, "error:"));
                eprintln!("{}", String::from_utf8_lossy(&output.stderr));
                std::process::exit(1);
            }
        }
        Err(e) => {
            eprintln!("{} Failed to run C compiler '{}': {}", c(colors::RED, "error:"), cc, e);
            eprintln!("{}", c(colors::DIM, "Make sure gcc or clang is installed, or set CC environment variable."));
            std::process::exit(1);
        }
    }

    let elapsed = start.elapsed();

    if run_after {
        // Run the compiled binary
        let run_result = Command::new(&output_bin).status();
        match run_result {
            Ok(status) => {
                std::process::exit(status.code().unwrap_or(1));
            }
            Err(e) => {
                eprintln!("{} Failed to run compiled binary: {}", c(colors::RED, "error:"), e);
                std::process::exit(1);
            }
        }
    } else {
        eprintln!("{} Compiled to {} {}",
            c(colors::GREEN, "\u{2713}"),
            bc(colors::CYAN, &output_bin.display().to_string()),
            c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64())));
    }
}

fn compile_to_js(path: &str, output_path: Option<&str>, run_after: bool) {
    use codegen::js_backend::JsBackend;
    use modules::ModuleLoader;
    use std::path::Path;
    use std::process::Command;
    use std::time::Instant;

    let start = Instant::now();
    let file_path = Path::new(path);
    let source = match std::fs::read_to_string(file_path) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("{} Error reading file '{}': {}", c(colors::RED, "error:"), path, e);
            std::process::exit(1);
        }
    };

    // Parse with module loading
    let mut loader = ModuleLoader::new();
    if let Some(parent) = file_path.parent() {
        loader.add_search_path(parent.to_path_buf());
    }

    let program = match loader.load_source(&source, Some(file_path)) {
        Ok(p) => p,
        Err(e) => {
            eprintln!("{} {}", c(colors::RED, "error:"), e);
            std::process::exit(1);
        }
    };

    // Type check
    let mut checker = TypeChecker::new();
    if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
        for error in errors {
            let diagnostic = error.to_diagnostic();
            eprint!("{}", render(&diagnostic, &source, Some(path)));
        }
        std::process::exit(1);
    }

    // Generate JavaScript code
    let mut backend = JsBackend::new();
    let js_code = match backend.generate(&program) {
        Ok(code) => code,
        Err(e) => {
            eprintln!("{} JS codegen: {}", c(colors::RED, "error:"), e);
            std::process::exit(1);
        }
    };

    // Determine output file path
    let output_js = if let Some(out) = output_path {
        Path::new(out).to_path_buf()
    } else {
        // Derive from source filename: foo.lux -> ./foo.js
        let stem = file_path.file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("output");
        Path::new(".").join(format!("{}.js", stem))
    };

    // Write the JavaScript file
    if let Err(e) = std::fs::write(&output_js, &js_code) {
        eprintln!("{} writing '{}': {}", c(colors::RED, "error:"), output_js.display(), e);
        std::process::exit(1);
    }

    let elapsed = start.elapsed();

    if run_after {
        // Run with Node.js
        let node_result = Command::new("node")
            .arg(&output_js)
            .status();

        match node_result {
            Ok(status) => {
                std::process::exit(status.code().unwrap_or(1));
            }
            Err(e) => {
                eprintln!("{} Failed to run with Node.js: {}", c(colors::RED, "error:"), e);
                eprintln!("{}", c(colors::DIM, "Make sure Node.js is installed."));
                std::process::exit(1);
            }
        }
    } else {
        eprintln!("{} Compiled to {} {}",
            c(colors::GREEN, "\u{2713}"),
            bc(colors::CYAN, &output_js.display().to_string()),
            c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64())));
    }
}

fn run_tests(args: &[String]) {
    use std::path::Path;
    use std::fs;
    use std::time::Instant;

    let start = Instant::now();

    // Find test files
    let pattern = args.first().map(|s| s.as_str());

    // Look for test files in multiple locations
    let mut test_files = Vec::new();

    // Current directory
    collect_test_files(".", pattern, &mut test_files);

    // tests/ subdirectory
    if Path::new("tests").is_dir() {
        collect_test_files("tests", pattern, &mut test_files);
    }

    // examples/ subdirectory (for example tests)
    if Path::new("examples").is_dir() {
        collect_test_files("examples", pattern, &mut test_files);
    }

    // If a specific file is given, use that
    if let Some(p) = pattern {
        if Path::new(p).is_file() {
            test_files.clear();
            test_files.push(std::path::PathBuf::from(p));
        }
    }

    if test_files.is_empty() {
        println!("No test files found.");
        println!("{}", c(colors::DIM, "Test files should be named test_*.lux or *_test.lux"));
        println!("{}", c(colors::DIM, "Or contain functions named test_*"));
        return;
    }

    println!("{}\n", bc("", "Running tests..."));

    let mut total_passed = 0;
    let mut total_failed = 0;
    let mut all_failures = Vec::new();

    for test_file in &test_files {
        let path_str = test_file.to_string_lossy().to_string();

        // Read and parse the file
        let source = match fs::read_to_string(test_file) {
            Ok(s) => s,
            Err(e) => {
                println!("  {} {} {}", c(colors::RED, "\u{2717}"), path_str, c(colors::RED, &e.to_string()));
                total_failed += 1;
                continue;
            }
        };

        let program = match Parser::parse_source(&source) {
            Ok(p) => p,
            Err(e) => {
                println!("  {} {} {}", c(colors::RED, "\u{2717}"), path_str, c(colors::RED, &format!("parse error: {}", e)));
                total_failed += 1;
                continue;
            }
        };

        // Type check
        let mut checker = typechecker::TypeChecker::new();
        if let Err(errors) = checker.check_program(&program) {
            println!("  {} {} {}", c(colors::RED, "\u{2717}"), path_str, c(colors::RED, "type error"));
            for err in errors {
                eprintln!("    {}", err);
            }
            total_failed += 1;
            continue;
        }

        // Get auto-generated migrations from typechecker
        let auto_migrations = checker.get_auto_migrations().clone();

        // Find test functions (functions starting with test_)
        let test_funcs: Vec<_> = program.declarations.iter().filter_map(|d| {
            if let ast::Declaration::Function(f) = d {
                if f.name.name.starts_with("test_") {
                    return Some(f.name.name.clone());
                }
            }
            None
        }).collect();

        if test_funcs.is_empty() {
            // No test functions, run the whole file
            let mut interp = Interpreter::new();
            interp.register_auto_migrations(&auto_migrations);
            interp.reset_test_results();

            match interp.run(&program) {
                Ok(_) => {
                    let results = interp.get_test_results();
                    if results.failed == 0 && results.passed == 0 {
                        println!("  {} {} {}", c(colors::GREEN, "\u{2713}"), path_str, c(colors::DIM, "(no assertions)"));
                        total_passed += 1;
                    } else if results.failed == 0 {
                        println!("  {} {} {}", c(colors::GREEN, "\u{2713}"), path_str,
                            c(colors::DIM, &format!("({} assertions)", results.passed)));
                        total_passed += results.passed;
                    } else {
                        println!("  {} {} {}", c(colors::RED, "\u{2717}"), path_str,
                            c(colors::RED, &format!("{} passed, {} failed", results.passed, results.failed)));
                        total_passed += results.passed;
                        total_failed += results.failed;
                        for failure in &results.failures {
                            all_failures.push((path_str.clone(), "".to_string(), failure.clone()));
                        }
                    }
                }
                Err(e) => {
                    println!("  {} {} {}", c(colors::RED, "\u{2717}"), path_str, c(colors::RED, &format!("runtime error: {}", e)));
                    total_failed += 1;
                }
            }
        } else {
            // Run individual test functions
            println!("  {}:", c(colors::DIM, &path_str));

            for test_name in &test_funcs {
                let mut interp = Interpreter::new();
                interp.register_auto_migrations(&auto_migrations);
                interp.reset_test_results();

                // First run the file to define all functions
                if let Err(e) = interp.run(&program) {
                    println!("    {} {} {}", c(colors::RED, "\u{2717}"), test_name, c(colors::RED, &e.to_string()));
                    total_failed += 1;
                    continue;
                }

                // Call the test function
                let call_source = format!("let testResult = run {}() with {{}}", test_name);
                let call_program = match Parser::parse_source(&call_source) {
                    Ok(p) => p,
                    Err(e) => {
                        println!("    {} {} {}", c(colors::RED, "\u{2717}"), test_name, c(colors::RED, &e.to_string()));
                        total_failed += 1;
                        continue;
                    }
                };

                match interp.run(&call_program) {
                    Ok(_) => {
                        let results = interp.get_test_results();
                        if results.failed == 0 {
                            println!("    {} {}", c(colors::GREEN, "\u{2713}"), test_name);
                            total_passed += 1;
                        } else {
                            println!("    {} {}", c(colors::RED, "\u{2717}"), test_name);
                            total_failed += 1;
                            for failure in &results.failures {
                                all_failures.push((path_str.clone(), test_name.clone(), failure.clone()));
                            }
                        }
                    }
                    Err(e) => {
                        println!("    {} {} {}", c(colors::RED, "\u{2717}"), test_name, c(colors::RED, &e.to_string()));
                        total_failed += 1;
                    }
                }
            }
        }
    }

    // Print failure details
    if !all_failures.is_empty() {
        println!("\n{}\n", bc(colors::RED, "--- Failures ---"));
        for (file, test, failure) in &all_failures {
            if test.is_empty() {
                println!("{}:", bc("", file));
            } else {
                println!("{} - {}:", bc("", file), bc("", test));
            }
            println!("  {}", failure.message);
            if let Some(expected) = &failure.expected {
                println!("  {} {}", c(colors::CYAN, "Expected:"), expected);
            }
            if let Some(actual) = &failure.actual {
                println!("  {} {}", c(colors::RED, "Actual:  "), actual);
            }
            println!();
        }
    }

    let elapsed = start.elapsed();
    let time_str = c(colors::DIM, &format!("in {:.2}s", elapsed.as_secs_f64()));

    if total_failed > 0 {
        println!("{} {} passed, {} failed {}",
            c(colors::RED, "\u{2717}"), total_passed, total_failed, time_str);
        std::process::exit(1);
    } else {
        println!("{} {} passed {}",
            c(colors::GREEN, "\u{2713}"), total_passed, time_str);
    }
}

fn collect_test_files(dir: &str, pattern: Option<&str>, files: &mut Vec<std::path::PathBuf>) {
    use std::fs;

    for entry in fs::read_dir(dir).into_iter().flatten().flatten() {
        let path = entry.path();
        if path.extension().map(|e| e == "lux").unwrap_or(false) {
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                if name.starts_with("test_") || name.ends_with("_test.lux") {
                    if pattern.map(|p| name.contains(p)).unwrap_or(true) {
                        files.push(path);
                    }
                }
            }
        }
    }
}

fn collect_lux_files(dir: &str, pattern: Option<&str>, files: &mut Vec<std::path::PathBuf>) {
    use std::fs;

    for entry in fs::read_dir(dir).into_iter().flatten().flatten() {
        let path = entry.path();
        if path.is_dir() {
            // Recursively search subdirectories
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                // Skip hidden directories and common non-source dirs
                if !name.starts_with('.') && name != "target" && name != "node_modules" {
                    collect_lux_files(path.to_str().unwrap_or(""), pattern, files);
                }
            }
        } else if path.extension().map(|e| e == "lux").unwrap_or(false) {
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                if pattern.map(|p| name.contains(p)).unwrap_or(true) {
                    files.push(path);
                }
            }
        }
    }
}

fn watch_file(path: &str) {
    use std::time::{Duration, SystemTime};
    use std::path::Path;

    let file_path = Path::new(path);
    if !file_path.exists() {
        eprintln!("File not found: {}", path);
        std::process::exit(1);
    }

    println!("Watching {} for changes (Ctrl+C to stop)...", path);
    println!();

    let mut last_modified = SystemTime::UNIX_EPOCH;

    loop {
        let metadata = match std::fs::metadata(file_path) {
            Ok(m) => m,
            Err(_) => {
                std::thread::sleep(Duration::from_millis(500));
                continue;
            }
        };

        let modified = metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH);

        if modified > last_modified {
            last_modified = modified;

            // Clear screen
            print!("\x1B[2J\x1B[H");

            println!("=== Running {} ===", path);
            println!();

            // Run the file
            let result = std::process::Command::new(std::env::current_exe().unwrap())
                .arg(path)
                .status();

            match result {
                Ok(status) if status.success() => {
                    println!();
                    println!("=== Success ===");
                }
                Ok(_) => {
                    println!();
                    println!("=== Failed ===");
                }
                Err(e) => {
                    eprintln!("Error running file: {}", e);
                }
            }

            println!();
            println!("Watching for changes...");
        }

        std::thread::sleep(Duration::from_millis(500));
    }
}

fn serve_static_files(dir: &str, port: u16) {
    use std::io::{Write, BufRead, BufReader};
    use std::net::TcpListener;
    use std::path::Path;

    let root = Path::new(dir).canonicalize().unwrap_or_else(|_| {
        eprintln!("{} Directory not found: {}", c(colors::RED, "error:"), dir);
        std::process::exit(1);
    });

    let listener = match TcpListener::bind(format!("0.0.0.0:{}", port)) {
        Ok(l) => l,
        Err(e) => {
            if e.kind() == std::io::ErrorKind::AddrInUse {
                eprintln!("{} Port {} is already in use", c(colors::RED, "error:"), port);
                // Try to find an available port
                for try_port in (port + 1)..=(port + 3) {
                    if TcpListener::bind(format!("0.0.0.0:{}", try_port)).is_ok() {
                        eprintln!("{}", c(colors::CYAN, &format!("  Try: lux serve --port {}", try_port)));
                        break;
                    }
                }
            } else {
                eprintln!("{} Could not bind to port {}: {}", c(colors::RED, "error:"), port, e);
            }
            std::process::exit(1);
        }
    };

    println!("{}", bc("", "Lux static file server"));
    println!("  {} {}", c(colors::DIM, "Serving:"), root.display());
    println!("  {} {}", c(colors::DIM, "URL:"), bc(colors::CYAN, &format!("http://localhost:{}", port)));
    println!();
    println!("{}", c(colors::DIM, "Press Ctrl+C to stop"));
    println!();

    for stream in listener.incoming() {
        let Ok(mut stream) = stream else { continue };

        // Read the request line
        let mut reader = BufReader::new(&stream);
        let mut request_line = String::new();
        if reader.read_line(&mut request_line).is_err() {
            continue;
        }

        // Parse the request
        let parts: Vec<&str> = request_line.split_whitespace().collect();
        if parts.len() < 2 {
            continue;
        }

        let method = parts[0];
        let path = parts[1];

        // Log the request (status is logged after response is determined)
        let log_method = bc("", method);
        let log_path = path.to_string();

        // Only handle GET requests
        if method != "GET" {
            let response = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 18\r\n\r\nMethod Not Allowed";
            let _ = stream.write_all(response.as_bytes());
            continue;
        }

        // Resolve the file path
        let mut file_path = root.clone();
        let request_path = if path == "/" { "/index.html" } else { path };

        // Prevent directory traversal
        for component in request_path.trim_start_matches('/').split('/') {
            if component == ".." {
                let response = "HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\n\r\nForbidden";
                let _ = stream.write_all(response.as_bytes());
                continue;
            }
            file_path.push(component);
        }

        // Try to serve the file
        let (status, content_type, body) = if file_path.is_file() {
            match std::fs::read(&file_path) {
                Ok(content) => {
                    let mime = get_mime_type(&file_path);
                    ("200 OK", mime, content)
                }
                Err(_) => {
                    ("500 Internal Server Error", "text/plain", b"Error reading file".to_vec())
                }
            }
        } else if file_path.is_dir() {
            // Try index.html
            let index_path = file_path.join("index.html");
            if index_path.is_file() {
                match std::fs::read(&index_path) {
                    Ok(content) => ("200 OK", "text/html", content),
                    Err(_) => ("500 Internal Server Error", "text/plain", b"Error reading file".to_vec()),
                }
            } else {
                ("404 Not Found", "text/plain", b"Not Found".to_vec())
            }
        } else {
            // Try with .html extension
            let html_path = file_path.with_extension("html");
            if html_path.is_file() {
                match std::fs::read(&html_path) {
                    Ok(content) => ("200 OK", "text/html", content),
                    Err(_) => ("500 Internal Server Error", "text/plain", b"Error reading file".to_vec()),
                }
            } else {
                ("404 Not Found", "text/plain", b"Not Found".to_vec())
            }
        };

        // Log the request with colored status
        let status_color = if status.starts_with("200") {
            colors::GREEN
        } else if status.starts_with("404") {
            colors::YELLOW
        } else {
            colors::RED
        };
        let status_code = status.split_whitespace().next().unwrap_or(status);
        println!("  {} {} {}", log_method, c(status_color, status_code), log_path);

        // Send response
        let response = format!(
            "HTTP/1.1 {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
            status, content_type, body.len()
        );
        let _ = stream.write_all(response.as_bytes());
        let _ = stream.write_all(&body);
    }
}

fn get_mime_type(path: &std::path::Path) -> &'static str {
    match path.extension().and_then(|e| e.to_str()) {
        Some("html") | Some("htm") => "text/html",
        Some("css") => "text/css",
        Some("js") => "application/javascript",
        Some("json") => "application/json",
        Some("png") => "image/png",
        Some("jpg") | Some("jpeg") => "image/jpeg",
        Some("gif") => "image/gif",
        Some("svg") => "image/svg+xml",
        Some("ico") => "image/x-icon",
        Some("woff") => "font/woff",
        Some("woff2") => "font/woff2",
        Some("ttf") => "font/ttf",
        Some("txt") => "text/plain",
        Some("xml") => "application/xml",
        Some("pdf") => "application/pdf",
        _ => "application/octet-stream",
    }
}

fn handle_pkg_command(args: &[String]) {
    use package::{PackageManager, DependencySource};
    use std::path::PathBuf;

    if args.is_empty() {
        print_pkg_help();
        return;
    }

    // Help doesn't require being in a project
    if matches!(args[0].as_str(), "help" | "--help" | "-h") {
        print_pkg_help();
        return;
    }

    // Init doesn't require being in a project (it creates one)
    if args[0] == "init" {
        init_pkg_here(args.get(1).map(|s| s.as_str()));
        return;
    }

    let project_root = match PackageManager::find_project_root() {
        Some(root) => root,
        None => {
            eprintln!("Error: Not in a Lux project (no lux.toml found)");
            eprintln!("Run 'lux pkg init' to initialize a project here, or 'lux init <name>' to create a new project");
            std::process::exit(1);
        }
    };

    let pkg = PackageManager::new(&project_root);

    match args[0].as_str() {
        "install" | "i" => {
            if let Err(e) = pkg.install() {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "add" => {
            if args.len() < 2 {
                eprintln!("Usage: lux pkg add <package> [version] [--git <url>] [--path <path>]");
                std::process::exit(1);
            }
            let name = &args[1];
            let mut version = "0.0.0".to_string();
            let mut source = DependencySource::Registry;

            let mut i = 2;
            while i < args.len() {
                match args[i].as_str() {
                    "--git" => {
                        if i + 1 < args.len() {
                            let url = args[i + 1].clone();
                            let branch = if i + 3 < args.len() && args[i + 2] == "--branch" {
                                i += 2;
                                Some(args[i + 1].clone())
                            } else {
                                None
                            };
                            source = DependencySource::Git { url, branch };
                            i += 2;
                        } else {
                            eprintln!("--git requires a URL");
                            std::process::exit(1);
                        }
                    }
                    "--path" => {
                        if i + 1 < args.len() {
                            source = DependencySource::Path {
                                path: PathBuf::from(&args[i + 1]),
                            };
                            i += 2;
                        } else {
                            eprintln!("--path requires a path");
                            std::process::exit(1);
                        }
                    }
                    v if !v.starts_with('-') => {
                        version = v.to_string();
                        i += 1;
                    }
                    _ => i += 1,
                }
            }

            if let Err(e) = pkg.add(name, &version, source) {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "remove" | "rm" => {
            if args.len() < 2 {
                eprintln!("Usage: lux pkg remove <package>");
                std::process::exit(1);
            }
            if let Err(e) = pkg.remove(&args[1]) {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "list" | "ls" => {
            if let Err(e) = pkg.list() {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "update" => {
            if let Err(e) = pkg.update() {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "clean" => {
            if let Err(e) = pkg.clean() {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        }
        "search" => {
            if args.len() < 2 {
                eprintln!("Usage: lux pkg search <query>");
                std::process::exit(1);
            }
            search_registry(&args[1]);
        }
        "publish" => {
            publish_package(&project_root);
        }
        "help" | "--help" | "-h" => {
            print_pkg_help();
        }
        unknown => {
            eprintln!("Unknown package command: {}", unknown);
            print_pkg_help();
            std::process::exit(1);
        }
    }
}

fn search_registry(query: &str) {
    let registry_url = std::env::var("LUX_REGISTRY_URL")
        .unwrap_or_else(|_| "https://pkgs.lux-lang.org".to_string());

    println!("Searching for '{}' in {}...", query, registry_url);

    // Make HTTP request to registry
    let url = format!("{}/api/v1/search?q={}", registry_url, query);

    // Use a simple HTTP client (could use reqwest in production)
    match simple_http_get(&url) {
        Ok(response) => {
            // Parse JSON response and display results
            if response.contains("\"packages\":[]") {
                println!("No packages found matching '{}'", query);
            } else {
                println!("\nFound packages:");
                // Simple parsing of package names from JSON
                for line in response.lines() {
                    if line.contains("\"name\":") {
                        if let Some(start) = line.find("\"name\":") {
                            let rest = &line[start + 8..];
                            if let Some(end) = rest.find('"') {
                                let name = &rest[..end];
                                println!("  {}", name);
                            }
                        }
                    }
                }
            }
        }
        Err(e) => {
            eprintln!("Failed to connect to registry: {}", e);
            eprintln!("Make sure the registry server is running or check LUX_REGISTRY_URL");
            std::process::exit(1);
        }
    }
}

fn publish_package(project_root: &std::path::Path) {
    use std::fs;

    let registry_url = std::env::var("LUX_REGISTRY_URL")
        .unwrap_or_else(|_| "https://pkgs.lux-lang.org".to_string());

    // Load manifest
    let manifest_path = project_root.join("lux.toml");
    if !manifest_path.exists() {
        eprintln!("No lux.toml found");
        std::process::exit(1);
    }

    let manifest_content = fs::read_to_string(&manifest_path).unwrap();

    // Extract project info
    let mut name = String::new();
    let mut version = String::new();

    for line in manifest_content.lines() {
        let line = line.trim();
        if line.starts_with("name") {
            if let Some(eq) = line.find('=') {
                name = line[eq+1..].trim().trim_matches('"').to_string();
            }
        } else if line.starts_with("version") {
            if let Some(eq) = line.find('=') {
                version = line[eq+1..].trim().trim_matches('"').to_string();
            }
        }
    }

    if name.is_empty() || version.is_empty() {
        eprintln!("Invalid lux.toml: missing name or version");
        std::process::exit(1);
    }

    println!("Publishing {} v{} to {}...", name, version, registry_url);

    // Create tarball of the package
    let tarball_name = format!("{}-{}.tar.gz", name, version);
    let tarball_path = project_root.join(&tarball_name);

    // Use tar command to create tarball
    let status = std::process::Command::new("tar")
        .arg("-czf")
        .arg(&tarball_path)
        .arg("--exclude=.lux_packages")
        .arg("--exclude=.git")
        .arg("--exclude=target")
        .arg("-C")
        .arg(project_root)
        .arg(".")
        .status();

    match status {
        Ok(s) if s.success() => {
            println!("Created package tarball: {}", tarball_name);
            println!();
            println!("To publish, upload the tarball to the registry:");
            println!("  curl -X POST {}/api/v1/publish -F 'package=@{}'",
                     registry_url, tarball_name);

            // Clean up tarball
            // fs::remove_file(&tarball_path).ok();
        }
        Ok(_) => {
            eprintln!("Failed to create package tarball");
            std::process::exit(1);
        }
        Err(e) => {
            eprintln!("Failed to run tar: {}", e);
            std::process::exit(1);
        }
    }
}

fn simple_http_get(url: &str) -> Result<String, String> {
    use std::io::{Read, Write};
    use std::net::TcpStream;

    // Parse URL
    let url = url.strip_prefix("http://").or_else(|| url.strip_prefix("https://")).unwrap_or(url);
    let (host_port, path) = if let Some(slash) = url.find('/') {
        (&url[..slash], &url[slash..])
    } else {
        (url, "/")
    };

    let (host, port) = if let Some(colon) = host_port.find(':') {
        (&host_port[..colon], host_port[colon+1..].parse::<u16>().unwrap_or(80))
    } else {
        (host_port, 80)
    };

    let mut stream = TcpStream::connect((host, port))
        .map_err(|e| format!("Connection failed: {}", e))?;

    let request = format!(
        "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
        path, host
    );

    stream.write_all(request.as_bytes())
        .map_err(|e| format!("Write failed: {}", e))?;

    let mut response = String::new();
    stream.read_to_string(&mut response)
        .map_err(|e| format!("Read failed: {}", e))?;

    // Extract body (after \r\n\r\n)
    if let Some(body_start) = response.find("\r\n\r\n") {
        Ok(response[body_start + 4..].to_string())
    } else {
        Ok(response)
    }
}

fn print_pkg_help() {
    println!("Lux Package Manager");
    println!();
    println!("Usage: lux pkg <command> [options]");
    println!();
    println!("Commands:");
    println!("  init [name]    Initialize a lux.toml in the current directory");
    println!("  install, i     Install all dependencies from lux.toml");
    println!("  add <pkg>      Add a dependency");
    println!("                   Options:");
    println!("                     [version]         Specify version (default: 0.0.0)");
    println!("                     --git <url>       Install from git repository");
    println!("                     --branch <name>   Git branch (with --git)");
    println!("                     --path <path>     Install from local path");
    println!("  remove, rm     Remove a dependency");
    println!("  list, ls       List dependencies and their status");
    println!("  update         Update all dependencies");
    println!("  clean          Remove installed packages");
    println!("  search <query> Search for packages in the registry");
    println!("  publish        Publish package to the registry");
    println!();
    println!("Registry Configuration:");
    println!("  Set LUX_REGISTRY_URL to use a custom registry (default: https://pkgs.lux-lang.org)");
    println!();
    println!("Examples:");
    println!("  lux pkg init");
    println!("  lux pkg init my-project");
    println!("  lux pkg install");
    println!("  lux pkg add http 1.0.0");
    println!("  lux pkg add mylib --git https://github.com/user/mylib");
    println!("  lux pkg add local-lib --path ../lib");
    println!("  lux pkg remove http");
    println!("  lux pkg search json");
    println!("  lux pkg publish");
}

fn init_pkg_here(name: Option<&str>) {
    use std::fs;
    use std::path::Path;

    let lux_toml = Path::new("lux.toml");

    if lux_toml.exists() {
        eprintln!("lux.toml already exists in this directory");
        std::process::exit(1);
    }

    // Get project name from argument or current directory name
    let project_name = name.map(String::from).unwrap_or_else(|| {
        std::env::current_dir()
            .ok()
            .and_then(|p| p.file_name().map(|s| s.to_string_lossy().to_string()))
            .unwrap_or_else(|| "my-project".to_string())
    });

    // Create lux.toml
    let toml_content = format!(
        r#"[project]
name = "{}"
version = "0.1.0"
description = "A Lux project"

[dependencies]
# Add dependencies here
# example = "1.0.0"
# mylib = {{ git = "https://github.com/user/mylib" }}
# local = {{ path = "../local-lib" }}
"#,
        project_name
    );

    if let Err(e) = fs::write(lux_toml, toml_content) {
        eprintln!("Failed to create lux.toml: {}", e);
        std::process::exit(1);
    }

    println!("Initialized Lux project: {}", project_name);
    println!();
    println!("Created lux.toml");
    println!();
    println!("Next steps:");
    println!("  lux pkg add <package>   Add a dependency");
    println!("  lux pkg install         Install dependencies");
}

fn init_project(name: Option<&str>) {
    use std::fs;
    use std::path::Path;

    let project_name = name.unwrap_or("my-lux-project");
    let project_dir = Path::new(project_name);

    if project_dir.exists() {
        eprintln!("Directory '{}' already exists", project_name);
        std::process::exit(1);
    }

    // Create project structure
    fs::create_dir_all(project_dir.join("src")).unwrap();
    fs::create_dir_all(project_dir.join("tests")).unwrap();

    // Create lux.toml
    let toml_content = format!(
        r#"[project]
name = "{}"
version = "0.1.0"
description = "A Lux project"

[dependencies]
# Add dependencies here
# example = "1.0.0"
"#,
        project_name
    );
    fs::write(project_dir.join("lux.toml"), toml_content).unwrap();

    // Create main.lux
    let main_content = r#"// Main entry point

fn main(): Unit with {Console} = {
    Console.print("Hello from Lux!")
}

let output = run main() with {}
"#;
    fs::write(project_dir.join("src").join("main.lux"), main_content).unwrap();

    // Create a test file
    let test_content = r#"// Example test file

fn testAddition(): Bool = {
    let result = 2 + 2
    result == 4
}

fn main(): Unit with {Console} = {
    if testAddition() then
        Console.print("Test passed!")
    else
        Console.print("Test failed!")
}

let output = run main() with {}
"#;
    fs::write(project_dir.join("tests").join("test_example.lux"), test_content).unwrap();

    // Create .gitignore
    let gitignore_content = r#"# Lux build artifacts
/target/
*.luxc

# Editor files
.vscode/
.idea/
*.swp
*~
"#;
    fs::write(project_dir.join(".gitignore"), gitignore_content).unwrap();

    println!("{} Created new Lux project: {}", c(colors::GREEN, "\u{2713}"), bc(colors::GREEN, project_name));
    println!();
    println!("{}", bc("", "Project structure:"));
    println!("  {}", bc(colors::CYAN, &format!("{}/", project_name)));
    println!("  {} {}", c(colors::DIM, "\u{251c}\u{2500}\u{2500}"), "lux.toml");
    println!("  {} {}", c(colors::DIM, "\u{251c}\u{2500}\u{2500}"), bc(colors::CYAN, "src/"));
    println!("  {} {} {}", c(colors::DIM, "\u{2502}"), c(colors::DIM, "  \u{2514}\u{2500}\u{2500}"), "main.lux");
    println!("  {} {}", c(colors::DIM, "\u{2514}\u{2500}\u{2500}"), bc(colors::CYAN, "tests/"));
    println!("      {} {}", c(colors::DIM, "\u{2514}\u{2500}\u{2500}"), "test_example.lux");
    println!();
    println!("To get started:");
    println!("  {} {}", c(colors::DIM, "$"), bc(colors::CYAN, &format!("cd {}", project_name)));
    println!("  {} {}", c(colors::DIM, "$"), bc(colors::CYAN, "lux src/main.lux"));
}

/// Generate API documentation for Lux source files
fn generate_docs(args: &[String]) {
    use std::path::Path;
    use std::collections::HashMap;

    let output_json = args.iter().any(|a| a == "--json");
    let output_dir = args.iter()
        .position(|a| a == "-o")
        .and_then(|i| args.get(i + 1))
        .map(|s| s.as_str())
        .unwrap_or("docs");
    let input_file = args.iter().find(|a| !a.starts_with('-') && *a != output_dir);

    // Collect files to document
    let mut files_to_doc = Vec::new();

    if let Some(path) = input_file {
        if Path::new(path).is_file() {
            files_to_doc.push(path.to_string());
        } else {
            eprintln!("File not found: {}", path);
            std::process::exit(1);
        }
    } else {
        // Auto-discover files
        if Path::new("src").is_dir() {
            collect_lux_files_for_docs("src", &mut files_to_doc);
        }
        if Path::new("stdlib").is_dir() {
            collect_lux_files_for_docs("stdlib", &mut files_to_doc);
        }
    }

    if files_to_doc.is_empty() {
        eprintln!("No .lux files found to document");
        std::process::exit(1);
    }

    // Create output directory
    if !output_json {
        if let Err(e) = std::fs::create_dir_all(output_dir) {
            eprintln!("Failed to create output directory: {}", e);
            std::process::exit(1);
        }
    }

    let mut all_docs: HashMap<String, ModuleDoc> = HashMap::new();
    let mut error_count = 0;

    for file_path in &files_to_doc {
        let source = match std::fs::read_to_string(file_path) {
            Ok(s) => s,
            Err(e) => {
                eprintln!("{}: ERROR - {}", file_path, e);
                error_count += 1;
                continue;
            }
        };

        match extract_module_doc(&source, file_path) {
            Ok(doc) => {
                let module_name = Path::new(file_path)
                    .file_stem()
                    .map(|s| s.to_string_lossy().to_string())
                    .unwrap_or_else(|| "unknown".to_string());
                all_docs.insert(module_name, doc);
            }
            Err(e) => {
                eprintln!("{}: PARSE ERROR - {}", file_path, e);
                error_count += 1;
            }
        }
    }

    if output_json {
        // Output as JSON
        println!("{}", docs_to_json(&all_docs));
    } else {
        // Generate HTML files
        let index_html = generate_index_html(&all_docs);
        let index_path = format!("{}/index.html", output_dir);
        if let Err(e) = std::fs::write(&index_path, &index_html) {
            eprintln!("Failed to write index.html: {}", e);
            error_count += 1;
        }

        for (module_name, doc) in &all_docs {
            let html = generate_module_html(module_name, doc);
            let path = format!("{}/{}.html", output_dir, module_name);
            if let Err(e) = std::fs::write(&path, &html) {
                eprintln!("Failed to write {}: {}", path, e);
                error_count += 1;
            }
        }

        // Generate CSS
        let css_path = format!("{}/style.css", output_dir);
        if let Err(e) = std::fs::write(&css_path, DOC_CSS) {
            eprintln!("Failed to write style.css: {}", e);
            error_count += 1;
        }

        println!("Generated documentation in {}/", output_dir);
        println!("  {} modules documented", all_docs.len());
        if error_count > 0 {
            println!("  {} errors", error_count);
        }
    }
}

fn collect_lux_files_for_docs(dir: &str, files: &mut Vec<String>) {
    if let Ok(entries) = std::fs::read_dir(dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            if path.is_file() && path.extension().map(|e| e == "lux").unwrap_or(false) {
                files.push(path.to_string_lossy().to_string());
            } else if path.is_dir() {
                collect_lux_files_for_docs(&path.to_string_lossy(), files);
            }
        }
    }
}

#[derive(Debug, Clone)]
struct ModuleDoc {
    description: Option<String>,
    functions: Vec<FunctionDoc>,
    types: Vec<TypeDoc>,
    effects: Vec<EffectDoc>,
}

#[derive(Debug, Clone)]
struct FunctionDoc {
    name: String,
    signature: String,
    description: Option<String>,
    is_public: bool,
    properties: Vec<String>,
}

#[derive(Debug, Clone)]
struct TypeDoc {
    name: String,
    definition: String,
    description: Option<String>,
    is_public: bool,
}

#[derive(Debug, Clone)]
struct EffectDoc {
    name: String,
    operations: Vec<String>,
    description: Option<String>,
}

fn extract_module_doc(source: &str, path: &str) -> Result<ModuleDoc, String> {
    use modules::ModuleLoader;
    use std::path::Path;

    let mut loader = ModuleLoader::new();
    let file_path = Path::new(path);
    if let Some(parent) = file_path.parent() {
        loader.add_search_path(parent.to_path_buf());
    }

    let program = loader.load_source(source, Some(file_path))
        .map_err(|e| format!("{}", e))?;

    let mut module_desc: Option<String> = None;
    let mut functions = Vec::new();
    let mut types = Vec::new();
    let mut effects = Vec::new();
    let mut pending_doc: Option<String> = None;

    // Extract module-level comment (first comment before any declarations)
    let lines: Vec<&str> = source.lines().collect();
    let mut module_comment = Vec::new();
    for line in &lines {
        let trimmed = line.trim();
        if trimmed.starts_with("//") {
            module_comment.push(trimmed.trim_start_matches('/').trim());
        } else if !trimmed.is_empty() {
            break;
        }
    }
    if !module_comment.is_empty() {
        module_desc = Some(module_comment.join("\n"));
    }

    for decl in &program.declarations {
        match decl {
            ast::Declaration::Function(f) => {
                // Build signature
                let params: Vec<String> = f.params.iter()
                    .map(|p| format!("{}: {}", p.name.name, format_type(&p.typ)))
                    .collect();
                let effects_str = if f.effects.is_empty() {
                    String::new()
                } else {
                    format!(" with {{{}}}", f.effects.iter().map(|e| e.name.clone()).collect::<Vec<_>>().join(", "))
                };
                let props: Vec<String> = f.properties.iter()
                    .map(|p| format!("{:?}", p).to_lowercase())
                    .collect();
                let props_str = if props.is_empty() {
                    String::new()
                } else {
                    format!(" is {}", props.join(", "))
                };

                let signature = format!(
                    "fn {}({}): {}{}{}",
                    f.name.name,
                    params.join(", "),
                    format_type(&f.return_type),
                    props_str,
                    effects_str
                );

                // Extract doc comment
                let doc = extract_doc_comment(source, f.span.start);

                functions.push(FunctionDoc {
                    name: f.name.name.clone(),
                    signature,
                    description: doc,
                    is_public: matches!(f.visibility, ast::Visibility::Public),
                    properties: props,
                });
            }
            ast::Declaration::Type(t) => {
                let doc = extract_doc_comment(source, t.span.start);
                types.push(TypeDoc {
                    name: t.name.name.clone(),
                    definition: format_type_def(t),
                    description: doc,
                    is_public: matches!(t.visibility, ast::Visibility::Public),
                });
            }
            ast::Declaration::Effect(e) => {
                let doc = extract_doc_comment(source, e.span.start);
                let ops: Vec<String> = e.operations.iter()
                    .map(|op| {
                        let params: Vec<String> = op.params.iter()
                            .map(|p| format!("{}: {}", p.name.name, format_type(&p.typ)))
                            .collect();
                        format!("{}({}): {}", op.name.name, params.join(", "), format_type(&op.return_type))
                    })
                    .collect();
                effects.push(EffectDoc {
                    name: e.name.name.clone(),
                    operations: ops,
                    description: doc,
                });
            }
            _ => {}
        }
    }

    Ok(ModuleDoc {
        description: module_desc,
        functions,
        types,
        effects,
    })
}

fn extract_doc_comment(source: &str, pos: usize) -> Option<String> {
    // Look backwards from the declaration for doc comments
    let prefix = &source[..pos];
    let lines: Vec<&str> = prefix.lines().collect();

    let mut doc_lines = Vec::new();
    for line in lines.iter().rev() {
        let trimmed = line.trim();
        if trimmed.starts_with("///") {
            doc_lines.push(trimmed.trim_start_matches('/').trim());
        } else if trimmed.starts_with("//") {
            // Regular comment, skip
            continue;
        } else if trimmed.is_empty() {
            if !doc_lines.is_empty() {
                break;
            }
        } else {
            break;
        }
    }

    if doc_lines.is_empty() {
        None
    } else {
        doc_lines.reverse();
        Some(doc_lines.join("\n"))
    }
}

fn format_type(t: &ast::TypeExpr) -> String {
    match t {
        ast::TypeExpr::Named(ident) => ident.name.clone(),
        ast::TypeExpr::App(base, args) => {
            let args_str: Vec<String> = args.iter().map(format_type).collect();
            format!("{}<{}>", format_type(base), args_str.join(", "))
        }
        ast::TypeExpr::Function { params, return_type, .. } => {
            let params_str: Vec<String> = params.iter().map(format_type).collect();
            format!("fn({}): {}", params_str.join(", "), format_type(return_type))
        }
        ast::TypeExpr::Tuple(types) => {
            let types_str: Vec<String> = types.iter().map(format_type).collect();
            format!("({})", types_str.join(", "))
        }
        ast::TypeExpr::Record(fields) => {
            let fields_str: Vec<String> = fields.iter()
                .map(|f| format!("{}: {}", f.name.name, format_type(&f.typ)))
                .collect();
            format!("{{ {} }}", fields_str.join(", "))
        }
        ast::TypeExpr::Unit => "Unit".to_string(),
        ast::TypeExpr::Versioned { base, .. } => format_type(base),
    }
}

fn format_type_def(t: &ast::TypeDecl) -> String {
    match &t.definition {
        ast::TypeDef::Alias(typ) => format!("type {} = {}", t.name.name, format_type(typ)),
        ast::TypeDef::Enum(variants) => {
            let variants_str: Vec<String> = variants.iter()
                .map(|v| {
                    match &v.fields {
                        ast::VariantFields::Unit => v.name.name.clone(),
                        ast::VariantFields::Tuple(types) => {
                            let types_str: Vec<String> = types.iter().map(format_type).collect();
                            format!("{}({})", v.name.name, types_str.join(", "))
                        }
                        ast::VariantFields::Record(fields) => {
                            let fields_str: Vec<String> = fields.iter()
                                .map(|f| format!("{}: {}", f.name.name, format_type(&f.typ)))
                                .collect();
                            format!("{}{{ {} }}", v.name.name, fields_str.join(", "))
                        }
                    }
                })
                .collect();
            format!("type {} = {}", t.name.name, variants_str.join(" | "))
        }
        ast::TypeDef::Record(fields) => {
            let fields_str: Vec<String> = fields.iter()
                .map(|f| format!("{}: {}", f.name.name, format_type(&f.typ)))
                .collect();
            format!("type {} = {{ {} }}", t.name.name, fields_str.join(", "))
        }
    }
}

fn docs_to_json(docs: &std::collections::HashMap<String, ModuleDoc>) -> String {
    let mut json = String::from("{\n");
    let mut first_module = true;

    for (name, doc) in docs {
        if !first_module {
            json.push_str(",\n");
        }
        first_module = false;

        json.push_str(&format!("  \"{}\": {{\n", escape_json(name)));

        if let Some(desc) = &doc.description {
            json.push_str(&format!("    \"description\": \"{}\",\n", escape_json(desc)));
        }

        // Functions
        json.push_str("    \"functions\": [\n");
        for (i, f) in doc.functions.iter().enumerate() {
            json.push_str(&format!(
                "      {{\"name\": \"{}\", \"signature\": \"{}\", \"public\": {}, \"description\": {}}}",
                escape_json(&f.name),
                escape_json(&f.signature),
                f.is_public,
                f.description.as_ref().map(|d| format!("\"{}\"", escape_json(d))).unwrap_or("null".to_string())
            ));
            if i < doc.functions.len() - 1 {
                json.push(',');
            }
            json.push('\n');
        }
        json.push_str("    ],\n");

        // Types
        json.push_str("    \"types\": [\n");
        for (i, t) in doc.types.iter().enumerate() {
            json.push_str(&format!(
                "      {{\"name\": \"{}\", \"definition\": \"{}\", \"public\": {}, \"description\": {}}}",
                escape_json(&t.name),
                escape_json(&t.definition),
                t.is_public,
                t.description.as_ref().map(|d| format!("\"{}\"", escape_json(d))).unwrap_or("null".to_string())
            ));
            if i < doc.types.len() - 1 {
                json.push(',');
            }
            json.push('\n');
        }
        json.push_str("    ],\n");

        // Effects
        json.push_str("    \"effects\": [\n");
        for (i, e) in doc.effects.iter().enumerate() {
            let ops_json: Vec<String> = e.operations.iter()
                .map(|o| format!("\"{}\"", escape_json(o)))
                .collect();
            json.push_str(&format!(
                "      {{\"name\": \"{}\", \"operations\": [{}], \"description\": {}}}",
                escape_json(&e.name),
                ops_json.join(", "),
                e.description.as_ref().map(|d| format!("\"{}\"", escape_json(d))).unwrap_or("null".to_string())
            ));
            if i < doc.effects.len() - 1 {
                json.push(',');
            }
            json.push('\n');
        }
        json.push_str("    ]\n");

        json.push_str("  }");
    }

    json.push_str("\n}");
    json
}

fn escape_json(s: &str) -> String {
    s.replace('\\', "\\\\")
        .replace('"', "\\\"")
        .replace('\n', "\\n")
        .replace('\r', "\\r")
        .replace('\t', "\\t")
}

fn generate_index_html(docs: &std::collections::HashMap<String, ModuleDoc>) -> String {
    let mut html = String::from(r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lux API Documentation</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Lux API Documentation</h1>
    </header>
    <main>
        <h2>Modules</h2>
        <ul class="module-list">
"#);

    let mut modules: Vec<_> = docs.keys().collect();
    modules.sort();

    for name in modules {
        let doc = &docs[name];
        let desc = doc.description.as_ref()
            .map(|d| d.lines().next().unwrap_or(""))
            .unwrap_or("");
        html.push_str(&format!(
            "            <li><a href=\"{}.html\">{}</a> - {}</li>\n",
            name, name, html_escape(desc)
        ));
    }

    html.push_str(r#"        </ul>
    </main>
</body>
</html>"#);

    html
}

fn generate_module_html(name: &str, doc: &ModuleDoc) -> String {
    let mut html = format!(r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{} - Lux API</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <a href="index.html">Back to Index</a>
        <h1>{}</h1>
    </header>
    <main>
"#, name, name);

    if let Some(desc) = &doc.description {
        html.push_str(&format!("        <div class=\"module-description\">{}</div>\n", html_escape(desc)));
    }

    // Types
    if !doc.types.is_empty() {
        html.push_str("        <section>\n            <h2>Types</h2>\n");
        for t in &doc.types {
            let visibility = if t.is_public { "pub " } else { "" };
            html.push_str(&format!(
                "            <div class=\"item\">\n                <code class=\"signature\">{}{}</code>\n",
                visibility, html_escape(&t.definition)
            ));
            if let Some(desc) = &t.description {
                html.push_str(&format!("                <p class=\"description\">{}</p>\n", html_escape(desc)));
            }
            html.push_str("            </div>\n");
        }
        html.push_str("        </section>\n");
    }

    // Effects
    if !doc.effects.is_empty() {
        html.push_str("        <section>\n            <h2>Effects</h2>\n");
        for e in &doc.effects {
            html.push_str(&format!(
                "            <div class=\"item\">\n                <h3>effect {}</h3>\n",
                html_escape(&e.name)
            ));
            if let Some(desc) = &e.description {
                html.push_str(&format!("                <p class=\"description\">{}</p>\n", html_escape(desc)));
            }
            html.push_str("                <ul class=\"operations\">\n");
            for op in &e.operations {
                html.push_str(&format!("                    <li><code>{}</code></li>\n", html_escape(op)));
            }
            html.push_str("                </ul>\n            </div>\n");
        }
        html.push_str("        </section>\n");
    }

    // Functions
    if !doc.functions.is_empty() {
        html.push_str("        <section>\n            <h2>Functions</h2>\n");
        for f in &doc.functions {
            let visibility = if f.is_public { "pub " } else { "" };
            html.push_str(&format!(
                "            <div class=\"item\" id=\"{}\">\n                <code class=\"signature\">{}{}</code>\n",
                f.name, visibility, html_escape(&f.signature)
            ));
            if let Some(desc) = &f.description {
                html.push_str(&format!("                <p class=\"description\">{}</p>\n", html_escape(desc)));
            }
            html.push_str("            </div>\n");
        }
        html.push_str("        </section>\n");
    }

    html.push_str(r#"    </main>
</body>
</html>"#);

    html
}

fn html_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
}

const DOC_CSS: &str = r#"
:root {
    --bg-color: #1a1a2e;
    --text-color: #e0e0e0;
    --link-color: #64b5f6;
    --code-bg: #16213e;
    --header-bg: #0f3460;
    --accent: #e94560;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-color);
    margin: 0;
    padding: 0;
    line-height: 1.6;
}

header {
    background-color: var(--header-bg);
    padding: 1rem 2rem;
    border-bottom: 2px solid var(--accent);
}

header h1 {
    margin: 0;
    color: white;
}

header a {
    color: var(--link-color);
    text-decoration: none;
    font-size: 0.9rem;
}

main {
    max-width: 900px;
    margin: 0 auto;
    padding: 2rem;
}

h2 {
    color: var(--accent);
    border-bottom: 1px solid var(--accent);
    padding-bottom: 0.5rem;
}

h3 {
    color: var(--link-color);
}

.module-list {
    list-style: none;
    padding: 0;
}

.module-list li {
    padding: 0.5rem 0;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}

.module-list a {
    color: var(--link-color);
    text-decoration: none;
    font-weight: bold;
}

.item {
    background-color: var(--code-bg);
    border-radius: 8px;
    padding: 1rem;
    margin: 1rem 0;
}

.signature {
    display: block;
    background-color: rgba(0,0,0,0.3);
    padding: 0.5rem 1rem;
    border-radius: 4px;
    font-family: 'Fira Code', 'Monaco', monospace;
    overflow-x: auto;
}

.description {
    margin-top: 0.5rem;
    color: #aaa;
}

.operations {
    list-style: none;
    padding-left: 1rem;
}

.operations li {
    padding: 0.25rem 0;
}

.module-description {
    background-color: var(--code-bg);
    padding: 1rem;
    border-radius: 8px;
    margin-bottom: 2rem;
    border-left: 3px solid var(--accent);
}
"#;

fn run_file(path: &str) {
    use modules::ModuleLoader;
    use std::path::Path;

    let file_path = Path::new(path);
    let source = match std::fs::read_to_string(file_path) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Error reading file '{}': {}", path, e);
            std::process::exit(1);
        }
    };

    // Set up module loader with the file's directory as a search path
    let mut loader = ModuleLoader::new();
    if let Some(parent) = file_path.parent() {
        loader.add_search_path(parent.to_path_buf());
    }

    // Load and parse the program (including any imports)
    let program = match loader.load_source(&source, Some(file_path)) {
        Ok(p) => p,
        Err(e) => {
            eprintln!("Module error: {}", e);
            std::process::exit(1);
        }
    };

    let mut checker = TypeChecker::new();
    if let Err(errors) = checker.check_program_with_modules(&program, &loader) {
        for error in errors {
            let diagnostic = error.to_diagnostic();
            eprint!("{}", render(&diagnostic, &source, Some(path)));
        }
        std::process::exit(1);
    }

    let mut interp = Interpreter::new();
    match interp.run_with_modules(&program, &loader) {
        Ok(value) => {
            if !matches!(value, interpreter::Value::Unit) {
                println!("{}", value);
            }
        }
        Err(e) => {
            let diagnostic = e.to_diagnostic();
            eprint!("{}", render(&diagnostic, &source, Some(path)));
            std::process::exit(1);
        }
    }
}

/// REPL helper for tab completion and syntax highlighting
struct LuxHelper {
    keywords: HashSet<String>,
    commands: Vec<String>,
    user_defined: HashSet<String>,
    last_loaded_file: Option<String>,
}

impl LuxHelper {
    fn new() -> Self {
        let keywords: HashSet<String> = vec![
            "fn", "let", "if", "then", "else", "match", "with", "effect", "handler",
            "handle", "resume", "type", "import", "pub", "is", "pure", "total",
            "idempotent", "deterministic", "commutative", "where", "assume", "true",
            "false", "None", "Some", "Ok", "Err", "Int", "Float", "String", "Bool",
            "Char", "Unit", "Option", "Result", "List",
        ]
        .into_iter()
        .map(String::from)
        .collect();

        let commands = vec![
            ":help", ":h", ":quit", ":q", ":type", ":t", ":clear", ":load", ":l",
            ":reload", ":r", ":trace", ":traces", ":info", ":i", ":env", ":doc", ":d",
            ":browse", ":b", ":ast",
        ]
        .into_iter()
        .map(String::from)
        .collect();

        Self {
            keywords,
            commands,
            user_defined: HashSet::new(),
            last_loaded_file: None,
        }
    }

    fn add_definition(&mut self, name: &str) {
        self.user_defined.insert(name.to_string());
    }
}

impl Completer for LuxHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context<'_>,
    ) -> rustyline::Result<(usize, Vec<Pair>)> {
        // Find the start of the current word
        let start = line[..pos]
            .rfind(|c: char| c.is_whitespace() || "(){}[],.;:".contains(c))
            .map(|i| i + 1)
            .unwrap_or(0);

        let prefix = &line[start..pos];

        if prefix.is_empty() {
            return Ok((pos, Vec::new()));
        }

        let mut completions = Vec::new();

        // Complete commands
        if prefix.starts_with(':') {
            for cmd in &self.commands {
                if cmd.starts_with(prefix) {
                    completions.push(Pair {
                        display: cmd.clone(),
                        replacement: cmd.clone(),
                    });
                }
            }
        } else {
            // Complete keywords
            for kw in &self.keywords {
                if kw.starts_with(prefix) {
                    completions.push(Pair {
                        display: kw.clone(),
                        replacement: kw.clone(),
                    });
                }
            }
            // Complete user-defined names
            for name in &self.user_defined {
                if name.starts_with(prefix) {
                    completions.push(Pair {
                        display: name.clone(),
                        replacement: name.clone(),
                    });
                }
            }
        }

        Ok((start, completions))
    }
}

impl Hinter for LuxHelper {
    type Hint = String;

    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
        None
    }
}

impl Highlighter for LuxHelper {
    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Cow::Owned(format!("\x1b[90m{}\x1b[0m", hint))
    }

    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.char_indices().peekable();

        while let Some((i, c)) = chars.next() {
            if c == '"' {
                // String literal - highlight in green
                result.push_str("\x1b[32m\"");
                let mut escaped = false;
                for (_, ch) in chars.by_ref() {
                    result.push(ch);
                    if escaped {
                        escaped = false;
                    } else if ch == '\\' {
                        escaped = true;
                    } else if ch == '"' {
                        break;
                    }
                }
                result.push_str("\x1b[0m");
            } else if c == ':' && i == 0 {
                // Command - highlight in cyan
                result.push_str("\x1b[36m:");
                for (_, ch) in chars.by_ref() {
                    if ch.is_whitespace() {
                        result.push_str("\x1b[0m");
                        result.push(ch);
                        break;
                    }
                    result.push(ch);
                }
                // Continue with the rest
                for (_, ch) in chars.by_ref() {
                    result.push(ch);
                }
            } else if c.is_ascii_digit() || (c == '-' && chars.peek().map(|(_, ch)| ch.is_ascii_digit()).unwrap_or(false)) {
                // Number - highlight in yellow
                result.push_str("\x1b[33m");
                result.push(c);
                while let Some(&(_, ch)) = chars.peek() {
                    if ch.is_ascii_digit() || ch == '.' {
                        result.push(ch);
                        chars.next();
                    } else {
                        break;
                    }
                }
                result.push_str("\x1b[0m");
            } else if c.is_alphabetic() || c == '_' {
                // Identifier or keyword
                let start = i;
                let mut end = i + c.len_utf8();
                while let Some(&(j, ch)) = chars.peek() {
                    if ch.is_alphanumeric() || ch == '_' {
                        end = j + ch.len_utf8();
                        chars.next();
                    } else {
                        break;
                    }
                }
                let word = &line[start..end];

                // Check if it's a keyword
                if self.keywords.contains(word) {
                    result.push_str("\x1b[35m"); // Magenta for keywords
                    result.push_str(word);
                    result.push_str("\x1b[0m");
                } else if word.starts_with(char::is_uppercase) {
                    result.push_str("\x1b[34m"); // Blue for types/constructors
                    result.push_str(word);
                    result.push_str("\x1b[0m");
                } else {
                    result.push_str(word);
                }
            } else if c == '/' && chars.peek().map(|(_, ch)| *ch == '/').unwrap_or(false) {
                // Comment - highlight in gray
                result.push_str("\x1b[90m");
                result.push(c);
                for (_, ch) in chars.by_ref() {
                    result.push(ch);
                }
                result.push_str("\x1b[0m");
            } else {
                result.push(c);
            }
        }

        Cow::Owned(result)
    }

    fn highlight_char(&self, _line: &str, _pos: usize, _forced: bool) -> bool {
        true
    }
}

impl Validator for LuxHelper {}
impl Helper for LuxHelper {}

fn get_history_path() -> Option<std::path::PathBuf> {
    std::env::var_os("HOME").map(|home| {
        std::path::PathBuf::from(home).join(".lux_history")
    })
}

fn run_repl() {
    println!("Lux v{}", VERSION);
    println!("Type :help for help, :quit to exit\n");

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

    let helper = LuxHelper::new();
    let mut rl: Editor<LuxHelper, DefaultHistory> = Editor::with_config(config).unwrap();
    rl.set_helper(Some(helper));

    // Load history
    if let Some(history_path) = get_history_path() {
        if let Some(parent) = history_path.parent() {
            let _ = std::fs::create_dir_all(parent);
        }
        let _ = rl.load_history(&history_path);
    }

    let mut interp = Interpreter::new();
    let mut checker = TypeChecker::new();
    let mut buffer = String::new();
    let mut continuation = false;

    loop {
        let prompt = if continuation { "... " } else { "lux> " };

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

                // Don't add empty lines or continuations to history
                if !line.is_empty() && !continuation {
                    let _ = rl.add_history_entry(line.as_str());
                }

                // Handle commands
                if !continuation && line.starts_with(':') {
                    handle_command(&line, &mut interp, &mut checker, rl.helper_mut().unwrap());
                    continue;
                }

                // Accumulate input
                buffer.push_str(&line);
                buffer.push('\n');

                // Check for continuation (unbalanced braces/parens)
                let open_braces = buffer.chars().filter(|c| *c == '{').count();
                let close_braces = buffer.chars().filter(|c| *c == '}').count();
                let open_parens = buffer.chars().filter(|c| *c == '(').count();
                let close_parens = buffer.chars().filter(|c| *c == ')').count();

                if open_braces > close_braces || open_parens > close_parens {
                    continuation = true;
                    continue;
                }

                continuation = false;
                let input = std::mem::take(&mut buffer);

                if input.trim().is_empty() {
                    continue;
                }

                // Track new definitions for completion
                if let Some(name) = extract_definition_name(&input) {
                    rl.helper_mut().unwrap().add_definition(&name);
                }

                eval_input(&input, &mut interp, &mut checker);
            }
            Err(ReadlineError::Interrupted) => {
                // Ctrl-C: clear buffer
                buffer.clear();
                continuation = false;
                println!("^C");
            }
            Err(ReadlineError::Eof) => {
                // Ctrl-D: exit
                break;
            }
            Err(err) => {
                eprintln!("Error: {:?}", err);
                break;
            }
        }
    }

    // Save history
    if let Some(history_path) = get_history_path() {
        let _ = rl.save_history(&history_path);
    }

    println!("\nGoodbye!");
}

fn extract_definition_name(input: &str) -> Option<String> {
    let input = input.trim();
    if input.starts_with("fn ") {
        input.split_whitespace().nth(1).and_then(|s| {
            s.split('(').next().map(String::from)
        })
    } else if input.starts_with("let ") {
        input.split_whitespace().nth(1).and_then(|s| {
            s.split([':', '=']).next().map(|s| s.trim().to_string())
        })
    } else if input.starts_with("type ") {
        input.split_whitespace().nth(1).and_then(|s| {
            s.split(['<', '=']).next().map(|s| s.trim().to_string())
        })
    } else if input.starts_with("effect ") {
        input.split_whitespace().nth(1).map(|s| {
            s.trim_end_matches('{').trim().to_string()
        })
    } else {
        None
    }
}

fn handle_command(
    line: &str,
    interp: &mut Interpreter,
    checker: &mut TypeChecker,
    helper: &mut LuxHelper,
) {
    let parts: Vec<&str> = line.splitn(2, ' ').collect();
    let cmd = parts[0];
    let arg = parts.get(1).map(|s| s.trim());

    match cmd {
        ":help" | ":h" => {
            println!("{}", HELP);
        }
        ":quit" | ":q" => {
            println!("Goodbye!");
            std::process::exit(0);
        }
        ":type" | ":t" => {
            if let Some(expr_str) = arg {
                show_type(expr_str, checker);
            } else {
                println!("Usage: :type <expression>");
            }
        }
        ":info" | ":i" => {
            if let Some(name) = arg {
                show_info(name, checker);
            } else {
                println!("Usage: :info <name>");
            }
        }
        ":env" => {
            show_environment(checker, helper);
        }
        ":clear" => {
            *interp = Interpreter::new();
            *checker = TypeChecker::new();
            helper.user_defined.clear();
            println!("Environment cleared.");
        }
        ":load" | ":l" => {
            if let Some(path) = arg {
                helper.last_loaded_file = Some(path.to_string());
                load_file(path, interp, checker, helper);
            } else {
                println!("Usage: :load <filename>");
            }
        }
        ":reload" | ":r" => {
            if let Some(ref path) = helper.last_loaded_file.clone() {
                println!("Reloading {}...", path);
                // Clear environment first
                *interp = Interpreter::new();
                *checker = TypeChecker::new();
                helper.user_defined.clear();
                load_file(path, interp, checker, helper);
            } else {
                println!("No file to reload. Use :load <file> first.");
            }
        }
        ":ast" => {
            if let Some(expr_str) = arg {
                show_ast(expr_str);
            } else {
                println!("Usage: :ast <expression>");
            }
        }
        ":trace" => match arg {
            Some("on") => {
                interp.enable_tracing();
                println!("Effect tracing enabled.");
            }
            Some("off") => {
                interp.trace_effects = false;
                println!("Effect tracing disabled.");
            }
            _ => {
                println!("Usage: :trace on|off");
            }
        },
        ":traces" => {
            if interp.get_traces().is_empty() {
                println!("No effect traces recorded. Use :trace on to enable tracing.");
            } else {
                interp.print_traces();
            }
        }
        ":doc" | ":d" => {
            if let Some(name) = arg {
                show_doc(name);
            } else {
                println!("Usage: :doc <name>");
            }
        }
        ":browse" | ":b" => {
            if let Some(module) = arg {
                browse_module(module);
            } else {
                println!("Usage: :browse <module>");
                println!("Available modules: List, String, Option, Result, Console, Random, File, Http");
            }
        }
        _ => {
            println!("Unknown command: {}", cmd);
            println!("Type :help for help");
        }
    }
}

fn show_info(name: &str, checker: &TypeChecker) {
    // Look up in the type environment
    if let Some(scheme) = checker.lookup(name) {
        println!("{} : {}", name, scheme);
    } else {
        println!("'{}' is not defined.", name);
    }
}

fn show_environment(checker: &TypeChecker, helper: &LuxHelper) {
    println!("User-defined bindings:");
    if helper.user_defined.is_empty() {
        println!("  (none)");
    } else {
        for name in &helper.user_defined {
            if let Some(scheme) = checker.lookup(name) {
                println!("  {} : {}", name, scheme);
            } else {
                println!("  {} : <unknown>", name);
            }
        }
    }
}

fn show_doc(name: &str) {
    // Built-in documentation for common functions
    let doc = match name {
        // List functions
        "List.length" => Some(("List.length : List<T> -> Int", "Returns the number of elements in a list.")),
        "List.head" => Some(("List.head : List<T> -> Option<T>", "Returns the first element of a list, or None if empty.")),
        "List.tail" => Some(("List.tail : List<T> -> Option<List<T>>", "Returns all elements except the first, or None if empty.")),
        "List.map" => Some(("List.map : (List<A>, fn(A) -> B) -> List<B>", "Applies a function to each element, returning a new list.")),
        "List.filter" => Some(("List.filter : (List<T>, fn(T) -> Bool) -> List<T>", "Returns elements for which the predicate returns true.")),
        "List.fold" => Some(("List.fold : (List<T>, U, fn(U, T) -> U) -> U", "Reduces a list to a single value using an accumulator function.")),
        "List.reverse" => Some(("List.reverse : List<T> -> List<T>", "Returns a new list with elements in reverse order.")),
        "List.concat" => Some(("List.concat : (List<T>, List<T>) -> List<T>", "Concatenates two lists.")),
        "List.take" => Some(("List.take : (List<T>, Int) -> List<T>", "Returns the first n elements.")),
        "List.drop" => Some(("List.drop : (List<T>, Int) -> List<T>", "Returns all elements after the first n.")),
        "List.get" => Some(("List.get : (List<T>, Int) -> Option<T>", "Returns the element at index n, or None if out of bounds.")),
        "List.contains" => Some(("List.contains : (List<T>, T) -> Bool", "Returns true if the list contains the element.")),
        "List.all" => Some(("List.all : (List<T>, fn(T) -> Bool) -> Bool", "Returns true if all elements satisfy the predicate.")),
        "List.any" => Some(("List.any : (List<T>, fn(T) -> Bool) -> Bool", "Returns true if any element satisfies the predicate.")),
        "List.find" => Some(("List.find : (List<T>, fn(T) -> Bool) -> Option<T>", "Returns the first element satisfying the predicate.")),
        "List.sort" => Some(("List.sort : List<Int> -> List<Int>", "Sorts a list of integers in ascending order.")),
        "List.range" => Some(("List.range : (Int, Int) -> List<Int>", "Creates a list of integers from start to end (exclusive).")),

        // String functions
        "String.length" => Some(("String.length : String -> Int", "Returns the number of characters in a string.")),
        "String.concat" => Some(("String.concat : (String, String) -> String", "Concatenates two strings.")),
        "String.substring" => Some(("String.substring : (String, Int, Int) -> String", "Returns a substring from start to end index.")),
        "String.split" => Some(("String.split : (String, String) -> List<String>", "Splits a string by a delimiter.")),
        "String.join" => Some(("String.join : (List<String>, String) -> String", "Joins a list of strings with a delimiter.")),
        "String.trim" => Some(("String.trim : String -> String", "Removes leading and trailing whitespace.")),
        "String.contains" => Some(("String.contains : (String, String) -> Bool", "Returns true if the string contains the substring.")),
        "String.replace" => Some(("String.replace : (String, String, String) -> String", "Replaces all occurrences of a pattern.")),
        "String.startsWith" => Some(("String.startsWith : (String, String) -> Bool", "Returns true if the string starts with the prefix.")),
        "String.endsWith" => Some(("String.endsWith : (String, String) -> Bool", "Returns true if the string ends with the suffix.")),
        "String.toUpper" => Some(("String.toUpper : String -> String", "Converts to uppercase.")),
        "String.toLower" => Some(("String.toLower : String -> String", "Converts to lowercase.")),

        // Option functions
        "Option.map" => Some(("Option.map : (Option<A>, fn(A) -> B) -> Option<B>", "Applies a function to the value inside Some, or returns None.")),
        "Option.flatMap" => Some(("Option.flatMap : (Option<A>, fn(A) -> Option<B>) -> Option<B>", "Like map, but the function returns an Option.")),
        "Option.getOrElse" => Some(("Option.getOrElse : (Option<T>, T) -> T", "Returns the value inside Some, or the default if None.")),
        "Option.isSome" => Some(("Option.isSome : Option<T> -> Bool", "Returns true if the value is Some.")),
        "Option.isNone" => Some(("Option.isNone : Option<T> -> Bool", "Returns true if the value is None.")),

        // Result functions
        "Result.map" => Some(("Result.map : (Result<A, E>, fn(A) -> B) -> Result<B, E>", "Applies a function to the Ok value.")),
        "Result.flatMap" => Some(("Result.flatMap : (Result<A, E>, fn(A) -> Result<B, E>) -> Result<B, E>", "Like map, but the function returns a Result.")),
        "Result.getOrElse" => Some(("Result.getOrElse : (Result<T, E>, T) -> T", "Returns the Ok value, or the default if Err.")),
        "Result.isOk" => Some(("Result.isOk : Result<T, E> -> Bool", "Returns true if the value is Ok.")),
        "Result.isErr" => Some(("Result.isErr : Result<T, E> -> Bool", "Returns true if the value is Err.")),

        // Effects
        "Console.print" => Some(("Console.print : String -> Unit", "Prints a string to the console.")),
        "Console.readLine" => Some(("Console.readLine : () -> String", "Reads a line from standard input.")),
        "Random.int" => Some(("Random.int : (Int, Int) -> Int", "Generates a random integer in the range [min, max].")),
        "Random.float" => Some(("Random.float : () -> Float", "Generates a random float in [0, 1).")),
        "Random.bool" => Some(("Random.bool : () -> Bool", "Generates a random boolean.")),
        "File.read" => Some(("File.read : String -> String", "Reads the contents of a file.")),
        "File.write" => Some(("File.write : (String, String) -> Unit", "Writes content to a file.")),
        "File.exists" => Some(("File.exists : String -> Bool", "Returns true if the file exists.")),
        "Http.get" => Some(("Http.get : String -> String", "Performs an HTTP GET request.")),
        "Http.post" => Some(("Http.post : (String, String) -> String", "Performs an HTTP POST request with a body.")),
        "Time.now" => Some(("Time.now : () -> Int", "Returns the current Unix timestamp in milliseconds.")),
        "Sql.open" => Some(("Sql.open : String -> SqlConn", "Opens a SQLite database file.")),
        "Sql.openMemory" => Some(("Sql.openMemory : () -> SqlConn", "Opens an in-memory SQLite database.")),
        "Sql.query" => Some(("Sql.query : (SqlConn, String) -> List<Row>", "Executes a SQL query and returns all rows.")),
        "Sql.execute" => Some(("Sql.execute : (SqlConn, String) -> Int", "Executes a SQL statement and returns affected rows.")),
        "Postgres.connect" => Some(("Postgres.connect : String -> Int", "Connects to a PostgreSQL database.")),
        "Postgres.query" => Some(("Postgres.query : (Int, String) -> List<Row>", "Executes a SQL query on PostgreSQL.")),
        "Postgres.execute" => Some(("Postgres.execute : (Int, String) -> Int", "Executes a SQL statement on PostgreSQL.")),

        // Language constructs
        "fn" => Some(("fn name(args): Type = body", "Defines a function.")),
        "let" => Some(("let name = value", "Binds a value to a name.")),
        "if" => Some(("if condition then expr1 else expr2", "Conditional expression.")),
        "match" => Some(("match value { pattern => expr, ... }", "Pattern matching expression.")),
        "effect" => Some(("effect Name { fn op(...): Type }", "Defines an effect with operations.")),
        "handler" => Some(("handler { op => body }", "Defines an effect handler.")),
        "with" => Some(("fn f(): T with {Effect1, Effect2}", "Declares effects a function may perform.")),
        "type" => Some(("type Name<T> = ...", "Defines a type alias.")),
        "run" => Some(("run expr with { handler }", "Executes an expression with effect handlers.")),

        _ => None,
    };

    match doc {
        Some((sig, desc)) => {
            println!("\x1b[1m{}\x1b[0m", sig);
            println!();
            println!("  {}", desc);
        }
        None => {
            println!("No documentation for '{}'", name);
            println!("Try :doc List.map or :browse List");
        }
    }
}

fn browse_module(module: &str) {
    let exports: Vec<(&str, &str)> = match module {
        "List" => vec![
            ("length", "List<T> -> Int"),
            ("head", "List<T> -> Option<T>"),
            ("tail", "List<T> -> Option<List<T>>"),
            ("get", "(List<T>, Int) -> Option<T>"),
            ("map", "(List<A>, fn(A) -> B) -> List<B>"),
            ("filter", "(List<T>, fn(T) -> Bool) -> List<T>"),
            ("fold", "(List<T>, U, fn(U, T) -> U) -> U"),
            ("reverse", "List<T> -> List<T>"),
            ("concat", "(List<T>, List<T>) -> List<T>"),
            ("take", "(List<T>, Int) -> List<T>"),
            ("drop", "(List<T>, Int) -> List<T>"),
            ("contains", "(List<T>, T) -> Bool"),
            ("all", "(List<T>, fn(T) -> Bool) -> Bool"),
            ("any", "(List<T>, fn(T) -> Bool) -> Bool"),
            ("find", "(List<T>, fn(T) -> Bool) -> Option<T>"),
            ("sort", "List<Int> -> List<Int>"),
            ("range", "(Int, Int) -> List<Int>"),
            ("forEach", "(List<T>, fn(T) -> Unit) -> Unit"),
        ],
        "String" => vec![
            ("length", "String -> Int"),
            ("concat", "(String, String) -> String"),
            ("substring", "(String, Int, Int) -> String"),
            ("split", "(String, String) -> List<String>"),
            ("join", "(List<String>, String) -> String"),
            ("trim", "String -> String"),
            ("contains", "(String, String) -> Bool"),
            ("replace", "(String, String, String) -> String"),
            ("startsWith", "(String, String) -> Bool"),
            ("endsWith", "(String, String) -> Bool"),
            ("toUpper", "String -> String"),
            ("toLower", "String -> String"),
            ("lines", "String -> List<String>"),
        ],
        "Option" => vec![
            ("map", "(Option<A>, fn(A) -> B) -> Option<B>"),
            ("flatMap", "(Option<A>, fn(A) -> Option<B>) -> Option<B>"),
            ("getOrElse", "(Option<T>, T) -> T"),
            ("isSome", "Option<T> -> Bool"),
            ("isNone", "Option<T> -> Bool"),
        ],
        "Result" => vec![
            ("map", "(Result<A, E>, fn(A) -> B) -> Result<B, E>"),
            ("flatMap", "(Result<A, E>, fn(A) -> Result<B, E>) -> Result<B, E>"),
            ("getOrElse", "(Result<T, E>, T) -> T"),
            ("isOk", "Result<T, E> -> Bool"),
            ("isErr", "Result<T, E> -> Bool"),
        ],
        "Console" => vec![
            ("print", "String -> Unit"),
            ("readLine", "() -> String"),
        ],
        "Random" => vec![
            ("int", "(Int, Int) -> Int"),
            ("float", "() -> Float"),
            ("bool", "() -> Bool"),
        ],
        "File" => vec![
            ("read", "String -> String"),
            ("write", "(String, String) -> Unit"),
            ("exists", "String -> Bool"),
            ("delete", "String -> Unit"),
        ],
        "Http" => vec![
            ("get", "String -> String"),
            ("post", "(String, String) -> String"),
        ],
        "Time" => vec![
            ("now", "() -> Int"),
        ],
        "Sql" => vec![
            ("open", "String -> SqlConn"),
            ("openMemory", "() -> SqlConn"),
            ("close", "SqlConn -> Unit"),
            ("query", "(SqlConn, String) -> List<Row>"),
            ("queryOne", "(SqlConn, String) -> Option<Row>"),
            ("execute", "(SqlConn, String) -> Int"),
            ("beginTx", "SqlConn -> Unit"),
            ("commit", "SqlConn -> Unit"),
            ("rollback", "SqlConn -> Unit"),
        ],
        "Postgres" => vec![
            ("connect", "String -> Int"),
            ("close", "Int -> Unit"),
            ("query", "(Int, String) -> List<Row>"),
            ("queryOne", "(Int, String) -> Option<Row>"),
            ("execute", "(Int, String) -> Int"),
            ("beginTx", "Int -> Unit"),
            ("commit", "Int -> Unit"),
            ("rollback", "Int -> Unit"),
        ],
        "Test" => vec![
            ("assert", "(Bool, String) -> Unit"),
            ("assertEqual", "(T, T) -> Unit"),
            ("assertTrue", "Bool -> Unit"),
            ("assertFalse", "Bool -> Unit"),
            ("fail", "String -> Unit"),
        ],
        _ => {
            println!("Unknown module: {}", module);
            println!("Available modules: List, String, Option, Result, Console, Random, File, Http, Time, Sql, Postgres, Test");
            return;
        }
    };

    println!("\x1b[1mmodule {}\x1b[0m", module);
    println!();
    for (name, sig) in exports {
        println!("  \x1b[34m{}.{}\x1b[0m : {}", module, name, sig);
    }
}

fn show_type(expr_str: &str, checker: &mut TypeChecker) {
    // Wrap expression in a let to parse it
    let wrapped = format!("let _expr_ = {}", expr_str);

    match Parser::parse_source(&wrapped) {
        Ok(program) => {
            if let Err(errors) = checker.check_program(&program) {
                for error in errors {
                    println!("Type error: {}", error);
                }
            } else {
                println!("(type checking passed)");
            }
        }
        Err(e) => {
            println!("Parse error: {}", e);
        }
    }
}

fn show_ast(expr_str: &str) {
    // Wrap expression in a let to parse it
    let wrapped = format!("let _expr_ = {}", expr_str);

    match Parser::parse_source(&wrapped) {
        Ok(program) => {
            // Pretty print the AST
            for decl in &program.declarations {
                println!("{:#?}", decl);
            }
        }
        Err(e) => {
            println!("Parse error: {}", e);
        }
    }
}

fn load_file(path: &str, interp: &mut Interpreter, checker: &mut TypeChecker, helper: &mut LuxHelper) {
    let source = match std::fs::read_to_string(path) {
        Ok(s) => s,
        Err(e) => {
            println!("Error reading file '{}': {}", path, e);
            return;
        }
    };

    let program = match Parser::parse_source(&source) {
        Ok(p) => p,
        Err(e) => {
            println!("Parse error: {}", e);
            return;
        }
    };

    if let Err(errors) = checker.check_program(&program) {
        for error in errors {
            println!("Type error: {}", error);
        }
        return;
    }

    // Add definitions for completion
    for decl in &program.declarations {
        if let Some(name) = extract_definition_name(&format!("{:?}", decl)) {
            helper.add_definition(&name);
        }
    }

    match interp.run(&program) {
        Ok(_) => println!("Loaded '{}'", path),
        Err(e) => println!("Runtime error: {}", e),
    }
}

fn eval_input(input: &str, interp: &mut Interpreter, checker: &mut TypeChecker) {
    // Try to parse as a program (declarations)
    match Parser::parse_source(input) {
        Ok(program) => {
            // Type check
            if let Err(errors) = checker.check_program(&program) {
                for error in errors {
                    println!("Type error: {}", error);
                }
                return;
            }

            // Execute
            match interp.run(&program) {
                Ok(value) => {
                    if !matches!(value, interpreter::Value::Unit) {
                        println!("{}", value);
                    }
                }
                Err(e) => {
                    println!("Runtime error: {}", e);
                }
            }
        }
        Err(parse_err) => {
            // Try wrapping as an expression
            let wrapped = format!("let _result_ = {}", input.trim());
            match Parser::parse_source(&wrapped) {
                Ok(program) => {
                    if let Err(errors) = checker.check_program(&program) {
                        for error in errors {
                            println!("Type error: {}", error);
                        }
                        return;
                    }

                    match interp.run(&program) {
                        Ok(value) => {
                            println!("{}", value);
                        }
                        Err(e) => {
                            println!("Runtime error: {}", e);
                        }
                    }
                }
                Err(_) => {
                    // Use original error
                    println!("Parse error: {}", parse_err);
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn eval(source: &str) -> Result<String, String> {
        let program = Parser::parse_source(source).map_err(|e| e.to_string())?;

        let mut checker = TypeChecker::new();
        checker.check_program(&program).map_err(|errors| {
            errors
                .iter()
                .map(|e| e.to_string())
                .collect::<Vec<_>>()
                .join("\n")
        })?;

        let mut interp = Interpreter::new();
        let value = interp.run(&program).map_err(|e| e.to_string())?;
        Ok(format!("{}", value))
    }

    #[test]
    fn test_arithmetic() {
        assert_eq!(eval("let x = 1 + 2").unwrap(), "3");
        assert_eq!(eval("let x = 10 - 3").unwrap(), "7");
        assert_eq!(eval("let x = 4 * 5").unwrap(), "20");
        assert_eq!(eval("let x = 15 / 3").unwrap(), "5");
    }

    #[test]
    fn test_function() {
        let source = r#"
            fn add(a: Int, b: Int): Int = a + b
            let result = add(3, 4)
        "#;
        assert_eq!(eval(source).unwrap(), "7");
    }

    #[test]
    fn test_if_expr() {
        let source = r#"
            fn max(a: Int, b: Int): Int = if a > b then a else b
            let result = max(5, 3)
        "#;
        assert_eq!(eval(source).unwrap(), "5");
    }

    #[test]
    fn test_recursion() {
        let source = r#"
            fn factorial(n: Int): Int = if n <= 1 then 1 else n * factorial(n - 1)
            let result = factorial(5)
        "#;
        assert_eq!(eval(source).unwrap(), "120");
    }

    #[test]
    fn test_lambda() {
        let source = r#"
            let double = fn(x: Int): Int => x * 2
            let result = double(21)
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_records() {
        let source = r#"
            let person = { name: "Alice", age: 30 }
            let result = person.age
        "#;
        assert_eq!(eval(source).unwrap(), "30");
    }

    #[test]
    fn test_lists() {
        let source = "let nums = [1, 2, 3]";
        assert_eq!(eval(source).unwrap(), "[1, 2, 3]");
    }

    #[test]
    fn test_tuples() {
        let source = "let pair = (42, \"hello\")";
        assert_eq!(eval(source).unwrap(), "(42, \"hello\")");
    }

    #[test]
    fn test_block() {
        let source = r#"
            let result = {
                let x = 10
                let y = 20
                x + y
            }
        "#;
        assert_eq!(eval(source).unwrap(), "30");
    }

    #[test]
    fn test_pipe() {
        let source = r#"
            fn double(x: Int): Int = x * 2
            fn add_one(x: Int): Int = x + 1
            let result = 5 |> double |> add_one
        "#;
        assert_eq!(eval(source).unwrap(), "11");
    }

    // ============ Standard Library Tests ============

    // List tests
    #[test]
    fn test_list_length() {
        assert_eq!(eval("let x = List.length([1, 2, 3])").unwrap(), "3");
        assert_eq!(eval("let x = List.length([])").unwrap(), "0");
    }

    #[test]
    fn test_list_reverse() {
        assert_eq!(
            eval("let x = List.reverse([1, 2, 3])").unwrap(),
            "[3, 2, 1]"
        );
        assert_eq!(eval("let x = List.reverse([])").unwrap(), "[]");
    }

    #[test]
    fn test_list_range() {
        assert_eq!(eval("let x = List.range(0, 5)").unwrap(), "[0, 1, 2, 3, 4]");
        assert_eq!(eval("let x = List.range(3, 3)").unwrap(), "[]");
        assert_eq!(eval("let x = List.range(-2, 2)").unwrap(), "[-2, -1, 0, 1]");
    }

    #[test]
    fn test_list_head() {
        assert_eq!(eval("let x = List.head([1, 2, 3])").unwrap(), "Some(1)");
        assert_eq!(eval("let x = List.head([])").unwrap(), "None");
    }

    #[test]
    fn test_list_tail() {
        assert_eq!(
            eval("let x = List.tail([1, 2, 3])").unwrap(),
            "Some([2, 3])"
        );
        assert_eq!(eval("let x = List.tail([1])").unwrap(), "Some([])");
        assert_eq!(eval("let x = List.tail([])").unwrap(), "None");
    }

    #[test]
    fn test_list_concat() {
        assert_eq!(
            eval("let x = List.concat([1, 2], [3, 4])").unwrap(),
            "[1, 2, 3, 4]"
        );
        assert_eq!(eval("let x = List.concat([], [1])").unwrap(), "[1]");
        assert_eq!(eval("let x = List.concat([1], [])").unwrap(), "[1]");
    }

    #[test]
    fn test_list_get() {
        assert_eq!(
            eval("let x = List.get([10, 20, 30], 0)").unwrap(),
            "Some(10)"
        );
        assert_eq!(
            eval("let x = List.get([10, 20, 30], 2)").unwrap(),
            "Some(30)"
        );
        assert_eq!(eval("let x = List.get([10, 20, 30], 5)").unwrap(), "None");
        assert_eq!(eval("let x = List.get([10, 20, 30], -1)").unwrap(), "None");
    }

    #[test]
    fn test_list_map() {
        let source = r#"
            fn double(x: Int): Int = x * 2
            let result = List.map([1, 2, 3], double)
        "#;
        assert_eq!(eval(source).unwrap(), "[2, 4, 6]");
    }

    #[test]
    fn test_list_map_lambda() {
        let source = "let x = List.map([1, 2, 3], fn(x: Int): Int => x * x)";
        assert_eq!(eval(source).unwrap(), "[1, 4, 9]");
    }

    #[test]
    fn test_list_filter() {
        let source = "let x = List.filter([1, 2, 3, 4, 5], fn(x: Int): Bool => x > 2)";
        assert_eq!(eval(source).unwrap(), "[3, 4, 5]");
    }

    #[test]
    fn test_list_filter_all() {
        let source = "let x = List.filter([1, 2, 3], fn(x: Int): Bool => x > 10)";
        assert_eq!(eval(source).unwrap(), "[]");
    }

    #[test]
    fn test_list_fold() {
        let source = "let x = List.fold([1, 2, 3, 4], 0, fn(acc: Int, x: Int): Int => acc + x)";
        assert_eq!(eval(source).unwrap(), "10");
    }

    #[test]
    fn test_list_fold_product() {
        let source = "let x = List.fold([1, 2, 3, 4], 1, fn(acc: Int, x: Int): Int => acc * x)";
        assert_eq!(eval(source).unwrap(), "24");
    }

    // String tests
    #[test]
    fn test_string_length() {
        assert_eq!(eval(r#"let x = String.length("hello")"#).unwrap(), "5");
        assert_eq!(eval(r#"let x = String.length("")"#).unwrap(), "0");
    }

    #[test]
    fn test_string_split() {
        assert_eq!(
            eval(r#"let x = String.split("a,b,c", ",")"#).unwrap(),
            r#"["a", "b", "c"]"#
        );
        assert_eq!(
            eval(r#"let x = String.split("hello", ",")"#).unwrap(),
            r#"["hello"]"#
        );
    }

    #[test]
    fn test_string_join() {
        assert_eq!(
            eval(r#"let x = String.join(["a", "b", "c"], "-")"#).unwrap(),
            r#""a-b-c""#
        );
        assert_eq!(
            eval(r#"let x = String.join(["hello"], ",")"#).unwrap(),
            r#""hello""#
        );
        assert_eq!(eval(r#"let x = String.join([], ",")"#).unwrap(), r#""""#);
    }

    #[test]
    fn test_string_trim() {
        assert_eq!(
            eval(r#"let x = String.trim("  hello  ")"#).unwrap(),
            r#""hello""#
        );
        assert_eq!(
            eval(r#"let x = String.trim("hello")"#).unwrap(),
            r#""hello""#
        );
        assert_eq!(eval(r#"let x = String.trim("   ")"#).unwrap(), r#""""#);
    }

    #[test]
    fn test_string_contains() {
        assert_eq!(
            eval(r#"let x = String.contains("hello world", "world")"#).unwrap(),
            "true"
        );
        assert_eq!(
            eval(r#"let x = String.contains("hello", "xyz")"#).unwrap(),
            "false"
        );
        assert_eq!(
            eval(r#"let x = String.contains("hello", "")"#).unwrap(),
            "true"
        );
    }

    #[test]
    fn test_string_replace() {
        assert_eq!(
            eval(r#"let x = String.replace("hello", "l", "L")"#).unwrap(),
            r#""heLLo""#
        );
        assert_eq!(
            eval(r#"let x = String.replace("aaa", "a", "b")"#).unwrap(),
            r#""bbb""#
        );
    }

    #[test]
    fn test_string_chars() {
        assert_eq!(eval(r#"let x = String.chars("hi")"#).unwrap(), "['h', 'i']");
        assert_eq!(eval(r#"let x = String.chars("")"#).unwrap(), "[]");
    }

    #[test]
    fn test_string_lines() {
        // Note: Using actual newline in the string
        let source = r#"let x = String.lines("a
b
c")"#;
        assert_eq!(eval(source).unwrap(), r#"["a", "b", "c"]"#);
    }

    // String interpolation tests
    #[test]
    fn test_string_interpolation_simple() {
        let source = r#"
            let name = "World"
            let x = "Hello, {name}!"
        "#;
        assert_eq!(eval(source).unwrap(), r#""Hello, World!""#);
    }

    #[test]
    fn test_string_interpolation_numbers() {
        let source = r#"
            let n = 42
            let x = "The answer is {n}"
        "#;
        assert_eq!(eval(source).unwrap(), r#""The answer is 42""#);
    }

    #[test]
    fn test_string_interpolation_expressions() {
        let source = r#"
            let a = 2
            let b = 3
            let x = "{a} + {b} = {a + b}"
        "#;
        assert_eq!(eval(source).unwrap(), r#""2 + 3 = 5""#);
    }

    #[test]
    fn test_string_interpolation_escaped_braces() {
        let source = r#"let x = "literal \{braces\}""#;
        assert_eq!(eval(source).unwrap(), r#""literal {braces}""#);
    }

    // Option tests
    #[test]
    fn test_option_constructors() {
        assert_eq!(eval("let x = Some(42)").unwrap(), "Some(42)");
        assert_eq!(eval("let x = None").unwrap(), "None");
    }

    #[test]
    fn test_option_is_some() {
        assert_eq!(eval("let x = Option.isSome(Some(42))").unwrap(), "true");
        assert_eq!(eval("let x = Option.isSome(None)").unwrap(), "false");
    }

    #[test]
    fn test_option_is_none() {
        assert_eq!(eval("let x = Option.isNone(None)").unwrap(), "true");
        assert_eq!(eval("let x = Option.isNone(Some(42))").unwrap(), "false");
    }

    #[test]
    fn test_option_get_or_else() {
        assert_eq!(eval("let x = Option.getOrElse(Some(42), 0)").unwrap(), "42");
        assert_eq!(eval("let x = Option.getOrElse(None, 0)").unwrap(), "0");
    }

    #[test]
    fn test_option_map() {
        let source = "let x = Option.map(Some(5), fn(x: Int): Int => x * 2)";
        assert_eq!(eval(source).unwrap(), "Some(10)");
    }

    #[test]
    fn test_option_map_none() {
        let source = "let x = Option.map(None, fn(x: Int): Int => x * 2)";
        assert_eq!(eval(source).unwrap(), "None");
    }

    #[test]
    fn test_option_flat_map() {
        let source = "let x = Option.flatMap(Some(5), fn(x: Int): Option<Int> => Some(x * 2))";
        assert_eq!(eval(source).unwrap(), "Some(10)");
    }

    #[test]
    fn test_option_flat_map_to_none() {
        let source = "let x = Option.flatMap(Some(5), fn(x: Int): Option<Int> => None)";
        assert_eq!(eval(source).unwrap(), "None");
    }

    // Result tests
    #[test]
    fn test_result_constructors() {
        assert_eq!(eval("let x = Ok(42)").unwrap(), "Ok(42)");
        assert_eq!(eval(r#"let x = Err("error")"#).unwrap(), r#"Err("error")"#);
    }

    #[test]
    fn test_result_is_ok() {
        assert_eq!(eval("let x = Result.isOk(Ok(42))").unwrap(), "true");
        assert_eq!(eval(r#"let x = Result.isOk(Err("e"))"#).unwrap(), "false");
    }

    #[test]
    fn test_result_is_err() {
        assert_eq!(eval(r#"let x = Result.isErr(Err("e"))"#).unwrap(), "true");
        assert_eq!(eval("let x = Result.isErr(Ok(42))").unwrap(), "false");
    }

    #[test]
    fn test_result_get_or_else() {
        assert_eq!(eval("let x = Result.getOrElse(Ok(42), 0)").unwrap(), "42");
        assert_eq!(
            eval(r#"let x = Result.getOrElse(Err("e"), 0)"#).unwrap(),
            "0"
        );
    }

    #[test]
    fn test_result_map() {
        let source = "let x = Result.map(Ok(5), fn(x: Int): Int => x * 2)";
        assert_eq!(eval(source).unwrap(), "Ok(10)");
    }

    #[test]
    fn test_result_map_err() {
        let source = r#"let x = Result.map(Err("e"), fn(x: Int): Int => x * 2)"#;
        assert_eq!(eval(source).unwrap(), r#"Err("e")"#);
    }

    // Utility function tests
    #[test]
    fn test_to_string() {
        assert_eq!(eval("let x = toString(42)").unwrap(), r#""42""#);
        assert_eq!(eval("let x = toString(true)").unwrap(), r#""true""#);
        assert_eq!(eval("let x = toString([1, 2])").unwrap(), r#""[1, 2]""#);
    }

    #[test]
    fn test_type_of() {
        assert_eq!(eval("let x = typeOf(42)").unwrap(), r#""Int""#);
        assert_eq!(eval("let x = typeOf(true)").unwrap(), r#""Bool""#);
        assert_eq!(eval("let x = typeOf([1, 2])").unwrap(), r#""List""#);
        assert_eq!(eval(r#"let x = typeOf("hello")"#).unwrap(), r#""String""#);
    }

    // Bug fix tests
    #[test]
    fn test_record_equality() {
        assert_eq!(eval("let x = { a: 1, b: 2 } == { a: 1, b: 2 }").unwrap(), "true");
        assert_eq!(eval("let x = { a: 1 } == { a: 2 }").unwrap(), "false");
        assert_eq!(eval("let x = { a: 1, b: 2 } == { a: 1, b: 3 }").unwrap(), "false");
    }

    #[test]
    fn test_invalid_escape_sequence() {
        let result = eval(r#"let x = "\z""#);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Invalid escape sequence"));
    }

    #[test]
    fn test_unknown_effect_error() {
        let result = eval("fn test(): Unit with {FakeEffect} = ()");
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Unknown effect"));
    }

    #[test]
    fn test_circular_type_definitions() {
        let result = eval("type A = B\ntype B = A");
        assert!(result.is_err(), "Should detect circular type definitions");
        let err = result.unwrap_err();
        assert!(err.contains("Circular"), "Error should mention 'Circular': {}", err);
    }

    #[test]
    fn test_mutual_recursion() {
        let source = r#"
            fn isEven(n: Int): Bool = if n == 0 then true else isOdd(n - 1)
            fn isOdd(n: Int): Bool = if n == 0 then false else isEven(n - 1)
            let result = isEven(10)
        "#;
        assert_eq!(eval(source).unwrap(), "true");
    }

    // Pipe with stdlib tests
    #[test]
    fn test_pipe_with_list() {
        assert_eq!(
            eval("let x = [1, 2, 3] |> List.reverse").unwrap(),
            "[3, 2, 1]"
        );
        assert_eq!(eval("let x = [1, 2, 3] |> List.length").unwrap(), "3");
    }

    #[test]
    fn test_pipe_with_string() {
        assert_eq!(
            eval(r#"let x = "  hello  " |> String.trim"#).unwrap(),
            r#""hello""#
        );
    }

    // Combined stdlib usage tests
    #[test]
    fn test_list_filter_even() {
        let source = r#"
            fn isEven(x: Int): Bool = x % 2 == 0
            let result = List.filter(List.range(1, 6), isEven)
        "#;
        assert_eq!(eval(source).unwrap(), "[2, 4]");
    }

    #[test]
    fn test_option_chain() {
        let source = r#"
            fn times10(x: Int): Int = x * 10
            let head = List.head([1, 2, 3])
            let mapped = Option.map(head, times10)
            let result = Option.getOrElse(mapped, 0)
        "#;
        assert_eq!(eval(source).unwrap(), "10");
    }

    #[test]
    fn test_option_chain_empty() {
        let source = r#"
            fn times10(x: Int): Int = x * 10
            let head = List.head([])
            let mapped = Option.map(head, times10)
            let result = Option.getOrElse(mapped, 0)
        "#;
        assert_eq!(eval(source).unwrap(), "0");
    }

    // ============ Behavioral Types Tests ============

    #[test]
    fn test_behavioral_pure_function() {
        // A pure function with no effects should work
        let source = r#"
            fn double(x: Int): Int is pure = x * 2
            let result = double(21)
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_behavioral_total_function() {
        // A total function should work
        let source = r#"
            fn always42(): Int is total = 42
            let result = always42()
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_behavioral_idempotent_function() {
        // An idempotent function
        let source = r#"
            fn clamp(x: Int): Int is idempotent = if x < 0 then 0 else x
            let result = clamp(clamp(-5))
        "#;
        assert_eq!(eval(source).unwrap(), "0");
    }

    #[test]
    fn test_behavioral_multiple_properties() {
        // A function with multiple properties
        let source = r#"
            fn identity(x: Int): Int is pure, is total = x
            let result = identity(100)
        "#;
        assert_eq!(eval(source).unwrap(), "100");
    }

    #[test]
    fn test_behavioral_deterministic() {
        let source = r#"
            fn square(x: Int): Int is deterministic = x * x
            let result = square(7)
        "#;
        assert_eq!(eval(source).unwrap(), "49");
    }

    #[test]
    fn test_behavioral_pure_with_effects_error() {
        // A pure function with effects should produce a type error
        let source = r#"
            fn bad(x: Int): Int with {Logger} is pure = x
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("pure but has effects"));
    }

    #[test]
    fn test_behavioral_deterministic_with_random_error() {
        // A deterministic function cannot use Random effect
        let source = r#"
            fn bad(): Int with {Random} is deterministic = Random.int(1, 100)
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("deterministic but uses non-deterministic effects"));
    }

    #[test]
    fn test_behavioral_deterministic_with_time_error() {
        // A deterministic function cannot use Time effect
        let source = r#"
            fn bad(): Int with {Time} is deterministic = Time.now()
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("deterministic but uses non-deterministic effects"));
    }

    #[test]
    fn test_behavioral_commutative_add() {
        // Addition is commutative
        let source = r#"
            fn add(a: Int, b: Int): Int is commutative = a + b
            let result = add(3, 5)
        "#;
        assert_eq!(eval(source).unwrap(), "8");
    }

    #[test]
    fn test_behavioral_commutative_mul() {
        // Multiplication is commutative
        let source = r#"
            fn mul(a: Int, b: Int): Int is commutative = a * b
            let result = mul(3, 5)
        "#;
        assert_eq!(eval(source).unwrap(), "15");
    }

    #[test]
    fn test_behavioral_commutative_subtract_error() {
        // Subtraction is NOT commutative
        let source = r#"
            fn sub(a: Int, b: Int): Int is commutative = a - b
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("commutative but its body is not"));
    }

    #[test]
    fn test_behavioral_commutative_wrong_params_error() {
        // Commutative requires exactly 2 params
        let source = r#"
            fn bad(a: Int): Int is commutative = a
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("has 1 parameters (expected 2)"));
    }

    #[test]
    fn test_behavioral_idempotent_identity() {
        // Identity function is idempotent
        let source = r#"
            fn identity(x: Int): Int is idempotent = x
            let result = identity(42)
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_behavioral_idempotent_constant() {
        // Constant function is idempotent
        let source = r#"
            fn always42(x: Int): Int is idempotent = 42
            let result = always42(100)
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_behavioral_idempotent_clamping() {
        // Clamping pattern is idempotent
        let source = r#"
            fn clampPositive(x: Int): Int is idempotent =
                if x < 0 then 0 else x
            let result = clampPositive(-5)
        "#;
        assert_eq!(eval(source).unwrap(), "0");
    }

    #[test]
    fn test_behavioral_idempotent_increment_error() {
        // Increment is NOT idempotent
        let source = r#"
            fn increment(x: Int): Int is idempotent = x + 1
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("idempotent but could not be verified"));
    }

    #[test]
    fn test_behavioral_total_non_recursive() {
        // Non-recursive function is total
        let source = r#"
            fn double(x: Int): Int is total = x * 2
            let result = double(21)
        "#;
        assert_eq!(eval(source).unwrap(), "42");
    }

    #[test]
    fn test_behavioral_total_structural_recursion() {
        // Structural recursion (n - 1) is total
        let source = r#"
            fn factorial(n: Int): Int is total =
                if n <= 1 then 1 else n * factorial(n - 1)
            let result = factorial(5)
        "#;
        assert_eq!(eval(source).unwrap(), "120");
    }

    #[test]
    fn test_behavioral_total_infinite_loop_error() {
        // Infinite loop is NOT total
        let source = r#"
            fn loop(x: Int): Int is total = loop(x)
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("may not terminate"));
    }

    #[test]
    fn test_behavioral_total_with_fail_error() {
        // Total function cannot use Fail effect
        let source = r#"
            effect Fail { fn fail(msg: String): Unit }
            fn bad(x: Int): Int with {Fail} is total =
                if x < 0 then { Fail.fail("negative"); 0 } else x
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("total but uses the Fail effect"));
    }

    #[test]
    fn test_where_clause_parsing() {
        // Where clause property constraints are parsed correctly
        let source = r#"
            fn retry<F>(action: F, times: Int): Int where F is idempotent = times
            fn idempotentFn(): Int is idempotent = 42
            let x = 1
        "#;
        // Just verify it parses and type-checks without errors
        assert!(eval(source).is_ok());
    }

    #[test]
    fn test_where_clause_satisfied() {
        // Where clause should be satisfied when passing an idempotent function
        // Note: Parameter type must be the type parameter F for constraint to apply
        let source = r#"
            fn runIdempotent<F>(action: F): Int where F is idempotent = 42
            fn idempotentFn(): Int is idempotent = 42
            let result = runIdempotent(idempotentFn)
        "#;
        assert!(eval(source).is_ok());
    }

    #[test]
    fn test_where_clause_violation() {
        // Where clause should fail when passing a non-idempotent function
        // The function notIdempotent doesn't declare "is idempotent", so this should fail
        let source = r#"
            fn runIdempotent<F>(action: F): Int where F is idempotent = 42
            fn notIdempotent(): Int = 42
            let result = runIdempotent(notIdempotent)
        "#;
        let result = eval(source);
        assert!(result.is_err(), "Should fail when where clause is violated");
        assert!(result.unwrap_err().contains("property constraint"),
                "Error should mention property constraint");
    }

    #[test]
    fn test_schema_version_preserved() {
        // Version annotations are preserved in type annotations
        let source = r#"
            fn processV1(x: Int @v1): Int = x
            let value: Int @v1 = 42
            let result = processV1(value)
        "#;
        assert!(eval(source).is_ok());
    }

    #[test]
    fn test_schema_version_mismatch_error() {
        // Version mismatch should produce an error when versions don't match
        let source = r#"
            fn expectV2(x: Int @v2): Int = x
            let oldValue: Int @v1 = 42
            let result = expectV2(oldValue)
        "#;
        let result = eval(source);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Version mismatch"));
    }

    #[test]
    fn test_schema_version_atleast() {
        // @v2+ should accept v2 or higher
        let source = r#"
            fn processAtLeastV2(x: Int @v2+): Int = x
            let value: Int @v2 = 42
            let result = processAtLeastV2(value)
        "#;
        assert!(eval(source).is_ok());
    }

    #[test]
    fn test_schema_version_latest() {
        // @latest should be compatible with any version
        let source = r#"
            fn processLatest(x: Int @latest): Int = x
            let value: Int @v1 = 42
            let result = processLatest(value)
        "#;
        assert!(eval(source).is_ok());
    }

    #[test]
    fn test_schema_breaking_change_with_migration() {
        // Breaking change with migration should not error
        let source = r#"
            type User @v1 { name: String }
            type User @v2 {
                name: String,
                email: String,
                from @v1 = { name: old.name, email: "unknown@example.com" }
            }
            let x = 1
        "#;
        assert!(eval(source).is_ok(), "Breaking change with migration should be allowed");
    }

    #[test]
    fn test_schema_breaking_change_without_migration() {
        // Breaking change without migration should produce an error
        let source = r#"
            type Config @v1 { host: String }
            type Config @v2 {
                host: String,
                port: Int
            }
        "#;
        let result = eval(source);
        assert!(result.is_err(), "Breaking change without migration should fail");
        let err = result.unwrap_err();
        assert!(err.contains("Breaking changes") || err.contains("required field"),
                "Error should mention breaking changes: {}", err);
    }

    #[test]
    fn test_schema_compatible_change() {
        // Compatible change (adding optional field) should not error
        // Note: Currently we don't have Option types fully integrated,
        // so this test verifies the basic flow
        let source = r#"
            type Settings @v1 { theme: String }
            type Settings @v2 { theme: String }
            let x = 1
        "#;
        let result = eval(source);
        assert!(result.is_ok(), "Compatible change should be allowed: {:?}", result);
    }

    // Built-in effect tests
    mod effect_tests {
        use crate::interpreter::{Interpreter, Value};
        use crate::parser::Parser;
        use crate::typechecker::TypeChecker;

        fn run_with_effects(source: &str, initial_state: Value, reader_value: Value) -> Result<(String, String), String> {
            let program = Parser::parse_source(source).map_err(|e| e.to_string())?;
            let mut checker = TypeChecker::new();
            checker.check_program(&program).map_err(|errors| {
                errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n")
            })?;
            let mut interp = Interpreter::new();
            interp.set_state(initial_state);
            interp.set_reader(reader_value);
            let result = interp.run(&program).map_err(|e| e.to_string())?;
            let final_state = interp.get_state();
            Ok((format!("{}", result), format!("{}", final_state)))
        }

        #[test]
        fn test_state_get() {
            let source = r#"
                fn getValue(): Int with {State} = State.get()
                let result = run getValue() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Int(42), Value::Unit).unwrap();
            assert_eq!(result, "42");
        }

        #[test]
        fn test_state_put() {
            let source = r#"
                fn setValue(x: Int): Unit with {State} = State.put(x)
                let result = run setValue(100) with {}
            "#;
            let (_, final_state) = run_with_effects(source, Value::Int(0), Value::Unit).unwrap();
            assert_eq!(final_state, "100");
        }

        #[test]
        fn test_state_get_and_put() {
            let source = r#"
                fn increment(): Int with {State} = {
                    let current = State.get()
                    State.put(current + 1)
                    State.get()
                }
                let result = run increment() with {}
            "#;
            let (result, final_state) = run_with_effects(source, Value::Int(10), Value::Unit).unwrap();
            assert_eq!(result, "11");
            assert_eq!(final_state, "11");
        }

        #[test]
        fn test_reader_ask() {
            let source = r#"
                fn getConfig(): String with {Reader} = Reader.ask()
                let result = run getConfig() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::String("config_value".to_string())).unwrap();
            // Value's Display includes quotes for strings
            assert_eq!(result, "\"config_value\"");
        }

        #[test]
        fn test_fail_effect() {
            let source = r#"
                fn failing(): Int with {Fail} = Fail.fail("oops")
                let result = run failing() with {}
            "#;
            let result = run_with_effects(source, Value::Unit, Value::Unit);
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("oops"));
        }

        #[test]
        fn test_combined_effects() {
            let source = r#"
                fn compute(): Int with {State, Reader} = {
                    let config = Reader.ask()
                    let current = State.get()
                    State.put(current + 1)
                    current
                }
                let result = run compute() with {}
            "#;
            let (result, final_state) = run_with_effects(source, Value::Int(5), Value::String("test".to_string())).unwrap();
            assert_eq!(result, "5");
            assert_eq!(final_state, "6");
        }

        #[test]
        fn test_random_int() {
            let source = r#"
                fn getRandomInt(): Int with {Random} = Random.int(1, 10)
                let result = run getRandomInt() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            let num: i64 = result.parse().expect("Should be an integer");
            assert!(num >= 1 && num <= 10, "Random int should be in range 1-10, got {}", num);
        }

        #[test]
        fn test_random_float() {
            let source = r#"
                fn getRandomFloat(): Float with {Random} = Random.float()
                let result = run getRandomFloat() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            let num: f64 = result.parse().expect("Should be a float");
            assert!(num >= 0.0 && num < 1.0, "Random float should be in range [0, 1), got {}", num);
        }

        #[test]
        fn test_random_bool() {
            let source = r#"
                fn getRandomBool(): Bool with {Random} = Random.bool()
                let result = run getRandomBool() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert!(result == "true" || result == "false", "Random bool should be true or false, got {}", result);
        }

        #[test]
        fn test_time_now() {
            let source = r#"
                fn getTime(): Int with {Time} = Time.now()
                let result = run getTime() with {}
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            let timestamp: i64 = result.parse().expect("Should be a timestamp");
            // Timestamp should be a reasonable Unix time in milliseconds (after 2020)
            assert!(timestamp > 1577836800000, "Timestamp should be after 2020");
        }

        #[test]
        fn test_resumable_handler() {
            // Test that resume works in handler bodies
            let source = r#"
                effect Counter {
                    fn increment(): Int
                }

                fn useCounter(): Int with {Counter} = {
                    let a = Counter.increment()
                    let b = Counter.increment()
                    a + b
                }

                handler countingHandler: Counter {
                    fn increment() = resume(10)
                }

                let result = run useCounter() with {
                    Counter = countingHandler
                }
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            // Each increment returns 10, so a + b = 10 + 10 = 20
            assert_eq!(result, "20");
        }

        #[test]
        fn test_resume_outside_handler_fails() {
            // Resume outside handler should fail at runtime
            let source = r#"
                fn bad(): Int = resume(42)
                let result = bad()
            "#;
            let result = run_with_effects(source, Value::Unit, Value::Unit);
            assert!(result.is_err());
            let err_msg = result.unwrap_err();
            assert!(err_msg.contains("outside") || err_msg.contains("Resume"),
                "Error should mention resume outside handler: {}", err_msg);
        }

        // Schema Evolution tests
        #[test]
        fn test_schema_versioned() {
            let source = r#"
                let user = Schema.versioned("User", 1, { name: "Alice", age: 30 })
                let version = Schema.getVersion(user)
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "1");
        }

        #[test]
        fn test_schema_migrate_same_version() {
            let source = r#"
                let user = Schema.versioned("User", 2, { name: "Bob" })
                let migrated = Schema.migrate(user, 2)
                let version = Schema.getVersion(migrated)
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "2");
        }

        #[test]
        fn test_schema_migrate_upgrade() {
            let source = r#"
                let user = Schema.versioned("User", 1, { name: "Charlie" })
                let migrated = Schema.migrate(user, 3)
                let version = Schema.getVersion(migrated)
            "#;
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "3");
        }

        #[test]
        fn test_schema_migrate_downgrade_fails() {
            let source = r#"
                let user = Schema.versioned("User", 3, { name: "Dave" })
                let migrated = Schema.migrate(user, 1)
            "#;
            let result = run_with_effects(source, Value::Unit, Value::Unit);
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("downgrade"));
        }

        // HttpServer effect type-checking tests
        #[test]
        fn test_http_server_typecheck() {
            // Verify HttpServer effect operations type-check correctly
            // We can't actually run the server in tests, but we can verify types
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            let source = r#"
                // Function that uses HttpServer effect
                fn handleRequest(req: { method: String, path: String, body: String, headers: List<(String, String)> }): Unit with {HttpServer} =
                    HttpServer.respond(200, "Hello!")

                fn serveOne(port: Int): Unit with {HttpServer} = {
                    HttpServer.listen(port)
                    let req = HttpServer.accept()
                    handleRequest(req)
                    HttpServer.stop()
                }
            "#;

            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            let result = checker.check_program(&program);
            assert!(result.is_ok(), "HttpServer type checking failed: {:?}", result);
        }

        // Behavioral types tests
        #[test]
        fn test_behavioral_pure_function() {
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            let source = r#"
                fn add(a: Int, b: Int): Int is pure = a + b
                fn square(x: Int): Int is pure = x * x
                let result = add(square(3), square(4))
            "#;
            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            assert!(checker.check_program(&program).is_ok());
        }

        #[test]
        fn test_behavioral_deterministic() {
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            let source = r#"
                fn factorial(n: Int): Int is deterministic =
                    if n <= 1 then 1 else n * factorial(n - 1)
                let result = factorial(5)
            "#;
            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            assert!(checker.check_program(&program).is_ok());
        }

        #[test]
        fn test_behavioral_commutative() {
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            // Commutative verification works with arithmetic operators
            let source = r#"
                fn add(a: Int, b: Int): Int is commutative = a + b
                fn mul(a: Int, b: Int): Int is commutative = a * b
                let result = add(10, 20)
            "#;
            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            assert!(checker.check_program(&program).is_ok());
        }

        #[test]
        fn test_behavioral_idempotent() {
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            // Idempotent verification works with abs pattern
            let source = r#"
                fn absolute(x: Int): Int is idempotent =
                    if x < 0 then 0 - x else x
                let result = absolute(-42)
            "#;
            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            assert!(checker.check_program(&program).is_ok());
        }

        #[test]
        fn test_behavioral_total() {
            use crate::parser::Parser;
            use crate::typechecker::TypeChecker;

            let source = r#"
                fn sumTo(n: Int): Int is total =
                    if n <= 0 then 0 else n + sumTo(n - 1)
                fn power(base: Int, exp: Int): Int is total =
                    if exp <= 0 then 1 else base * power(base, exp - 1)
                let result = sumTo(10)
            "#;
            let program = Parser::parse_source(source).expect("parse failed");
            let mut checker = TypeChecker::new();
            assert!(checker.check_program(&program).is_ok());
        }

        // Math module tests
        #[test]
        fn test_math_abs() {
            let (result, _) = run_with_effects("let x = Math.abs(-42)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "42");
            let (result, _) = run_with_effects("let x = Math.abs(42)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "42");
        }

        #[test]
        fn test_math_min_max() {
            let (result, _) = run_with_effects("let x = Math.min(3, 7)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "3");
            let (result, _) = run_with_effects("let x = Math.max(3, 7)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "7");
        }

        #[test]
        fn test_math_sqrt() {
            let (result, _) = run_with_effects("let x = Math.sqrt(16)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "4");
        }

        #[test]
        fn test_math_pow() {
            let (result, _) = run_with_effects("let x = Math.pow(2, 10)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "1024");
        }

        #[test]
        fn test_math_floor_ceil_round() {
            let (result, _) = run_with_effects("let x = Math.floor(3.7)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "3");
            let (result, _) = run_with_effects("let x = Math.ceil(3.2)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "4");
            let (result, _) = run_with_effects("let x = Math.round(3.5)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "4");
        }

        // List module additional functions
        #[test]
        fn test_list_is_empty() {
            let (result, _) = run_with_effects("let x = List.isEmpty([])", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "true");
            let (result, _) = run_with_effects("let x = List.isEmpty([1, 2])", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "false");
        }

        #[test]
        fn test_list_find() {
            let source = "let x = List.find([1, 2, 3, 4, 5], fn(x: Int): Bool => x > 3)";
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "Some(4)");
            let source = "let x = List.find([1, 2, 3], fn(x: Int): Bool => x > 10)";
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "None");
        }

        #[test]
        fn test_list_any_all() {
            let source = "let x = List.any([1, 2, 3], fn(x: Int): Bool => x > 2)";
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "true");
            let source = "let x = List.all([1, 2, 3], fn(x: Int): Bool => x > 0)";
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "true");
            let source = "let x = List.all([1, 2, 3], fn(x: Int): Bool => x > 2)";
            let (result, _) = run_with_effects(source, Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "false");
        }

        #[test]
        fn test_list_take_drop() {
            let (result, _) = run_with_effects("let x = List.take([1, 2, 3, 4, 5], 3)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "[1, 2, 3]");
            let (result, _) = run_with_effects("let x = List.drop([1, 2, 3, 4, 5], 2)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "[3, 4, 5]");
        }

        // String module additional functions
        #[test]
        fn test_string_starts_ends_with() {
            let (result, _) = run_with_effects("let x = String.startsWith(\"hello\", \"he\")", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "true");
            let (result, _) = run_with_effects("let x = String.endsWith(\"hello\", \"lo\")", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "true");
        }

        #[test]
        fn test_string_case_conversion() {
            let (result, _) = run_with_effects("let x = String.toUpper(\"hello\")", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "\"HELLO\"");
            let (result, _) = run_with_effects("let x = String.toLower(\"HELLO\")", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "\"hello\"");
        }

        #[test]
        fn test_string_substring() {
            let (result, _) = run_with_effects("let x = String.substring(\"hello world\", 0, 5)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "\"hello\"");
            let (result, _) = run_with_effects("let x = String.substring(\"hello\", 2, 4)", Value::Unit, Value::Unit).unwrap();
            assert_eq!(result, "\"ll\"");
        }
    }

    // Diagnostic rendering tests
    mod diagnostic_tests {
        use crate::diagnostics::{render_diagnostic_plain, Diagnostic, Severity};
        use crate::ast::Span;
        use crate::typechecker::TypeError;
        use crate::interpreter::RuntimeError;

        #[test]
        fn test_type_error_to_diagnostic() {
            let error = TypeError {
                message: "Type mismatch: expected Int, got String".to_string(),
                span: Span { start: 10, end: 20 },
            };
            let diag = error.to_diagnostic();

            assert_eq!(diag.severity, Severity::Error);
            assert_eq!(diag.title, "Type Mismatch");
            assert!(diag.hints.len() > 0);
        }

        #[test]
        fn test_runtime_error_to_diagnostic() {
            let error = RuntimeError {
                message: "Division by zero".to_string(),
                span: Some(Span { start: 5, end: 10 }),
            };
            let diag = error.to_diagnostic();

            assert_eq!(diag.severity, Severity::Error);
            assert_eq!(diag.title, "Division by Zero");
            assert!(diag.hints.len() > 0);
        }

        #[test]
        fn test_diagnostic_render_with_real_code() {
            let source = "fn add(a: Int, b: Int): Int = a + b\nlet result = add(1, \"two\")";
            let diag = Diagnostic::error(
                "Type Mismatch",
                "Expected Int but got String",
                Span { start: 56, end: 61 },
            )
            .with_hint("The second argument should be an Int.");

            let output = render_diagnostic_plain(&diag, source, Some("example.lux"));

            assert!(output.contains("ERROR"));
            assert!(output.contains("example.lux"));
            assert!(output.contains("Type Mismatch"));
            assert!(output.contains("\"two\""));
            assert!(output.contains("Hint:"));
        }

        #[test]
        fn test_undefined_variable_categorization() {
            let error = TypeError {
                message: "Undefined variable: foobar".to_string(),
                span: Span { start: 0, end: 6 },
            };
            let diag = error.to_diagnostic();

            assert_eq!(diag.title, "Undefined Variable");
            assert!(diag.hints.iter().any(|h| h.contains("spelling")));
            // Check error code is set
            assert!(diag.code.is_some());
        }

        #[test]
        fn test_undefined_variable_suggestion() {
            // Test that similar variable names are suggested
            let source = r#"
                let myVariable = 42
                let x = myVriable
            "#;
            let result = super::eval(source);
            assert!(result.is_err());
            let err = result.unwrap_err();
            // The error should contain a "Did you mean?" suggestion
            assert!(err.contains("Did you mean") || err.contains("myVariable"),
                "Error should suggest 'myVariable': {}", err);
        }

        #[test]
        fn test_purity_violation_categorization() {
            let error = TypeError {
                message: "Function 'foo' is declared as pure but has effects: {Console}".to_string(),
                span: Span { start: 0, end: 10 },
            };
            let diag = error.to_diagnostic();

            assert_eq!(diag.title, "Purity Violation");
        }
    }

    // Exhaustiveness checking tests
    mod exhaustiveness_tests {
        use super::*;

        #[test]
        fn test_exhaustive_bool_match() {
            let source = r#"
                fn check(b: Bool): Int = match b {
                    true => 1,
                    false => 0
                }
                let result = check(true)
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_non_exhaustive_bool_match() {
            let source = r#"
                fn check(b: Bool): Int = match b {
                    true => 1
                }
                let result = check(true)
            "#;
            let result = eval(source);
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("Non-exhaustive"));
        }

        #[test]
        fn test_exhaustive_option_match() {
            let source = r#"
                fn unwrap_or(opt: Option<Int>, default: Int): Int = match opt {
                    Some(x) => x,
                    None => default
                }
                let result = unwrap_or(Some(42), 0)
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_non_exhaustive_option_missing_none() {
            let source = r#"
                fn get_value(opt: Option<Int>): Int = match opt {
                    Some(x) => x
                }
                let result = get_value(Some(1))
            "#;
            let result = eval(source);
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("Non-exhaustive"));
        }

        #[test]
        fn test_wildcard_is_exhaustive() {
            let source = r#"
                fn classify(n: Int): String = match n {
                    0 => "zero",
                    1 => "one",
                    _ => "many"
                }
                let result = classify(5)
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_variable_pattern_is_exhaustive() {
            let source = r#"
                fn identity(n: Int): Int = match n {
                    x => x
                }
                let result = identity(42)
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_redundant_arm_warning() {
            let source = r#"
                fn test_fn(n: Int): Int = match n {
                    _ => 1,
                    0 => 2
                }
                let result = test_fn(0)
            "#;
            let result = eval(source);
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("Redundant"));
        }

        #[test]
        fn test_exhaustive_result_match() {
            let source = r#"
                fn handle_result(r: Result<Int, String>): Int = match r {
                    Ok(n) => n,
                    Err(_) => 0
                }
                let result = handle_result(Ok(42))
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_tail_call_optimization() {
            // This test verifies that tail-recursive functions don't overflow the stack.
            // Without TCO, a countdown from 10000 would cause a stack overflow.
            let source = r#"
                fn countdown(n: Int): Int = if n <= 0 then 0 else countdown(n - 1)
                let result = countdown(10000)
            "#;
            assert_eq!(eval(source).unwrap(), "0");
        }

        #[test]
        fn test_tail_call_with_accumulator() {
            // Test TCO with an accumulator pattern (common for tail-recursive sum)
            let source = r#"
                fn sum_to(n: Int, acc: Int): Int = if n <= 0 then acc else sum_to(n - 1, acc + n)
                let result = sum_to(1000, 0)
            "#;
            // Sum from 1 to 1000 = 1000 * 1001 / 2 = 500500
            assert_eq!(eval(source).unwrap(), "500500");
        }

        #[test]
        fn test_tail_call_in_match() {
            // Test that TCO works through match expressions
            let source = r#"
                fn process(opt: Option<Int>, acc: Int): Int = match opt {
                    Some(n) => if n <= 0 then acc else process(Some(n - 1), acc + n),
                    None => acc
                }
                let result = process(Some(100), 0)
            "#;
            // Sum from 1 to 100 = 5050
            assert_eq!(eval(source).unwrap(), "5050");
        }

        #[test]
        fn test_trait_definition() {
            // Test that trait declarations parse and type check correctly
            let source = r#"
                trait Show {
                    fn show(x: Int): String
                }
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_trait_impl() {
            // Test that impl declarations parse and type check correctly
            let source = r#"
                trait Double {
                    fn double(x: Int): Int
                }
                impl Double for Int {
                    fn double(x: Int): Int = x * 2
                }
                let result = 21
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_trait_with_super_trait() {
            // Test super trait syntax
            let source = r#"
                trait Eq {
                    fn eq(a: Int, b: Int): Bool
                }
                trait Ord: Eq {
                    fn lt(a: Int, b: Int): Bool
                }
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_impl_with_where_clause() {
            // Test impl with where clause for trait constraints
            let source = r#"
                trait Show {
                    fn show(x: Int): String
                }
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success but got: {:?}", result);
        }

        #[test]
        fn test_effect_inference_function() {
            // Test that effects are inferred when not explicitly declared
            // This function uses Console effect without declaring it
            let source = r#"
                effect Console {
                    fn print(msg: String): Unit
                }
                fn greet(name: String): Unit = Console.print("Hello")
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success with inferred effects but got: {:?}", result);
        }

        #[test]
        fn test_effect_inference_lambda() {
            // Test that lambda effects are inferred
            let source = r#"
                effect Logger {
                    fn log(msg: String): Unit
                }
                let logFn = fn(msg: String) => Logger.log(msg)
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success with inferred lambda effects but got: {:?}", result);
        }

        #[test]
        fn test_explicit_effects_validation() {
            // Test that explicitly declared effects are validated against usage
            let source = r#"
                effect Console {
                    fn print(msg: String): Unit
                }
                fn greet(name: String): Unit with {Console} = Console.print("Hello")
                let result = 42
            "#;
            let result = eval(source);
            assert!(result.is_ok(), "Expected success with explicit effects but got: {:?}", result);
        }

        #[test]
        fn test_doc_comments_on_function() {
            // Test that doc comments are parsed and attached to functions
            let source = r#"
                /// Adds two numbers together.
                /// Returns the sum.
                fn add(a: Int, b: Int): Int = a + b
                let result = add(1, 2)
            "#;
            assert_eq!(eval(source).unwrap(), "3");
        }

        #[test]
        fn test_doc_comments_on_type() {
            // Test that doc comments are parsed and attached to types
            let source = r#"
                /// A point in 2D space.
                type Point { x: Int, y: Int }
                let p = { x: 1, y: 2 }
                let result = p.x + p.y
            "#;
            assert_eq!(eval(source).unwrap(), "3");
        }

        #[test]
        fn test_doc_comments_multiline() {
            // Test that multiple doc comment lines are combined
            let source = r#"
                /// First line of documentation.
                /// Second line of documentation.
                /// Third line of documentation.
                fn documented(): Int = 42
                let result = documented()
            "#;
            assert_eq!(eval(source).unwrap(), "42");
        }
    }

    // Integration tests for example files
    mod example_tests {
        use super::*;
        use std::fs;

        fn check_file(path: &str) -> Result<(), String> {
            let source = fs::read_to_string(path)
                .map_err(|e| format!("Failed to read {}: {}", path, e))?;

            let program = Parser::parse_source(&source)
                .map_err(|e| format!("Parse error in {}: {}", path, e))?;

            let mut checker = TypeChecker::new();
            checker.check_program(&program).map_err(|errors| {
                format!("Type errors in {}:\n{}", path,
                    errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"))
            })?;

            Ok(())
        }

        #[test]
        fn test_example_hello() {
            check_file("examples/hello.lux").unwrap();
        }

        #[test]
        fn test_example_factorial() {
            check_file("examples/factorial.lux").unwrap();
        }

        #[test]
        fn test_example_functional() {
            check_file("examples/functional.lux").unwrap();
        }

        #[test]
        fn test_example_pipelines() {
            check_file("examples/pipelines.lux").unwrap();
        }

        #[test]
        fn test_example_tailcall() {
            check_file("examples/tailcall.lux").unwrap();
        }

        #[test]
        fn test_example_effects() {
            check_file("examples/effects.lux").unwrap();
        }

        #[test]
        fn test_example_handlers() {
            check_file("examples/handlers.lux").unwrap();
        }

        #[test]
        fn test_example_builtin_effects() {
            check_file("examples/builtin_effects.lux").unwrap();
        }

        #[test]
        fn test_example_datatypes() {
            check_file("examples/datatypes.lux").unwrap();
        }

        #[test]
        fn test_example_generics() {
            check_file("examples/generics.lux").unwrap();
        }

        #[test]
        fn test_example_traits() {
            check_file("examples/traits.lux").unwrap();
        }

        #[test]
        fn test_example_interpolation() {
            check_file("examples/interpolation.lux").unwrap();
        }

        #[test]
        fn test_example_behavioral() {
            check_file("examples/behavioral.lux").unwrap();
        }

        #[test]
        fn test_example_behavioral_types() {
            check_file("examples/behavioral_types.lux").unwrap();
        }

        #[test]
        fn test_example_versioning() {
            check_file("examples/versioning.lux").unwrap();
        }

        #[test]
        fn test_example_schema_evolution() {
            check_file("examples/schema_evolution.lux").unwrap();
        }

        #[test]
        fn test_example_file_io() {
            check_file("examples/file_io.lux").unwrap();
        }

        #[test]
        fn test_example_json() {
            check_file("examples/json.lux").unwrap();
        }

        #[test]
        fn test_example_random() {
            check_file("examples/random.lux").unwrap();
        }

        #[test]
        fn test_example_statemachine() {
            check_file("examples/statemachine.lux").unwrap();
        }

        #[test]
        fn test_example_shell() {
            check_file("examples/shell.lux").unwrap();
        }

        #[test]
        fn test_example_http() {
            check_file("examples/http.lux").unwrap();
        }

        #[test]
        fn test_example_http_server() {
            check_file("examples/http_server.lux").unwrap();
        }

        #[test]
        fn test_example_jit_test() {
            check_file("examples/jit_test.lux").unwrap();
        }

        #[test]
        fn test_project_json_parser() {
            check_file("projects/json-parser/main.lux").unwrap();
        }

        #[test]
        fn test_project_markdown_converter() {
            check_file("projects/markdown-converter/main.lux").unwrap();
        }

        #[test]
        fn test_project_todo_app() {
            check_file("projects/todo-app/main.lux").unwrap();
        }

        #[test]
        fn test_project_mini_interpreter() {
            check_file("projects/mini-interpreter/main.lux").unwrap();
        }

        #[test]
        fn test_project_guessing_game() {
            check_file("projects/guessing-game/main.lux").unwrap();
        }

        #[test]
        fn test_project_rest_api() {
            check_file("projects/rest-api/main.lux").unwrap();
        }
    }
}
