//! JavaScript code generation backend for Lux
//!
//! Compiles Lux programs to JavaScript for browser and Node.js execution.
//!
//! ## Compilation Strategy
//!
//! Lux source → Parse → Type check → Generate JavaScript → Run in browser/Node
//!
//! ## Runtime Type Representations
//!
//! | Lux Type | JavaScript Type |
//! |----------|-----------------|
//! | Int | `number` (BigInt for large values) |
//! | Float | `number` |
//! | Bool | `boolean` |
//! | String | `string` |
//! | Unit | `undefined` |
//! | List<T> | `Array` |
//! | Option<T> | `{tag: "Some", value: T} \| {tag: "None"}` |
//! | Result<T, E> | `{tag: "Ok", value: T} \| {tag: "Err", error: E}` |
//! | Closure | `function` (native JS closures) |
//! | ADT | `{tag: "VariantName", field0: ..., field1: ...}` |
//!
//! ## Effects
//!
//! Effects are compiled to handler objects passed as parameters:
//! ```javascript
//! async function fetchUser_lux(handlers, id) {
//!     return await handlers.Http.get(`/api/users/${id}`);
//! }
//! ```

use crate::ast::*;
use std::collections::{HashMap, HashSet};

/// JavaScript code generation errors
#[derive(Debug, Clone)]
pub struct JsGenError {
    pub message: String,
    #[allow(dead_code)]
    pub span: Option<Span>,
}

impl std::fmt::Display for JsGenError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "JS codegen error: {}", self.message)
    }
}

impl std::error::Error for JsGenError {}

/// The JavaScript backend code generator
pub struct JsBackend {
    /// Generated JavaScript code
    output: String,
    /// Current indentation level
    indent: usize,
    /// Known function names
    functions: HashSet<String>,
    /// Counter for generating unique names
    name_counter: usize,
    /// Mapping from variant names to their parent type name
    variant_to_type: HashMap<String, String>,
    /// Mapping from (type_name, variant_name) to field types
    variant_field_types: HashMap<(String, String), Vec<String>>,
    /// Functions that use effects (have handlers parameter)
    effectful_functions: HashSet<String>,
    /// Whether we're currently inside an effectful function
    has_handlers: bool,
    /// Variable substitutions for let binding
    var_substitutions: HashMap<String, String>,
}

impl JsBackend {
    pub fn new() -> Self {
        // Initialize built-in type variants
        let mut variant_to_type = HashMap::new();
        variant_to_type.insert("Some".to_string(), "Option".to_string());
        variant_to_type.insert("None".to_string(), "Option".to_string());
        variant_to_type.insert("Ok".to_string(), "Result".to_string());
        variant_to_type.insert("Err".to_string(), "Result".to_string());

        Self {
            output: String::new(),
            indent: 0,
            functions: HashSet::new(),
            name_counter: 0,
            variant_to_type,
            variant_field_types: HashMap::new(),
            effectful_functions: HashSet::new(),
            has_handlers: false,
            var_substitutions: HashMap::new(),
        }
    }

    /// Generate JavaScript code from a Lux program
    pub fn generate(&mut self, program: &Program) -> Result<String, JsGenError> {
        self.output.clear();

        // Emit runtime helpers
        self.emit_runtime();

        // First pass: collect all function names, types, and effects
        for decl in &program.declarations {
            match decl {
                Declaration::Function(f) => {
                    self.functions.insert(f.name.name.clone());
                    if !f.effects.is_empty() {
                        self.effectful_functions.insert(f.name.name.clone());
                    }
                }
                Declaration::Type(t) => {
                    self.collect_type(t)?;
                }
                _ => {}
            }
        }

        // Emit type constructors
        for decl in &program.declarations {
            if let Declaration::Type(t) = decl {
                self.emit_type_constructors(t)?;
            }
        }

        // Emit functions
        for decl in &program.declarations {
            if let Declaration::Function(f) = decl {
                self.emit_function(f)?;
            }
        }

        // Check if any top-level let calls main (to avoid double invocation)
        let has_main_call = program.declarations.iter().any(|decl| {
            if let Declaration::Let(l) = decl {
                self.expr_calls_main(&l.value)
            } else {
                false
            }
        });

        // Emit top-level let bindings and expressions
        if program.declarations.iter().any(|d| matches!(d, Declaration::Let(_))) {
            self.writeln("// Top-level bindings");
            for decl in &program.declarations {
                if let Declaration::Let(l) = decl {
                    self.emit_top_level_let(l)?;
                }
            }
        }

        // Emit main function call if it exists and not already called
        if self.functions.contains("main") && !has_main_call {
            self.writeln("");
            self.writeln("// Entry point");
            if self.effectful_functions.contains("main") {
                self.writeln("main_lux(Lux.defaultHandlers);");
            } else {
                self.writeln("main_lux();");
            }
        }

        Ok(self.output.clone())
    }

    /// Emit the minimal Lux runtime
    fn emit_runtime(&mut self) {
        self.writeln("// Lux Runtime");
        self.writeln("const Lux = {");
        self.indent += 1;

        // Option helpers
        self.writeln("Some: (value) => ({ tag: \"Some\", value }),");
        self.writeln("None: () => ({ tag: \"None\" }),");
        self.writeln("");

        // Result helpers
        self.writeln("Ok: (value) => ({ tag: \"Ok\", value }),");
        self.writeln("Err: (error) => ({ tag: \"Err\", error }),");
        self.writeln("");

        // List helpers
        self.writeln("Cons: (head, tail) => [head, ...tail],");
        self.writeln("Nil: () => [],");
        self.writeln("");

        // Default handlers for effects
        self.writeln("defaultHandlers: {");
        self.indent += 1;

        // Console effect
        self.writeln("Console: {");
        self.indent += 1;
        self.writeln("print: (msg) => console.log(msg),");
        self.writeln("readLine: () => {");
        self.indent += 1;
        self.writeln("if (typeof require !== 'undefined') {");
        self.indent += 1;
        self.writeln("const readline = require('readline');");
        self.writeln("const rl = readline.createInterface({ input: process.stdin, output: process.stdout });");
        self.writeln("return new Promise(resolve => rl.question('', answer => { rl.close(); resolve(answer); }));");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("return prompt('') || '';");
        self.indent -= 1;
        self.writeln("},");
        self.writeln("readInt: () => parseInt(Lux.defaultHandlers.Console.readLine(), 10)");
        self.indent -= 1;
        self.writeln("},");

        // Random effect
        self.writeln("Random: {");
        self.indent += 1;
        self.writeln("int: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,");
        self.writeln("bool: () => Math.random() < 0.5,");
        self.writeln("float: () => Math.random()");
        self.indent -= 1;
        self.writeln("},");

        // Time effect
        self.writeln("Time: {");
        self.indent += 1;
        self.writeln("now: () => Date.now(),");
        self.writeln("sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms))");
        self.indent -= 1;
        self.writeln("},");

        // Http effect (browser/Node compatible)
        self.writeln("Http: {");
        self.indent += 1;
        self.writeln("get: async (url) => {");
        self.indent += 1;
        self.writeln("try {");
        self.indent += 1;
        self.writeln("const response = await fetch(url);");
        self.writeln("const body = await response.text();");
        self.writeln("const headers = [];");
        self.writeln("response.headers.forEach((v, k) => headers.push([k, v]));");
        self.writeln("return Lux.Ok({ status: response.status, body, headers });");
        self.indent -= 1;
        self.writeln("} catch (e) {");
        self.indent += 1;
        self.writeln("return Lux.Err(e.message);");
        self.indent -= 1;
        self.writeln("}");
        self.indent -= 1;
        self.writeln("},");
        self.writeln("post: async (url, body) => {");
        self.indent += 1;
        self.writeln("try {");
        self.indent += 1;
        self.writeln("const response = await fetch(url, { method: 'POST', body });");
        self.writeln("const respBody = await response.text();");
        self.writeln("const headers = [];");
        self.writeln("response.headers.forEach((v, k) => headers.push([k, v]));");
        self.writeln("return Lux.Ok({ status: response.status, body: respBody, headers });");
        self.indent -= 1;
        self.writeln("} catch (e) {");
        self.indent += 1;
        self.writeln("return Lux.Err(e.message);");
        self.indent -= 1;
        self.writeln("}");
        self.indent -= 1;
        self.writeln("},");
        self.writeln("postJson: async (url, json) => {");
        self.indent += 1;
        self.writeln("try {");
        self.indent += 1;
        self.writeln("const response = await fetch(url, {");
        self.indent += 1;
        self.writeln("method: 'POST',");
        self.writeln("headers: { 'Content-Type': 'application/json' },");
        self.writeln("body: JSON.stringify(json)");
        self.indent -= 1;
        self.writeln("});");
        self.writeln("const body = await response.text();");
        self.writeln("const headers = [];");
        self.writeln("response.headers.forEach((v, k) => headers.push([k, v]));");
        self.writeln("return Lux.Ok({ status: response.status, body, headers });");
        self.indent -= 1;
        self.writeln("} catch (e) {");
        self.indent += 1;
        self.writeln("return Lux.Err(e.message);");
        self.indent -= 1;
        self.writeln("}");
        self.indent -= 1;
        self.writeln("}");
        self.indent -= 1;
        self.writeln("},");

        // Dom effect (browser only - stubs for Node.js)
        self.writeln("Dom: {");
        self.indent += 1;

        // Query selectors
        self.writeln("querySelector: (selector) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return Lux.None();");
        self.writeln("const el = document.querySelector(selector);");
        self.writeln("return el ? Lux.Some(el) : Lux.None();");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("querySelectorAll: (selector) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return [];");
        self.writeln("return Array.from(document.querySelectorAll(selector));");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getElementById: (id) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return Lux.None();");
        self.writeln("const el = document.getElementById(id);");
        self.writeln("return el ? Lux.Some(el) : Lux.None();");
        self.indent -= 1;
        self.writeln("},");

        // Element creation
        self.writeln("createElement: (tag) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return null;");
        self.writeln("return document.createElement(tag);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("createTextNode: (text) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return null;");
        self.writeln("return document.createTextNode(text);");
        self.indent -= 1;
        self.writeln("},");

        // DOM manipulation
        self.writeln("appendChild: (parent, child) => {");
        self.indent += 1;
        self.writeln("if (parent && child) parent.appendChild(child);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("removeChild: (parent, child) => {");
        self.indent += 1;
        self.writeln("if (parent && child) parent.removeChild(child);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("replaceChild: (parent, newChild, oldChild) => {");
        self.indent += 1;
        self.writeln("if (parent && newChild && oldChild) parent.replaceChild(newChild, oldChild);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("insertBefore: (parent, newNode, refNode) => {");
        self.indent += 1;
        self.writeln("if (parent && newNode) parent.insertBefore(newNode, refNode);");
        self.indent -= 1;
        self.writeln("},");

        // Content
        self.writeln("setTextContent: (el, text) => {");
        self.indent += 1;
        self.writeln("if (el) el.textContent = text;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getTextContent: (el) => {");
        self.indent += 1;
        self.writeln("return el ? el.textContent : '';");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("setInnerHtml: (el, html) => {");
        self.indent += 1;
        self.writeln("if (el) el.innerHTML = html;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getInnerHtml: (el) => {");
        self.indent += 1;
        self.writeln("return el ? el.innerHTML : '';");
        self.indent -= 1;
        self.writeln("},");

        // Attributes
        self.writeln("setAttribute: (el, name, value) => {");
        self.indent += 1;
        self.writeln("if (el) el.setAttribute(name, value);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getAttribute: (el, name) => {");
        self.indent += 1;
        self.writeln("if (!el) return Lux.None();");
        self.writeln("const val = el.getAttribute(name);");
        self.writeln("return val !== null ? Lux.Some(val) : Lux.None();");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("removeAttribute: (el, name) => {");
        self.indent += 1;
        self.writeln("if (el) el.removeAttribute(name);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("hasAttribute: (el, name) => {");
        self.indent += 1;
        self.writeln("return el ? el.hasAttribute(name) : false;");
        self.indent -= 1;
        self.writeln("},");

        // Classes
        self.writeln("addClass: (el, className) => {");
        self.indent += 1;
        self.writeln("if (el) el.classList.add(className);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("removeClass: (el, className) => {");
        self.indent += 1;
        self.writeln("if (el) el.classList.remove(className);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("toggleClass: (el, className) => {");
        self.indent += 1;
        self.writeln("if (el) el.classList.toggle(className);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("hasClass: (el, className) => {");
        self.indent += 1;
        self.writeln("return el ? el.classList.contains(className) : false;");
        self.indent -= 1;
        self.writeln("},");

        // Styles
        self.writeln("setStyle: (el, property, value) => {");
        self.indent += 1;
        self.writeln("if (el) el.style[property] = value;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getStyle: (el, property) => {");
        self.indent += 1;
        self.writeln("return el ? el.style[property] : '';");
        self.indent -= 1;
        self.writeln("},");

        // Form elements
        self.writeln("getValue: (el) => {");
        self.indent += 1;
        self.writeln("return el ? el.value : '';");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("setValue: (el, value) => {");
        self.indent += 1;
        self.writeln("if (el) el.value = value;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("isChecked: (el) => {");
        self.indent += 1;
        self.writeln("return el ? el.checked : false;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("setChecked: (el, checked) => {");
        self.indent += 1;
        self.writeln("if (el) el.checked = checked;");
        self.indent -= 1;
        self.writeln("},");

        // Events
        self.writeln("addEventListener: (el, event, handler) => {");
        self.indent += 1;
        self.writeln("if (el) el.addEventListener(event, handler);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("removeEventListener: (el, event, handler) => {");
        self.indent += 1;
        self.writeln("if (el) el.removeEventListener(event, handler);");
        self.indent -= 1;
        self.writeln("},");

        // Focus
        self.writeln("focus: (el) => {");
        self.indent += 1;
        self.writeln("if (el && el.focus) el.focus();");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("blur: (el) => {");
        self.indent += 1;
        self.writeln("if (el && el.blur) el.blur();");
        self.indent -= 1;
        self.writeln("},");

        // Document
        self.writeln("getBody: () => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return null;");
        self.writeln("return document.body;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getHead: () => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return null;");
        self.writeln("return document.head;");
        self.indent -= 1;
        self.writeln("},");

        // Window
        self.writeln("getWindow: () => {");
        self.indent += 1;
        self.writeln("if (typeof window === 'undefined') return null;");
        self.writeln("return window;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("alert: (msg) => {");
        self.indent += 1;
        self.writeln("if (typeof alert !== 'undefined') alert(msg);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("confirm: (msg) => {");
        self.indent += 1;
        self.writeln("if (typeof confirm !== 'undefined') return confirm(msg);");
        self.writeln("return false;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("prompt: (msg, defaultValue) => {");
        self.indent += 1;
        self.writeln("if (typeof prompt !== 'undefined') {");
        self.indent += 1;
        self.writeln("const result = prompt(msg, defaultValue || '');");
        self.writeln("return result !== null ? Lux.Some(result) : Lux.None();");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("return Lux.None();");
        self.indent -= 1;
        self.writeln("},");

        // Scroll
        self.writeln("scrollTo: (x, y) => {");
        self.indent += 1;
        self.writeln("if (typeof window !== 'undefined') window.scrollTo(x, y);");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("scrollIntoView: (el) => {");
        self.indent += 1;
        self.writeln("if (el && el.scrollIntoView) el.scrollIntoView();");
        self.indent -= 1;
        self.writeln("},");

        // Dimensions
        self.writeln("getBoundingClientRect: (el) => {");
        self.indent += 1;
        self.writeln("if (!el) return { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 };");
        self.writeln("const rect = el.getBoundingClientRect();");
        self.writeln("return { top: rect.top, left: rect.left, width: rect.width, height: rect.height, right: rect.right, bottom: rect.bottom };");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("getWindowSize: () => {");
        self.indent += 1;
        self.writeln("if (typeof window === 'undefined') return { width: 0, height: 0 };");
        self.writeln("return { width: window.innerWidth, height: window.innerHeight };");
        self.indent -= 1;
        self.writeln("}");

        self.indent -= 1;
        self.writeln("}");

        self.indent -= 1;
        self.writeln("},");

        // HTML rendering helpers
        self.writeln("");
        self.writeln("// HTML rendering");
        self.writeln("renderHtml: (node) => {");
        self.indent += 1;
        self.writeln("if (!node) return '';");
        self.writeln("if (typeof node === 'string') return Lux.escapeHtml(node);");
        self.writeln("if (node.tag === 'text') return Lux.escapeHtml(node.content);");
        self.writeln("if (node.tag === 'raw') return node.html;");
        self.writeln("if (node.tag === 'empty') return '';");
        self.writeln("if (node.tag === 'fragment') return node.children.map(Lux.renderHtml).join('');");
        self.writeln("");
        self.writeln("const tag = node.tag;");
        self.writeln("const attrs = (node.attrs || []).filter(a => a.attr).map(a => ` ${a.attr}=\"${Lux.escapeHtml(a.value)}\"`).join('');");
        self.writeln("const events = (node.attrs || []).filter(a => a.event);");
        self.writeln("");
        self.writeln("// Self-closing tags");
        self.writeln("if (['br', 'hr', 'img', 'input', 'meta', 'link'].includes(tag)) {");
        self.indent += 1;
        self.writeln("return `<${tag}${attrs} />`;");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("");
        self.writeln("const children = (node.children || []).map(Lux.renderHtml).join('');");
        self.writeln("return `<${tag}${attrs}>${children}</${tag}>`;");
        self.indent -= 1;
        self.writeln("},");

        self.writeln("");
        self.writeln("// Escape HTML special characters");
        self.writeln("escapeHtml: (str) => {");
        self.indent += 1;
        self.writeln("if (typeof str !== 'string') return String(str);");
        self.writeln("return str");
        self.indent += 1;
        self.writeln(".replace(/&/g, '&amp;')");
        self.writeln(".replace(/</g, '&lt;')");
        self.writeln(".replace(/>/g, '&gt;')");
        self.writeln(".replace(/\"/g, '&quot;')");
        self.writeln(".replace(/'/g, '&#39;');");
        self.indent -= 1;
        self.indent -= 1;
        self.writeln("},");

        self.writeln("");
        self.writeln("// Render HTML to DOM element");
        self.writeln("renderToDom: (node) => {");
        self.indent += 1;
        self.writeln("if (typeof document === 'undefined') return null;");
        self.writeln("if (!node) return null;");
        self.writeln("if (typeof node === 'string') return document.createTextNode(node);");
        self.writeln("if (node.tag === 'text') return document.createTextNode(node.content);");
        self.writeln("if (node.tag === 'raw') {");
        self.indent += 1;
        self.writeln("const wrapper = document.createElement('div');");
        self.writeln("wrapper.innerHTML = node.html;");
        self.writeln("return wrapper.firstChild;");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("if (node.tag === 'empty') return document.createTextNode('');");
        self.writeln("if (node.tag === 'fragment') {");
        self.indent += 1;
        self.writeln("const frag = document.createDocumentFragment();");
        self.writeln("for (const child of node.children) {");
        self.indent += 1;
        self.writeln("const el = Lux.renderToDom(child);");
        self.writeln("if (el) frag.appendChild(el);");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("return frag;");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("");
        self.writeln("const el = document.createElement(node.tag);");
        self.writeln("");
        self.writeln("// Set attributes");
        self.writeln("for (const attr of (node.attrs || [])) {");
        self.indent += 1;
        self.writeln("if (attr.attr) {");
        self.indent += 1;
        self.writeln("el.setAttribute(attr.attr, attr.value);");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("if (attr.event && attr.handler) {");
        self.indent += 1;
        self.writeln("el.addEventListener(attr.event, attr.handler);");
        self.indent -= 1;
        self.writeln("}");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("");
        self.writeln("// Append children");
        self.writeln("for (const child of (node.children || [])) {");
        self.indent += 1;
        self.writeln("const childEl = Lux.renderToDom(child);");
        self.writeln("if (childEl) el.appendChild(childEl);");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("");
        self.writeln("return el;");
        self.indent -= 1;
        self.writeln("},");

        // TEA (The Elm Architecture) runtime
        self.writeln("");
        self.writeln("// The Elm Architecture (TEA) runtime");
        self.writeln("app: (config) => {");
        self.indent += 1;
        self.writeln("const { init, update, view, root } = config;");
        self.writeln("let model = init;");
        self.writeln("let rootEl = typeof root === 'string' ? document.querySelector(root) : root;");
        self.writeln("let currentVdom = null;");
        self.writeln("let currentDom = null;");
        self.writeln("");
        self.writeln("const dispatch = (msg) => {");
        self.indent += 1;
        self.writeln("model = update(model, msg);");
        self.writeln("render();");
        self.indent -= 1;
        self.writeln("};");
        self.writeln("");
        self.writeln("const render = () => {");
        self.indent += 1;
        self.writeln("const newVdom = view(model, dispatch);");
        self.writeln("const newDom = Lux.renderToDom(newVdom);");
        self.writeln("if (currentDom) {");
        self.indent += 1;
        self.writeln("rootEl.replaceChild(newDom, currentDom);");
        self.indent -= 1;
        self.writeln("} else {");
        self.indent += 1;
        self.writeln("rootEl.innerHTML = '';");
        self.writeln("rootEl.appendChild(newDom);");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("currentVdom = newVdom;");
        self.writeln("currentDom = newDom;");
        self.indent -= 1;
        self.writeln("};");
        self.writeln("");
        self.writeln("// Initial render");
        self.writeln("if (rootEl) render();");
        self.writeln("");
        self.writeln("return { dispatch, getModel: () => model };");
        self.indent -= 1;
        self.writeln("},");

        // Simple app (for string-based views like the counter example)
        self.writeln("");
        self.writeln("// Simple TEA app (string-based view)");
        self.writeln("simpleApp: (config) => {");
        self.indent += 1;
        self.writeln("const { init, update, view, root } = config;");
        self.writeln("let model = init;");
        self.writeln("let rootEl = typeof root === 'string' ? document.querySelector(root) : root;");
        self.writeln("");
        self.writeln("const dispatch = (msg) => {");
        self.indent += 1;
        self.writeln("model = update(model, msg);");
        self.writeln("render();");
        self.indent -= 1;
        self.writeln("};");
        self.writeln("");
        self.writeln("// Make dispatch globally available for onclick handlers");
        self.writeln("window.dispatch = dispatch;");
        self.writeln("");
        self.writeln("const render = () => {");
        self.indent += 1;
        self.writeln("if (rootEl) rootEl.innerHTML = view(model);");
        self.indent -= 1;
        self.writeln("};");
        self.writeln("");
        self.writeln("if (rootEl) render();");
        self.writeln("return { dispatch, getModel: () => model };");
        self.indent -= 1;
        self.writeln("},");

        // Diff and patch (basic implementation for view_deps optimization)
        self.writeln("");
        self.writeln("// Basic diff - checks if model fields changed");
        self.writeln("hasChanged: (oldModel, newModel, ...paths) => {");
        self.indent += 1;
        self.writeln("for (const path of paths) {");
        self.indent += 1;
        self.writeln("const parts = path.split('.');");
        self.writeln("let oldVal = oldModel, newVal = newModel;");
        self.writeln("for (const part of parts) {");
        self.indent += 1;
        self.writeln("oldVal = oldVal?.[part];");
        self.writeln("newVal = newVal?.[part];");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("if (oldVal !== newVal) return true;");
        self.indent -= 1;
        self.writeln("}");
        self.writeln("return false;");
        self.indent -= 1;
        self.writeln("}");

        self.indent -= 1;
        self.writeln("};");
        self.writeln("");
    }

    /// Collect type information from a type declaration
    fn collect_type(&mut self, decl: &TypeDecl) -> Result<(), JsGenError> {
        if let TypeDef::Enum(variants) = &decl.definition {
            for variant in variants {
                self.variant_to_type
                    .insert(variant.name.name.clone(), decl.name.name.clone());

                // Store field types
                let field_types: Vec<String> = match &variant.fields {
                    VariantFields::Unit => vec![],
                    VariantFields::Tuple(types) => {
                        types.iter().map(|_| "any".to_string()).collect()
                    }
                    VariantFields::Record(fields) => {
                        fields.iter().map(|_| "any".to_string()).collect()
                    }
                };
                self.variant_field_types.insert(
                    (decl.name.name.clone(), variant.name.name.clone()),
                    field_types,
                );
            }
        }
        Ok(())
    }

    /// Emit constructor functions for ADT variants
    fn emit_type_constructors(&mut self, decl: &TypeDecl) -> Result<(), JsGenError> {
        if let TypeDef::Enum(variants) = &decl.definition {
            self.writeln(&format!("// {} constructors", decl.name.name));

            for variant in variants {
                match &variant.fields {
                    VariantFields::Unit => {
                        // Unit variant: const None_lux = { tag: "None" };
                        self.writeln(&format!(
                            "const {}_lux = {{ tag: \"{}\" }};",
                            variant.name.name, variant.name.name
                        ));
                    }
                    VariantFields::Tuple(types) => {
                        // Tuple variant: function Some_lux(value) { return { tag: "Some", field0: value }; }
                        let params: Vec<String> = (0..types.len())
                            .map(|i| format!("field{}", i))
                            .collect();
                        let fields: Vec<String> = params
                            .iter()
                            .enumerate()
                            .map(|(i, p)| format!("field{}: {}", i, p))
                            .collect();
                        self.writeln(&format!(
                            "function {}_lux({}) {{ return {{ tag: \"{}\", {} }}; }}",
                            variant.name.name,
                            params.join(", "),
                            variant.name.name,
                            fields.join(", ")
                        ));
                    }
                    VariantFields::Record(fields) => {
                        // Record variant: function Foo_lux(a, b) { return { tag: "Foo", a, b }; }
                        let params: Vec<String> =
                            fields.iter().map(|f| f.name.name.clone()).collect();
                        let field_inits: Vec<String> =
                            params.iter().map(|p| format!("{}: {}", p, p)).collect();
                        self.writeln(&format!(
                            "function {}_lux({}) {{ return {{ tag: \"{}\", {} }}; }}",
                            variant.name.name,
                            params.join(", "),
                            variant.name.name,
                            field_inits.join(", ")
                        ));
                    }
                }
            }
            self.writeln("");
        }
        Ok(())
    }

    /// Emit a function declaration
    fn emit_function(&mut self, func: &FunctionDecl) -> Result<(), JsGenError> {
        let func_name = self.mangle_name(&func.name.name);
        let is_effectful = !func.effects.is_empty();

        // Build parameter list
        let mut params: Vec<String> = func.params.iter().map(|p| p.name.name.clone()).collect();

        // Effectful functions get handlers as first parameter
        if is_effectful {
            params.insert(0, "handlers".to_string());
        }

        // Function declaration
        self.writeln(&format!(
            "function {}({}) {{",
            func_name,
            params.join(", ")
        ));
        self.indent += 1;

        // Set context for effect handling
        let prev_has_handlers = self.has_handlers;
        self.has_handlers = is_effectful;

        // Clear var substitutions for this function
        self.var_substitutions.clear();

        // Emit function body
        let body_code = self.emit_expr(&func.body)?;
        self.writeln(&format!("return {};", body_code));

        self.has_handlers = prev_has_handlers;

        self.indent -= 1;
        self.writeln("}");
        self.writeln("");

        Ok(())
    }

    /// Emit a top-level let binding
    fn emit_top_level_let(&mut self, let_decl: &LetDecl) -> Result<(), JsGenError> {
        let val = self.emit_expr(&let_decl.value)?;
        let var_name = &let_decl.name.name;

        // Check if this is a run expression (often results in undefined)
        // We still want to execute it for its side effects
        self.writeln(&format!("const {} = {};", var_name, val));

        // Register the variable for future use
        self.var_substitutions
            .insert(var_name.clone(), var_name.clone());

        Ok(())
    }

    /// Emit an expression and return the JavaScript code
    fn emit_expr(&mut self, expr: &Expr) -> Result<String, JsGenError> {
        match expr {
            Expr::Literal(lit) => self.emit_literal(lit),

            Expr::Var(ident) => {
                // Check for variable substitution
                if let Some(subst) = self.var_substitutions.get(&ident.name) {
                    return Ok(subst.clone());
                }

                // Check if this is a unit constructor
                if let Some(type_name) = self.variant_to_type.get(&ident.name) {
                    // Use Lux.* for built-in types (Option, Result)
                    if type_name == "Option" || type_name == "Result" {
                        Ok(format!("Lux.{}()", ident.name))
                    } else {
                        Ok(format!("{}_lux", ident.name))
                    }
                } else if self.functions.contains(&ident.name) {
                    // Function reference (used as value)
                    Ok(self.mangle_name(&ident.name))
                } else {
                    Ok(self.escape_js_keyword(&ident.name))
                }
            }

            Expr::BinaryOp {
                op, left, right, ..
            } => {
                let l = self.emit_expr(left)?;
                let r = self.emit_expr(right)?;

                // Check for string concatenation
                if matches!(op, BinaryOp::Add) {
                    if self.is_string_expr(left) || self.is_string_expr(right) {
                        return Ok(format!("({} + {})", l, r));
                    }
                }

                let op_str = match op {
                    BinaryOp::Add => "+",
                    BinaryOp::Sub => "-",
                    BinaryOp::Mul => "*",
                    BinaryOp::Div => "/",
                    BinaryOp::Mod => "%",
                    BinaryOp::Eq => "===",
                    BinaryOp::Ne => "!==",
                    BinaryOp::Lt => "<",
                    BinaryOp::Le => "<=",
                    BinaryOp::Gt => ">",
                    BinaryOp::Ge => ">=",
                    BinaryOp::And => "&&",
                    BinaryOp::Or => "||",
                    BinaryOp::Pipe => {
                        // Pipe operator: x |> f becomes f(x)
                        return Ok(format!("{}({})", r, l));
                    }
                };

                Ok(format!("({} {} {})", l, op_str, r))
            }

            Expr::UnaryOp { op, operand, .. } => {
                let val = self.emit_expr(operand)?;
                let op_str = match op {
                    UnaryOp::Neg => "-",
                    UnaryOp::Not => "!",
                };
                Ok(format!("({}{})", op_str, val))
            }

            Expr::If {
                condition,
                then_branch,
                else_branch,
                ..
            } => {
                // Check if branches contain statements that need if-else instead of ternary
                let needs_block = self.expr_has_statements(then_branch)
                    || self.expr_has_statements(else_branch);

                if needs_block {
                    // Use if-else statement for branches with statements
                    let result_var = format!("_if_result_{}", self.fresh_name());
                    let cond = self.emit_expr(condition)?;

                    self.writeln(&format!("let {};", result_var));
                    self.writeln(&format!("if ({}) {{", cond));
                    self.indent += 1;
                    let then_val = self.emit_expr(then_branch)?;
                    self.writeln(&format!("{} = {};", result_var, then_val));
                    self.indent -= 1;
                    self.writeln("} else {");
                    self.indent += 1;
                    let else_val = self.emit_expr(else_branch)?;
                    self.writeln(&format!("{} = {};", result_var, else_val));
                    self.indent -= 1;
                    self.writeln("}");

                    Ok(result_var)
                } else {
                    // Simple ternary for pure expressions
                    let cond = self.emit_expr(condition)?;
                    let then_val = self.emit_expr(then_branch)?;
                    let else_val = self.emit_expr(else_branch)?;
                    Ok(format!("({} ? {} : {})", cond, then_val, else_val))
                }
            }

            Expr::Let {
                name, value, body, ..
            } => {
                let val = self.emit_expr(value)?;
                let var_name = format!("{}_{}", name.name, self.fresh_name());

                self.writeln(&format!("const {} = {};", var_name, val));

                // Add substitution
                self.var_substitutions
                    .insert(name.name.clone(), var_name.clone());

                let body_result = self.emit_expr(body)?;

                // Remove substitution
                self.var_substitutions.remove(&name.name);

                Ok(body_result)
            }

            Expr::Call { func, args, .. } => {
                // Check for List module calls
                if let Expr::Field { object, field, .. } = func.as_ref() {
                    if let Expr::Var(module_name) = object.as_ref() {
                        if module_name.name == "List" {
                            return self.emit_list_operation(&field.name, args);
                        }
                    }
                }

                // Check for built-in functions
                if let Expr::Var(ident) = func.as_ref() {
                    if ident.name == "toString" {
                        let arg = self.emit_expr(&args[0])?;
                        return Ok(format!("String({})", arg));
                    }
                }

                let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
                let args_str = arg_strs?.join(", ");

                match func.as_ref() {
                    Expr::Var(ident) if self.functions.contains(&ident.name) => {
                        let js_func_name = self.mangle_name(&ident.name);
                        let is_effectful = self.effectful_functions.contains(&ident.name);

                        if is_effectful && self.has_handlers {
                            // Use the handlers variable from substitutions, or default "handlers"
                            let handlers_name = self
                                .var_substitutions
                                .get("handlers")
                                .cloned()
                                .unwrap_or_else(|| "handlers".to_string());
                            if args_str.is_empty() {
                                Ok(format!("{}({})", js_func_name, handlers_name))
                            } else {
                                Ok(format!("{}({}, {})", js_func_name, handlers_name, args_str))
                            }
                        } else if is_effectful {
                            if args_str.is_empty() {
                                Ok(format!("{}(Lux.defaultHandlers)", js_func_name))
                            } else {
                                Ok(format!("{}(Lux.defaultHandlers, {})", js_func_name, args_str))
                            }
                        } else {
                            Ok(format!("{}({})", js_func_name, args_str))
                        }
                    }
                    Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => {
                        // ADT constructor call
                        // Use Lux.* for built-in types (Option, Result)
                        let type_name = self.variant_to_type.get(&ident.name).unwrap();
                        if type_name == "Option" || type_name == "Result" {
                            Ok(format!("Lux.{}({})", ident.name, args_str))
                        } else {
                            Ok(format!("{}_lux({})", ident.name, args_str))
                        }
                    }
                    _ => {
                        // Generic function call
                        let func_code = self.emit_expr(func)?;
                        Ok(format!("{}({})", func_code, args_str))
                    }
                }
            }

            Expr::EffectOp {
                effect,
                operation,
                args,
                ..
            } => {
                // Special case: List module operations (not an effect)
                if effect.name == "List" {
                    return self.emit_list_operation(&operation.name, args);
                }

                // Special case: String module operations (not an effect)
                if effect.name == "String" {
                    return self.emit_string_operation(&operation.name, args);
                }

                // Special case: Option module operations (not an effect)
                if effect.name == "Option" {
                    return self.emit_option_operation(&operation.name, args);
                }

                // Special case: Math module operations (not an effect)
                if effect.name == "Math" {
                    return self.emit_math_operation(&operation.name, args);
                }

                // Special case: Result module operations (not an effect)
                if effect.name == "Result" {
                    return self.emit_result_operation(&operation.name, args);
                }

                // Special case: Json module operations (not an effect)
                if effect.name == "Json" {
                    return self.emit_json_operation(&operation.name, args);
                }

                // Special case: Html module operations (not an effect)
                if effect.name == "Html" {
                    return self.emit_html_operation(&operation.name, args);
                }

                let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
                let args_str = arg_strs?.join(", ");

                if self.has_handlers {
                    // Use the handlers variable from substitutions, or default "handlers"
                    let handlers_name = self
                        .var_substitutions
                        .get("handlers")
                        .cloned()
                        .unwrap_or_else(|| "handlers".to_string());
                    Ok(format!(
                        "{}.{}.{}({})",
                        handlers_name, effect.name, operation.name, args_str
                    ))
                } else {
                    Ok(format!(
                        "Lux.defaultHandlers.{}.{}({})",
                        effect.name, operation.name, args_str
                    ))
                }
            }

            Expr::Lambda {
                params,
                body,
                effects,
                ..
            } => {
                let param_names: Vec<String> =
                    params.iter().map(|p| p.name.name.clone()).collect();

                // If lambda has effects, it takes handlers
                let all_params = if !effects.is_empty() {
                    let mut p = vec!["handlers".to_string()];
                    p.extend(param_names);
                    p
                } else {
                    param_names
                };

                // Save handler state
                let prev_has_handlers = self.has_handlers;
                self.has_handlers = !effects.is_empty();

                let body_code = self.emit_expr(body)?;

                self.has_handlers = prev_has_handlers;

                Ok(format!(
                    "(function({}) {{ return {}; }})",
                    all_params.join(", "),
                    body_code
                ))
            }

            Expr::Match {
                scrutinee, arms, ..
            } => self.emit_match(scrutinee, arms),

            Expr::Block {
                statements, result, ..
            } => {
                // Emit each statement
                for stmt in statements {
                    match stmt {
                        Statement::Expr(expr) => {
                            let code = self.emit_expr(expr)?;
                            self.writeln(&format!("{};", code));
                        }
                        Statement::Let { name, value, .. } => {
                            let val = self.emit_expr(value)?;
                            let var_name = format!("{}_{}", name.name, self.fresh_name());
                            self.writeln(&format!("const {} = {};", var_name, val));
                            self.var_substitutions
                                .insert(name.name.clone(), var_name.clone());
                        }
                    }
                }

                // Emit result
                self.emit_expr(result)
            }

            Expr::Record { fields, .. } => {
                let field_strs: Result<Vec<_>, _> = fields
                    .iter()
                    .map(|(name, expr)| {
                        let val = self.emit_expr(expr)?;
                        Ok(format!("{}: {}", name.name, val))
                    })
                    .collect();
                Ok(format!("{{ {} }}", field_strs?.join(", ")))
            }

            Expr::Tuple { elements, .. } => {
                let elem_strs: Result<Vec<_>, _> =
                    elements.iter().map(|e| self.emit_expr(e)).collect();
                Ok(format!("[{}]", elem_strs?.join(", ")))
            }

            Expr::List { elements, .. } => {
                let elem_strs: Result<Vec<_>, _> =
                    elements.iter().map(|e| self.emit_expr(e)).collect();
                Ok(format!("[{}]", elem_strs?.join(", ")))
            }

            Expr::Field { object, field, .. } => {
                let obj = self.emit_expr(object)?;
                Ok(format!("{}.{}", obj, field.name))
            }

            Expr::Run {
                expr, handlers, ..
            } => {
                // Create handler object, merging with defaults
                let handlers_var = format!("_handlers_{}", self.fresh_name());

                if handlers.is_empty() {
                    // No custom handlers, use defaults
                    self.writeln(&format!(
                        "const {} = Lux.defaultHandlers;",
                        handlers_var
                    ));
                } else {
                    // Merge custom handlers with defaults
                    let handler_strs: Result<Vec<_>, _> = handlers
                        .iter()
                        .map(|(name, handler_expr)| {
                            let handler_code = self.emit_expr(handler_expr)?;
                            Ok(format!("{}: {}", name.name, handler_code))
                        })
                        .collect();
                    let custom_handlers = handler_strs?.join(", ");
                    self.writeln(&format!(
                        "const {} = {{ ...Lux.defaultHandlers, {} }};",
                        handlers_var, custom_handlers
                    ));
                }

                // Set has_handlers and emit the expression
                let prev_has_handlers = self.has_handlers;
                self.has_handlers = true;

                // For function calls inside run, we need to pass the handlers
                // Save the current handlers variable name for use in calls
                let saved_substitutions = self.var_substitutions.clone();
                self.var_substitutions
                    .insert("handlers".to_string(), handlers_var.clone());

                // Emit the expression with the custom handlers
                let inner_code = self.emit_expr(expr)?;

                self.var_substitutions = saved_substitutions;
                self.has_handlers = prev_has_handlers;

                Ok(inner_code)
            }

            Expr::Resume { value, .. } => {
                let val = self.emit_expr(value)?;
                Ok(format!("resume({})", val))
            }
        }
    }

    /// Emit a match expression
    fn emit_match(&mut self, scrutinee: &Expr, arms: &[MatchArm]) -> Result<String, JsGenError> {
        let scrutinee_code = self.emit_expr(scrutinee)?;
        let scrutinee_var = format!("_match_{}", self.fresh_name());
        let result_var = format!("_result_{}", self.fresh_name());

        self.writeln(&format!("const {} = {};", scrutinee_var, scrutinee_code));
        self.writeln(&format!("let {};", result_var));

        for (i, arm) in arms.iter().enumerate() {
            let condition = self.emit_pattern_condition(&arm.pattern, &scrutinee_var)?;
            let bindings = self.emit_pattern_bindings(&arm.pattern, &scrutinee_var)?;

            if i == 0 {
                self.writeln(&format!("if ({}) {{", condition));
            } else {
                self.writeln(&format!("}} else if ({}) {{", condition));
            }

            self.indent += 1;

            // Emit bindings
            for (name, code) in &bindings {
                self.writeln(&format!("const {} = {};", name, code));
                self.var_substitutions.insert(name.clone(), name.clone());
            }

            // Emit guard if present
            if let Some(guard) = &arm.guard {
                let guard_code = self.emit_expr(guard)?;
                self.writeln(&format!("if ({}) {{", guard_code));
                self.indent += 1;
            }

            // Emit body
            let body_code = self.emit_expr(&arm.body)?;
            self.writeln(&format!("{} = {};", result_var, body_code));

            if arm.guard.is_some() {
                self.indent -= 1;
                self.writeln("}");
            }

            // Remove bindings
            for (name, _) in &bindings {
                self.var_substitutions.remove(name);
            }

            self.indent -= 1;
        }

        self.writeln("} else {");
        self.indent += 1;
        self.writeln(&format!(
            "throw new Error('Non-exhaustive match on ' + JSON.stringify({}));",
            scrutinee_var
        ));
        self.indent -= 1;
        self.writeln("}");

        Ok(result_var)
    }

    /// Emit a condition that checks if a pattern matches
    fn emit_pattern_condition(
        &self,
        pattern: &Pattern,
        scrutinee: &str,
    ) -> Result<String, JsGenError> {
        match pattern {
            Pattern::Wildcard(_) => Ok("true".to_string()),
            Pattern::Var(_) => Ok("true".to_string()),
            Pattern::Literal(lit) => {
                let lit_code = self.emit_literal(lit)?;
                Ok(format!("{} === {}", scrutinee, lit_code))
            }
            Pattern::Constructor { name, fields, .. } => {
                let mut conditions = vec![format!("{}.tag === \"{}\"", scrutinee, name.name)];

                for (i, field_pattern) in fields.iter().enumerate() {
                    // Use named fields for built-in types
                    let field_access = match (name.name.as_str(), i) {
                        ("Some", 0) => format!("{}.value", scrutinee),
                        ("Ok", 0) => format!("{}.value", scrutinee),
                        ("Err", 0) => format!("{}.error", scrutinee),
                        _ => format!("{}.field{}", scrutinee, i),
                    };
                    let field_cond = self.emit_pattern_condition(field_pattern, &field_access)?;
                    if field_cond != "true" {
                        conditions.push(field_cond);
                    }
                }

                Ok(conditions.join(" && "))
            }
            Pattern::Record { fields, .. } => {
                let mut conditions = vec![];
                for (field_name, field_pattern) in fields {
                    let field_access = format!("{}.{}", scrutinee, field_name.name);
                    let field_cond = self.emit_pattern_condition(field_pattern, &field_access)?;
                    if field_cond != "true" {
                        conditions.push(field_cond);
                    }
                }
                if conditions.is_empty() {
                    Ok("true".to_string())
                } else {
                    Ok(conditions.join(" && "))
                }
            }
            Pattern::Tuple { elements, .. } => {
                let mut conditions = vec![];
                for (i, elem_pattern) in elements.iter().enumerate() {
                    let elem_access = format!("{}[{}]", scrutinee, i);
                    let elem_cond = self.emit_pattern_condition(elem_pattern, &elem_access)?;
                    if elem_cond != "true" {
                        conditions.push(elem_cond);
                    }
                }
                if conditions.is_empty() {
                    Ok("true".to_string())
                } else {
                    Ok(conditions.join(" && "))
                }
            }
        }
    }

    /// Extract variable bindings from a pattern
    fn emit_pattern_bindings(
        &self,
        pattern: &Pattern,
        scrutinee: &str,
    ) -> Result<Vec<(String, String)>, JsGenError> {
        let mut bindings = vec![];

        match pattern {
            Pattern::Wildcard(_) => {}
            Pattern::Var(ident) => {
                bindings.push((ident.name.clone(), scrutinee.to_string()));
            }
            Pattern::Literal(_) => {}
            Pattern::Constructor { name, fields, .. } => {
                for (i, field_pattern) in fields.iter().enumerate() {
                    // Use named fields for built-in types
                    let field_access = match (name.name.as_str(), i) {
                        ("Some", 0) => format!("{}.value", scrutinee),
                        ("Ok", 0) => format!("{}.value", scrutinee),
                        ("Err", 0) => format!("{}.error", scrutinee),
                        _ => format!("{}.field{}", scrutinee, i),
                    };
                    let field_bindings = self.emit_pattern_bindings(field_pattern, &field_access)?;
                    bindings.extend(field_bindings);
                }
            }
            Pattern::Record { fields, .. } => {
                for (field_name, field_pattern) in fields {
                    let field_access = format!("{}.{}", scrutinee, field_name.name);
                    let field_bindings =
                        self.emit_pattern_bindings(field_pattern, &field_access)?;
                    bindings.extend(field_bindings);
                }
            }
            Pattern::Tuple { elements, .. } => {
                for (i, elem_pattern) in elements.iter().enumerate() {
                    let elem_access = format!("{}[{}]", scrutinee, i);
                    let elem_bindings = self.emit_pattern_bindings(elem_pattern, &elem_access)?;
                    bindings.extend(elem_bindings);
                }
            }
        }

        Ok(bindings)
    }

    /// Emit List module operations
    fn emit_list_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            "map" => {
                let list = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!("{}.map({})", list, func))
            }
            "filter" => {
                let list = self.emit_expr(&args[0])?;
                let pred = self.emit_expr(&args[1])?;
                Ok(format!("{}.filter({})", list, pred))
            }
            "foldl" | "fold" => {
                let list = self.emit_expr(&args[0])?;
                let init = self.emit_expr(&args[1])?;
                let func = self.emit_expr(&args[2])?;
                Ok(format!("{}.reduce({}, {})", list, func, init))
            }
            "foldr" => {
                let list = self.emit_expr(&args[0])?;
                let init = self.emit_expr(&args[1])?;
                let func = self.emit_expr(&args[2])?;
                Ok(format!("{}.reduceRight({}, {})", list, func, init))
            }
            "length" => {
                let list = self.emit_expr(&args[0])?;
                Ok(format!("{}.length", list))
            }
            "head" => {
                let list = self.emit_expr(&args[0])?;
                Ok(format!(
                    "({}.length > 0 ? Lux.Some({}[0]) : Lux.None())",
                    list, list
                ))
            }
            "tail" => {
                let list = self.emit_expr(&args[0])?;
                Ok(format!(
                    "({}.length > 0 ? Lux.Some({}.slice(1)) : Lux.None())",
                    list, list
                ))
            }
            "isEmpty" => {
                let list = self.emit_expr(&args[0])?;
                Ok(format!("{}.length === 0", list))
            }
            "append" => {
                let list1 = self.emit_expr(&args[0])?;
                let list2 = self.emit_expr(&args[1])?;
                Ok(format!("[...{}, ...{}]", list1, list2))
            }
            "reverse" => {
                let list = self.emit_expr(&args[0])?;
                Ok(format!("[...{}].reverse()", list))
            }
            "range" => {
                let start = self.emit_expr(&args[0])?;
                let end = self.emit_expr(&args[1])?;
                Ok(format!(
                    "Array.from({{ length: {} - {} + 1 }}, (_, i) => {} + i)",
                    end, start, start
                ))
            }
            _ => Err(JsGenError {
                message: format!("Unknown List operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit String module operations (not effects)
    fn emit_string_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            "length" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.length", s))
            }
            "concat" => {
                let s1 = self.emit_expr(&args[0])?;
                let s2 = self.emit_expr(&args[1])?;
                Ok(format!("({} + {})", s1, s2))
            }
            "substring" | "slice" => {
                let s = self.emit_expr(&args[0])?;
                let start = self.emit_expr(&args[1])?;
                let end = self.emit_expr(&args[2])?;
                Ok(format!("{}.slice({}, {})", s, start, end))
            }
            "charAt" => {
                let s = self.emit_expr(&args[0])?;
                let idx = self.emit_expr(&args[1])?;
                Ok(format!("{}.charAt({})", s, idx))
            }
            "indexOf" => {
                let s = self.emit_expr(&args[0])?;
                let sub = self.emit_expr(&args[1])?;
                Ok(format!("{}.indexOf({})", s, sub))
            }
            "contains" => {
                let s = self.emit_expr(&args[0])?;
                let sub = self.emit_expr(&args[1])?;
                Ok(format!("{}.includes({})", s, sub))
            }
            "startsWith" => {
                let s = self.emit_expr(&args[0])?;
                let prefix = self.emit_expr(&args[1])?;
                Ok(format!("{}.startsWith({})", s, prefix))
            }
            "endsWith" => {
                let s = self.emit_expr(&args[0])?;
                let suffix = self.emit_expr(&args[1])?;
                Ok(format!("{}.endsWith({})", s, suffix))
            }
            "toUpperCase" | "upper" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.toUpperCase()", s))
            }
            "toLowerCase" | "lower" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.toLowerCase()", s))
            }
            "trim" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.trim()", s))
            }
            "split" => {
                let s = self.emit_expr(&args[0])?;
                let delim = self.emit_expr(&args[1])?;
                Ok(format!("{}.split({})", s, delim))
            }
            "join" => {
                let list = self.emit_expr(&args[0])?;
                let sep = self.emit_expr(&args[1])?;
                Ok(format!("{}.join({})", list, sep))
            }
            "replace" => {
                let s = self.emit_expr(&args[0])?;
                let from = self.emit_expr(&args[1])?;
                let to = self.emit_expr(&args[2])?;
                Ok(format!("{}.replace({}, {})", s, from, to))
            }
            "replaceAll" => {
                let s = self.emit_expr(&args[0])?;
                let from = self.emit_expr(&args[1])?;
                let to = self.emit_expr(&args[2])?;
                Ok(format!("{}.replaceAll({}, {})", s, from, to))
            }
            "repeat" => {
                let s = self.emit_expr(&args[0])?;
                let n = self.emit_expr(&args[1])?;
                Ok(format!("{}.repeat({})", s, n))
            }
            "isEmpty" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.length === 0", s))
            }
            "chars" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("[...{}]", s))
            }
            "lines" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!("{}.split(\"\\n\")", s))
            }
            _ => Err(JsGenError {
                message: format!("Unknown String operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit Option module operations (not effects)
    fn emit_option_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            "isSome" => {
                let opt = self.emit_expr(&args[0])?;
                Ok(format!("({}.tag === \"Some\")", opt))
            }
            "isNone" => {
                let opt = self.emit_expr(&args[0])?;
                Ok(format!("({}.tag === \"None\")", opt))
            }
            "unwrap" => {
                let opt = self.emit_expr(&args[0])?;
                Ok(format!("{}.value", opt))
            }
            "unwrapOr" | "getOrElse" => {
                let opt = self.emit_expr(&args[0])?;
                let default = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Some\" ? {}.value : {})",
                    opt, opt, default
                ))
            }
            "map" => {
                let opt = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Some\" ? Lux.Some({}({}.value)) : Lux.None())",
                    opt, func, opt
                ))
            }
            "flatMap" | "andThen" => {
                let opt = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Some\" ? {}({}.value) : Lux.None())",
                    opt, func, opt
                ))
            }
            _ => Err(JsGenError {
                message: format!("Unknown Option operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit Math module operations
    fn emit_math_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            // Constants
            "pi" => Ok("Math.PI".to_string()),
            "e" => Ok("Math.E".to_string()),
            "infinity" => Ok("Infinity".to_string()),
            "negInfinity" => Ok("-Infinity".to_string()),
            "nan" => Ok("NaN".to_string()),

            // Basic operations
            "abs" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.abs({})", x))
            }
            "floor" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.floor({})", x))
            }
            "ceil" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.ceil({})", x))
            }
            "round" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.round({})", x))
            }
            "trunc" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.trunc({})", x))
            }
            "sign" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.sign({})", x))
            }

            // Power and roots
            "pow" => {
                let base = self.emit_expr(&args[0])?;
                let exp = self.emit_expr(&args[1])?;
                Ok(format!("Math.pow({}, {})", base, exp))
            }
            "sqrt" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.sqrt({})", x))
            }
            "cbrt" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.cbrt({})", x))
            }
            "exp" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.exp({})", x))
            }
            "log" | "ln" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.log({})", x))
            }
            "log10" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.log10({})", x))
            }
            "log2" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.log2({})", x))
            }

            // Trigonometry
            "sin" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.sin({})", x))
            }
            "cos" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.cos({})", x))
            }
            "tan" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.tan({})", x))
            }
            "asin" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.asin({})", x))
            }
            "acos" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.acos({})", x))
            }
            "atan" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.atan({})", x))
            }
            "atan2" => {
                let y = self.emit_expr(&args[0])?;
                let x = self.emit_expr(&args[1])?;
                Ok(format!("Math.atan2({}, {})", y, x))
            }
            "sinh" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.sinh({})", x))
            }
            "cosh" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.cosh({})", x))
            }
            "tanh" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.tanh({})", x))
            }

            // Min/max
            "min" => {
                let a = self.emit_expr(&args[0])?;
                let b = self.emit_expr(&args[1])?;
                Ok(format!("Math.min({}, {})", a, b))
            }
            "max" => {
                let a = self.emit_expr(&args[0])?;
                let b = self.emit_expr(&args[1])?;
                Ok(format!("Math.max({}, {})", a, b))
            }
            "clamp" => {
                let x = self.emit_expr(&args[0])?;
                let min = self.emit_expr(&args[1])?;
                let max = self.emit_expr(&args[2])?;
                Ok(format!("Math.min(Math.max({}, {}), {})", x, min, max))
            }

            // Checks
            "isNaN" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Number.isNaN({})", x))
            }
            "isFinite" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Number.isFinite({})", x))
            }
            "isInfinite" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("(!Number.isFinite({}) && !Number.isNaN({}))", x, x))
            }

            // Conversion
            "toInt" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Math.trunc({})", x))
            }
            "toFloat" => {
                let x = self.emit_expr(&args[0])?;
                Ok(format!("Number({})", x))
            }

            _ => Err(JsGenError {
                message: format!("Unknown Math operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit Result module operations
    fn emit_result_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            "isOk" => {
                let result = self.emit_expr(&args[0])?;
                Ok(format!("({}.tag === \"Ok\")", result))
            }
            "isErr" => {
                let result = self.emit_expr(&args[0])?;
                Ok(format!("({}.tag === \"Err\")", result))
            }
            "unwrap" => {
                let result = self.emit_expr(&args[0])?;
                Ok(format!("{}.value", result))
            }
            "unwrapErr" => {
                let result = self.emit_expr(&args[0])?;
                Ok(format!("{}.error", result))
            }
            "unwrapOr" => {
                let result = self.emit_expr(&args[0])?;
                let default = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Ok\" ? {}.value : {})",
                    result, result, default
                ))
            }
            "map" => {
                let result = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Ok\" ? Lux.Ok({}({}.value)) : {})",
                    result, func, result, result
                ))
            }
            "mapErr" => {
                let result = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Err\" ? Lux.Err({}({}.error)) : {})",
                    result, func, result, result
                ))
            }
            "flatMap" | "andThen" => {
                let result = self.emit_expr(&args[0])?;
                let func = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}.tag === \"Ok\" ? {}({}.value) : {})",
                    result, func, result, result
                ))
            }
            "toOption" => {
                let result = self.emit_expr(&args[0])?;
                Ok(format!(
                    "({}.tag === \"Ok\" ? Lux.Some({}.value) : Lux.None())",
                    result, result
                ))
            }
            _ => Err(JsGenError {
                message: format!("Unknown Result operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit Json module operations
    fn emit_json_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            // Parse JSON string to value (returns Result)
            "parse" => {
                let s = self.emit_expr(&args[0])?;
                Ok(format!(
                    "(function() {{ try {{ return Lux.Ok(JSON.parse({})); }} catch(e) {{ return Lux.Err(e.message); }} }})()",
                    s
                ))
            }
            // Stringify value to JSON string
            "stringify" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!("JSON.stringify({})", v))
            }
            // Pretty print with indentation
            "prettyPrint" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!("JSON.stringify({}, null, 2)", v))
            }
            // Get a field from a JSON object (returns Option)
            "get" => {
                let obj = self.emit_expr(&args[0])?;
                let key = self.emit_expr(&args[1])?;
                Ok(format!(
                    "({}[{}] !== undefined ? Lux.Some({}[{}]) : Lux.None())",
                    obj, key, obj, key
                ))
            }
            // Get a field with a default value
            "getOr" => {
                let obj = self.emit_expr(&args[0])?;
                let key = self.emit_expr(&args[1])?;
                let default = self.emit_expr(&args[2])?;
                Ok(format!(
                    "({}[{}] !== undefined ? {}[{}] : {})",
                    obj, key, obj, key, default
                ))
            }
            // Check if key exists
            "hasKey" => {
                let obj = self.emit_expr(&args[0])?;
                let key = self.emit_expr(&args[1])?;
                Ok(format!("({} in {})", key, obj))
            }
            // Get all keys of an object
            "keys" => {
                let obj = self.emit_expr(&args[0])?;
                Ok(format!("Object.keys({})", obj))
            }
            // Get all values of an object
            "values" => {
                let obj = self.emit_expr(&args[0])?;
                Ok(format!("Object.values({})", obj))
            }
            // Get entries as list of [key, value] pairs
            "entries" => {
                let obj = self.emit_expr(&args[0])?;
                Ok(format!("Object.entries({})", obj))
            }
            // Create object from entries
            "fromEntries" => {
                let entries = self.emit_expr(&args[0])?;
                Ok(format!("Object.fromEntries({})", entries))
            }
            // Check if value is null
            "isNull" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!("({} === null)", v))
            }
            // Check if value is an array
            "isArray" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!("Array.isArray({})", v))
            }
            // Check if value is an object
            "isObject" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!(
                    "(typeof {} === 'object' && {} !== null && !Array.isArray({}))",
                    v, v, v
                ))
            }
            // Get type of JSON value as string
            "typeOf" => {
                let v = self.emit_expr(&args[0])?;
                Ok(format!(
                    "(Array.isArray({}) ? 'array' : {} === null ? 'null' : typeof {})",
                    v, v, v
                ))
            }
            _ => Err(JsGenError {
                message: format!("Unknown Json operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit Html module operations for type-safe HTML construction
    fn emit_html_operation(
        &mut self,
        operation: &str,
        args: &[Expr],
    ) -> Result<String, JsGenError> {
        match operation {
            // Text node
            "text" => {
                let content = self.emit_expr(&args[0])?;
                Ok(format!(
                    "{{ tag: \"text\", content: {} }}",
                    content
                ))
            }

            // Raw HTML (unsafe)
            "raw" => {
                let html = self.emit_expr(&args[0])?;
                Ok(format!(
                    "{{ tag: \"raw\", html: {} }}",
                    html
                ))
            }

            // Empty node
            "empty" => Ok("{ tag: \"empty\" }".to_string()),

            // Fragment (list of nodes)
            "fragment" => {
                let children = self.emit_expr(&args[0])?;
                Ok(format!(
                    "{{ tag: \"fragment\", children: {} }}",
                    children
                ))
            }

            // Standard HTML elements - all take (attrs, children)
            "div" | "span" | "p" | "a" | "button" | "input" | "form" | "label" |
            "h1" | "h2" | "h3" | "h4" | "h5" | "h6" |
            "ul" | "ol" | "li" | "table" | "tr" | "td" | "th" | "thead" | "tbody" |
            "header" | "footer" | "nav" | "main" | "aside" | "section" | "article" |
            "pre" | "code" | "blockquote" | "strong" | "em" | "small" |
            "img" | "video" | "audio" | "canvas" | "svg" |
            "select" | "option" | "textarea" | "fieldset" | "legend" => {
                let attrs = self.emit_expr(&args[0])?;
                let children = self.emit_expr(&args[1])?;
                Ok(format!(
                    "{{ tag: \"{}\", attrs: {}, children: {} }}",
                    operation, attrs, children
                ))
            }

            // Self-closing elements
            "br" | "hr" => {
                Ok(format!(
                    "{{ tag: \"{}\", attrs: [], children: [] }}",
                    operation
                ))
            }

            // Element with custom tag
            "element" => {
                let tag = self.emit_expr(&args[0])?;
                let attrs = self.emit_expr(&args[1])?;
                let children = self.emit_expr(&args[2])?;
                Ok(format!(
                    "{{ tag: {}, attrs: {}, children: {} }}",
                    tag, attrs, children
                ))
            }

            // Attribute constructors
            "class" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"class\", value: {} }}", value))
            }
            "id" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"id\", value: {} }}", value))
            }
            "style" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"style\", value: {} }}", value))
            }
            "href" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"href\", value: {} }}", value))
            }
            "src" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"src\", value: {} }}", value))
            }
            "alt" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"alt\", value: {} }}", value))
            }
            "type" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"type\", value: {} }}", value))
            }
            "name" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"name\", value: {} }}", value))
            }
            "value" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"value\", value: {} }}", value))
            }
            "placeholder" => {
                let value = self.emit_expr(&args[0])?;
                Ok(format!("{{ attr: \"placeholder\", value: {} }}", value))
            }
            "disabled" => {
                Ok("{ attr: \"disabled\", value: \"\" }".to_string())
            }
            "checked" => {
                Ok("{ attr: \"checked\", value: \"\" }".to_string())
            }
            "readonly" => {
                Ok("{ attr: \"readonly\", value: \"\" }".to_string())
            }
            "required" => {
                Ok("{ attr: \"required\", value: \"\" }".to_string())
            }
            "autofocus" => {
                Ok("{ attr: \"autofocus\", value: \"\" }".to_string())
            }

            // Custom attribute
            "attr" => {
                let name = self.emit_expr(&args[0])?;
                let value = self.emit_expr(&args[1])?;
                Ok(format!("{{ attr: {}, value: {} }}", name, value))
            }

            // Data attribute
            "data" => {
                let name = self.emit_expr(&args[0])?;
                let value = self.emit_expr(&args[1])?;
                Ok(format!("{{ attr: \"data-\" + {}, value: {} }}", name, value))
            }

            // Event handlers
            "onClick" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"click\", handler: {} }}", handler))
            }
            "onInput" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"input\", handler: {} }}", handler))
            }
            "onChange" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"change\", handler: {} }}", handler))
            }
            "onSubmit" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"submit\", handler: {} }}", handler))
            }
            "onKeyDown" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"keydown\", handler: {} }}", handler))
            }
            "onKeyUp" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"keyup\", handler: {} }}", handler))
            }
            "onKeyPress" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"keypress\", handler: {} }}", handler))
            }
            "onFocus" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"focus\", handler: {} }}", handler))
            }
            "onBlur" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"blur\", handler: {} }}", handler))
            }
            "onMouseOver" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"mouseover\", handler: {} }}", handler))
            }
            "onMouseOut" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"mouseout\", handler: {} }}", handler))
            }
            "onMouseDown" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"mousedown\", handler: {} }}", handler))
            }
            "onMouseUp" => {
                let handler = self.emit_expr(&args[0])?;
                Ok(format!("{{ event: \"mouseup\", handler: {} }}", handler))
            }

            // Custom event
            "on" => {
                let event = self.emit_expr(&args[0])?;
                let handler = self.emit_expr(&args[1])?;
                Ok(format!("{{ event: {}, handler: {} }}", event, handler))
            }

            // Render HTML to string
            "render" => {
                let node = self.emit_expr(&args[0])?;
                Ok(format!("Lux.renderHtml({})", node))
            }

            // Render HTML to DOM (returns Element)
            "renderToDom" => {
                let node = self.emit_expr(&args[0])?;
                Ok(format!("Lux.renderToDom({})", node))
            }

            _ => Err(JsGenError {
                message: format!("Unknown Html operation: {}", operation),
                span: None,
            }),
        }
    }

    /// Emit a literal value
    fn emit_literal(&self, lit: &Literal) -> Result<String, JsGenError> {
        match &lit.kind {
            LiteralKind::Int(n) => Ok(n.to_string()),
            LiteralKind::Float(f) => {
                if f.is_infinite() {
                    if *f > 0.0 {
                        Ok("Infinity".to_string())
                    } else {
                        Ok("-Infinity".to_string())
                    }
                } else if f.is_nan() {
                    Ok("NaN".to_string())
                } else {
                    Ok(format!("{}", f))
                }
            }
            LiteralKind::String(s) => {
                // Escape special characters for JS string
                let escaped = s
                    .replace('\\', "\\\\")
                    .replace('"', "\\\"")
                    .replace('\n', "\\n")
                    .replace('\r', "\\r")
                    .replace('\t', "\\t");
                Ok(format!("\"{}\"", escaped))
            }
            LiteralKind::Char(c) => {
                // Chars become single-character strings in JS
                Ok(format!("\"{}\"", c))
            }
            LiteralKind::Bool(b) => Ok(if *b { "true" } else { "false" }.to_string()),
            LiteralKind::Unit => Ok("undefined".to_string()),
        }
    }

    /// Check if an expression produces a string
    fn is_string_expr(&self, expr: &Expr) -> bool {
        match expr {
            Expr::Literal(lit) => matches!(lit.kind, LiteralKind::String(_)),
            Expr::Call { func, .. } => {
                if let Expr::Var(ident) = func.as_ref() {
                    ident.name == "toString"
                } else {
                    false
                }
            }
            Expr::BinaryOp { op, left, right, .. } => {
                matches!(op, BinaryOp::Add)
                    && (self.is_string_expr(left) || self.is_string_expr(right))
            }
            _ => false,
        }
    }

    /// Check if an expression contains statements that emit code before the result
    fn expr_has_statements(&self, expr: &Expr) -> bool {
        match expr {
            Expr::Block { statements, .. } => !statements.is_empty(),
            Expr::Let { .. } => true,
            Expr::Match { .. } => true,
            Expr::If { then_branch, else_branch, .. } => {
                self.expr_has_statements(then_branch) || self.expr_has_statements(else_branch)
            }
            _ => false,
        }
    }

    /// Check if an expression calls the main function
    fn expr_calls_main(&self, expr: &Expr) -> bool {
        match expr {
            Expr::Call { func, .. } => {
                if let Expr::Var(ident) = func.as_ref() {
                    if ident.name == "main" {
                        return true;
                    }
                }
                false
            }
            Expr::Run { expr, .. } => self.expr_calls_main(expr),
            Expr::Let { value, body, .. } => {
                self.expr_calls_main(value) || self.expr_calls_main(body)
            }
            Expr::Block { statements, result, .. } => {
                for stmt in statements {
                    if let Statement::Let { value, .. } = stmt {
                        if self.expr_calls_main(value) {
                            return true;
                        }
                    }
                }
                self.expr_calls_main(result)
            }
            _ => false,
        }
    }

    /// Mangle a Lux name to a valid JavaScript name
    fn mangle_name(&self, name: &str) -> String {
        format!("{}_lux", name)
    }

    /// Escape JavaScript reserved keywords
    fn escape_js_keyword(&self, name: &str) -> String {
        match name {
            "break" | "case" | "catch" | "continue" | "debugger" | "default" | "delete" | "do"
            | "else" | "finally" | "for" | "function" | "if" | "in" | "instanceof" | "new"
            | "return" | "switch" | "this" | "throw" | "try" | "typeof" | "var" | "void"
            | "while" | "with" | "class" | "const" | "enum" | "export" | "extends" | "import"
            | "super" | "implements" | "interface" | "let" | "package" | "private"
            | "protected" | "public" | "static" | "yield" | "await" | "async" => {
                format!("{}_", name)
            }
            _ => name.to_string(),
        }
    }

    /// Generate a fresh unique name
    fn fresh_name(&mut self) -> usize {
        let n = self.name_counter;
        self.name_counter += 1;
        n
    }

    /// Write a line with current indentation
    fn writeln(&mut self, s: &str) {
        for _ in 0..self.indent {
            self.output.push_str("    ");
        }
        self.output.push_str(s);
        self.output.push('\n');
    }
}

impl Default for JsBackend {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;
    use std::process::Command;

    #[test]
    fn test_literal_int() {
        let backend = JsBackend::new();
        let lit = Literal {
            kind: LiteralKind::Int(42),
            span: Span::default(),
        };
        assert_eq!(backend.emit_literal(&lit).unwrap(), "42");
    }

    #[test]
    fn test_literal_string() {
        let backend = JsBackend::new();
        let lit = Literal {
            kind: LiteralKind::String("hello\nworld".to_string()),
            span: Span::default(),
        };
        assert_eq!(backend.emit_literal(&lit).unwrap(), "\"hello\\nworld\"");
    }

    #[test]
    fn test_literal_bool() {
        let backend = JsBackend::new();
        let lit = Literal {
            kind: LiteralKind::Bool(true),
            span: Span::default(),
        };
        assert_eq!(backend.emit_literal(&lit).unwrap(), "true");
    }

    #[test]
    fn test_mangle_name() {
        let backend = JsBackend::new();
        assert_eq!(backend.mangle_name("foo"), "foo_lux");
        assert_eq!(backend.mangle_name("main"), "main_lux");
    }

    #[test]
    fn test_escape_keywords() {
        let backend = JsBackend::new();
        assert_eq!(backend.escape_js_keyword("class"), "class_");
        assert_eq!(backend.escape_js_keyword("foo"), "foo");
    }

    /// Helper to compile Lux source to JS and run in Node.js
    fn compile_and_run(source: &str) -> Result<String, String> {
        let program = Parser::parse_source(source).map_err(|e| format!("Parse error: {}", e))?;

        let mut backend = JsBackend::new();
        let js_code = backend
            .generate(&program)
            .map_err(|e| format!("Codegen error: {}", e))?;

        let output = Command::new("node")
            .arg("-e")
            .arg(&js_code)
            .output()
            .map_err(|e| format!("Node.js error: {}", e))?;

        if !output.status.success() {
            return Err(format!(
                "Node.js execution failed:\n{}",
                String::from_utf8_lossy(&output.stderr)
            ));
        }

        Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
    }

    #[test]
    fn test_js_factorial() {
        let source = r#"
            fn factorial(n: Int): Int =
                if n <= 1 then 1
                else n * factorial(n - 1)

            fn main(): Unit with {Console} =
                Console.print("Result: " + toString(factorial(5)))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Result: 120");
    }

    #[test]
    fn test_js_fibonacci() {
        let source = r#"
            fn fib(n: Int): Int =
                if n <= 1 then n
                else fib(n - 1) + fib(n - 2)

            fn main(): Unit with {Console} =
                Console.print("fib(10) = " + toString(fib(10)))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "fib(10) = 55");
    }

    #[test]
    fn test_js_adt_and_pattern_matching() {
        let source = r#"
            type Tree =
                | Leaf(Int)
                | Node(Tree, Tree)

            fn sumTree(tree: Tree): Int =
                match tree {
                    Leaf(n) => n,
                    Node(left, right) => sumTree(left) + sumTree(right)
                }

            let tree = Node(Node(Leaf(1), Leaf(2)), Leaf(3))

            fn main(): Unit with {Console} =
                Console.print("Sum: " + toString(sumTree(tree)))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Sum: 6");
    }

    #[test]
    fn test_js_option_type() {
        let source = r#"
            fn safeDivide(a: Int, b: Int): Option<Int> =
                if b == 0 then None
                else Some(a / b)

            fn showResult(opt: Option<Int>): String =
                match opt {
                    Some(n) => "Got: " + toString(n),
                    None => "None"
                }

            fn main(): Unit with {Console} = {
                Console.print(showResult(safeDivide(10, 2)))
                Console.print(showResult(safeDivide(10, 0)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Got: 5\nNone");
    }

    #[test]
    fn test_js_closures() {
        let source = r#"
            fn makeAdder(x: Int): fn(Int): Int =
                fn(y: Int): Int => x + y

            let add5 = makeAdder(5)

            fn main(): Unit with {Console} =
                Console.print("5 + 10 = " + toString(add5(10)))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "5 + 10 = 15");
    }

    #[test]
    fn test_js_higher_order_functions() {
        let source = r#"
            fn apply(f: fn(Int): Int, x: Int): Int = f(x)
            fn double(x: Int): Int = x * 2

            fn main(): Unit with {Console} =
                Console.print("double(21) = " + toString(apply(double, 21)))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "double(21) = 42");
    }

    #[test]
    fn test_js_list_operations() {
        let source = r#"
            let nums = [1, 2, 3, 4, 5]
            let doubled = List.map(nums, fn(x: Int): Int => x * 2)
            let sum = List.foldl(doubled, 0, fn(acc: Int, x: Int): Int => acc + x)

            fn main(): Unit with {Console} =
                Console.print("Sum of doubled: " + toString(sum))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Sum of doubled: 30");
    }

    #[test]
    fn test_js_pipe_operator() {
        let source = r#"
            fn double(x: Int): Int = x * 2
            fn addOne(x: Int): Int = x + 1

            let result = 5 |> double |> addOne

            fn main(): Unit with {Console} =
                Console.print("Result: " + toString(result))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Result: 11");
    }

    #[test]
    fn test_js_records() {
        let source = r#"
            let point = { x: 10, y: 20 }
            let sum = point.x + point.y

            fn main(): Unit with {Console} =
                Console.print("Sum: " + toString(sum))
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Sum: 30");
    }

    #[test]
    fn test_js_string_concatenation() {
        let source = r#"
            let name = "World"
            let greeting = "Hello, " + name + "!"

            fn main(): Unit with {Console} =
                Console.print(greeting)
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Hello, World!");
    }

    #[test]
    fn test_js_random_int() {
        let source = r#"
            fn main(): Unit with {Console, Random} = {
                let n = Random.int(1, 10)
                // Just verify it runs without error and produces a number
                Console.print("Random: " + toString(n))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert!(output.starts_with("Random: "));
    }

    #[test]
    fn test_js_random_bool() {
        let source = r#"
            fn main(): Unit with {Console, Random} = {
                let b = Random.bool()
                Console.print("Bool: " + toString(b))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert!(output == "Bool: true" || output == "Bool: false");
    }

    #[test]
    fn test_js_random_float() {
        let source = r#"
            fn main(): Unit with {Console, Random} = {
                let f = Random.float()
                // Float should be between 0 and 1
                Console.print("Float generated")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Float generated");
    }

    #[test]
    fn test_js_time_now() {
        let source = r#"
            fn main(): Unit with {Console, Time} = {
                let t = Time.now()
                // Should be a positive timestamp
                if t > 0 then Console.print("Time works")
                else Console.print("Time failed")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Time works");
    }

    // ========================================================================
    // String Module Tests
    // ========================================================================

    #[test]
    fn test_js_string_length() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "hello"
                Console.print("Length: " + toString(String.length(s)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Length: 5");
    }

    #[test]
    fn test_js_string_concat() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let result = String.concat("Hello, ", "World!")
                Console.print(result)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Hello, World!");
    }

    #[test]
    fn test_js_string_slice() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "Hello, World!"
                Console.print(String.slice(s, 0, 5))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Hello");
    }

    #[test]
    fn test_js_string_char_at() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "abc"
                Console.print(String.charAt(s, 1))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "b");
    }

    #[test]
    fn test_js_string_index_of() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "hello world"
                Console.print("Index: " + toString(String.indexOf(s, "world")))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Index: 6");
    }

    #[test]
    fn test_js_string_contains() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "hello world"
                if String.contains(s, "world") then Console.print("Found")
                else Console.print("Not found")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Found");
    }

    #[test]
    fn test_js_string_starts_ends_with() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "hello world"
                let starts = String.startsWith(s, "hello")
                let ends = String.endsWith(s, "world")
                if starts && ends then Console.print("Both")
                else Console.print("Neither")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Both");
    }

    #[test]
    fn test_js_string_case() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "Hello World"
                Console.print(String.toUpperCase(s))
                Console.print(String.toLowerCase(s))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "HELLO WORLD\nhello world");
    }

    #[test]
    fn test_js_string_trim() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "  hello  "
                Console.print("[" + String.trim(s) + "]")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "[hello]");
    }

    #[test]
    fn test_js_string_split_join() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "a,b,c"
                let parts = String.split(s, ",")
                let rejoined = String.join(parts, "-")
                Console.print(rejoined)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "a-b-c");
    }

    #[test]
    fn test_js_string_replace() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "hello world"
                Console.print(String.replace(s, "world", "lux"))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "hello lux");
    }

    #[test]
    fn test_js_string_replace_all() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let s = "a-b-c-d"
                Console.print(String.replaceAll(s, "-", "_"))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "a_b_c_d");
    }

    #[test]
    fn test_js_string_repeat() {
        let source = r#"
            fn main(): Unit with {Console} = {
                Console.print(String.repeat("ab", 3))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "ababab");
    }

    #[test]
    fn test_js_string_is_empty() {
        let source = r#"
            fn main(): Unit with {Console} = {
                if String.isEmpty("") then Console.print("empty")
                else Console.print("not empty")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "empty");
    }

    #[test]
    fn test_js_string_chars() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let chars = String.chars("abc")
                Console.print("Len: " + toString(List.length(chars)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Len: 3");
    }

    #[test]
    fn test_js_string_lines() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let text = "line1
line2
line3"
                let lines = String.lines(text)
                Console.print("Lines: " + toString(List.length(lines)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Lines: 3");
    }

    // ========================================================================
    // Option Module Tests
    // ========================================================================

    #[test]
    fn test_js_option_is_some_none() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let some = Some(42)
                let none: Option<Int> = None
                if Option.isSome(some) then Console.print("some is Some")
                else Console.print("some is None")
                if Option.isNone(none) then Console.print("none is None")
                else Console.print("none is Some")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "some is Some\nnone is None");
    }

    #[test]
    fn test_js_option_unwrap() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let opt = Some(42)
                Console.print("Value: " + toString(Option.unwrap(opt)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Value: 42");
    }

    #[test]
    fn test_js_option_unwrap_or() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let some = Some(42)
                let none: Option<Int> = None
                Console.print("Some: " + toString(Option.unwrapOr(some, 0)))
                Console.print("None: " + toString(Option.unwrapOr(none, 99)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Some: 42\nNone: 99");
    }

    #[test]
    fn test_js_option_map() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let opt = Some(21)
                let doubled = Option.map(opt, fn(x: Int): Int => x * 2)
                match doubled {
                    Some(n) => Console.print("Doubled: " + toString(n)),
                    None => Console.print("None")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Doubled: 42");
    }

    #[test]
    fn test_js_option_map_none() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let opt: Option<Int> = None
                let doubled = Option.map(opt, fn(x: Int): Int => x * 2)
                match doubled {
                    Some(n) => Console.print("Doubled: " + toString(n)),
                    None => Console.print("Still None")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Still None");
    }

    #[test]
    fn test_js_option_flat_map() {
        let source = r#"
            fn safeDivide(a: Int, b: Int): Option<Int> =
                if b == 0 then None
                else Some(a / b)

            fn main(): Unit with {Console} = {
                let opt = Some(100)
                let result = Option.flatMap(opt, fn(x: Int): Option<Int> => safeDivide(x, 5))
                match result {
                    Some(n) => Console.print("Result: " + toString(n)),
                    None => Console.print("None")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Result: 20");
    }

    #[test]
    fn test_js_option_get_or_else() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let none: Option<Int> = None
                Console.print("Value: " + toString(Option.getOrElse(none, 42)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Value: 42");
    }

    // ========================================================================
    // Math Module Tests
    // ========================================================================

    #[test]
    fn test_js_math_basic() {
        let source = r#"
            fn main(): Unit with {Console} = {
                Console.print("abs: " + toString(Math.abs(-5)))
                Console.print("floor: " + toString(Math.floor(3.7)))
                Console.print("ceil: " + toString(Math.ceil(3.2)))
                Console.print("round: " + toString(Math.round(3.5)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "abs: 5\nfloor: 3\nceil: 4\nround: 4");
    }

    #[test]
    fn test_js_math_power_roots() {
        let source = r#"
            fn main(): Unit with {Console} = {
                Console.print("pow: " + toString(Math.pow(2, 3)))
                Console.print("sqrt: " + toString(Math.sqrt(16)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "pow: 8\nsqrt: 4");
    }

    #[test]
    fn test_js_math_min_max() {
        let source = r#"
            fn main(): Unit with {Console} = {
                Console.print("min: " + toString(Math.min(3, 7)))
                Console.print("max: " + toString(Math.max(3, 7)))
                Console.print("clamp: " + toString(Math.clamp(15, 0, 10)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "min: 3\nmax: 7\nclamp: 10");
    }

    #[test]
    fn test_js_math_trig() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let zero = Math.sin(0)
                let one = Math.cos(0)
                if zero == 0 && one == 1 then Console.print("Trig works")
                else Console.print("Trig failed")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Trig works");
    }

    #[test]
    fn test_js_math_constants() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let pi = Math.pi()
                let e = Math.e()
                if pi > 3.14 && pi < 3.15 then Console.print("pi ok")
                else Console.print("pi bad")
                if e > 2.71 && e < 2.72 then Console.print("e ok")
                else Console.print("e bad")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "pi ok\ne ok");
    }

    #[test]
    fn test_js_math_checks() {
        let source = r#"
            fn main(): Unit with {Console} = {
                if Math.isFinite(42) then Console.print("42 is finite")
                else Console.print("42 is not finite")
                if Math.isNaN(0 / 0) then Console.print("0/0 is NaN")
                else Console.print("0/0 is not NaN")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        // Note: 0/0 in JS is NaN, but in integer division it may differ
        assert!(output.contains("42 is finite"));
    }

    #[test]
    fn test_js_math_log() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ln_e = Math.ln(Math.e())
                if ln_e > 0.99 && ln_e < 1.01 then Console.print("ln(e) = 1")
                else Console.print("ln(e) != 1")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "ln(e) = 1");
    }

    // ========================================================================
    // Result Module Tests
    // ========================================================================

    #[test]
    fn test_js_result_is_ok_err() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(42)
                let err: Result<Int, String> = Err("error")
                if Result.isOk(ok) then Console.print("ok is Ok")
                else Console.print("ok is Err")
                if Result.isErr(err) then Console.print("err is Err")
                else Console.print("err is Ok")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "ok is Ok\nerr is Err");
    }

    #[test]
    fn test_js_result_unwrap() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(42)
                Console.print("Value: " + toString(Result.unwrap(ok)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Value: 42");
    }

    #[test]
    fn test_js_result_unwrap_or() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(42)
                let err: Result<Int, String> = Err("error")
                Console.print("Ok: " + toString(Result.unwrapOr(ok, 0)))
                Console.print("Err: " + toString(Result.unwrapOr(err, 99)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Ok: 42\nErr: 99");
    }

    #[test]
    fn test_js_result_map() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(21)
                let doubled = Result.map(ok, fn(x: Int): Int => x * 2)
                match doubled {
                    Ok(n) => Console.print("Doubled: " + toString(n)),
                    Err(e) => Console.print("Error: " + e)
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Doubled: 42");
    }

    #[test]
    fn test_js_result_map_err() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let err: Result<Int, String> = Err("oops")
                let mapped = Result.mapErr(err, fn(e: String): String => "Error: " + e)
                match mapped {
                    Ok(n) => Console.print("Got: " + toString(n)),
                    Err(e) => Console.print(e)
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Error: oops");
    }

    #[test]
    fn test_js_result_flat_map() {
        let source = r#"
            fn safeDivide(a: Int, b: Int): Result<Int, String> =
                if b == 0 then Err("division by zero")
                else Ok(a / b)

            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(100)
                let result = Result.flatMap(ok, fn(x: Int): Result<Int, String> => safeDivide(x, 5))
                match result {
                    Ok(n) => Console.print("Result: " + toString(n)),
                    Err(e) => Console.print("Error: " + e)
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Result: 20");
    }

    #[test]
    fn test_js_result_to_option() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let ok: Result<Int, String> = Ok(42)
                let err: Result<Int, String> = Err("error")
                match Result.toOption(ok) {
                    Some(n) => Console.print("Ok->Some: " + toString(n)),
                    None => Console.print("Ok->None")
                }
                match Result.toOption(err) {
                    Some(n) => Console.print("Err->Some: " + toString(n)),
                    None => Console.print("Err->None")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Ok->Some: 42\nErr->None");
    }

    // ========================================================================
    // JSON Module Tests
    // ========================================================================

    #[test]
    fn test_js_json_stringify() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { name: "Alice", age: 30 }
                Console.print(Json.stringify(obj))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert!(output.contains("Alice") && output.contains("30"));
    }

    #[test]
    fn test_js_json_parse() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let json = "[1, 2, 3]"
                match Json.parse(json) {
                    Ok(arr) => Console.print("Parsed: " + toString(List.length(arr))),
                    Err(e) => Console.print("Parse error: " + e)
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Parsed: 3");
    }

    #[test]
    fn test_js_json_parse_error() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let bad = "[invalid"
                match Json.parse(bad) {
                    Ok(obj) => Console.print("Parsed ok"),
                    Err(e) => Console.print("Got error")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Got error");
    }

    #[test]
    fn test_js_json_get() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { name: "Bob", age: 25 }
                match Json.get(obj, "name") {
                    Some(v) => Console.print("Name: " + v),
                    None => Console.print("Not found")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Name: Bob");
    }

    #[test]
    fn test_js_json_get_or() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { name: "Bob" }
                let age = Json.getOr(obj, "age", 0)
                Console.print("Age: " + toString(age))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Age: 0");
    }

    #[test]
    fn test_js_json_has_key() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { name: "Bob" }
                if Json.hasKey(obj, "name") then Console.print("has name")
                else Console.print("no name")
                if Json.hasKey(obj, "age") then Console.print("has age")
                else Console.print("no age")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "has name\nno age");
    }

    #[test]
    fn test_js_json_keys_values() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { a: 1, b: 2 }
                let keys = Json.keys(obj)
                Console.print("Keys: " + toString(List.length(keys)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Keys: 2");
    }

    #[test]
    fn test_js_json_type_of() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let arr = [1, 2, 3]
                let obj = { x: 1 }
                let num = 42
                Console.print("array: " + Json.typeOf(arr))
                Console.print("object: " + Json.typeOf(obj))
                Console.print("number: " + Json.typeOf(num))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "array: array\nobject: object\nnumber: number");
    }

    #[test]
    fn test_js_json_pretty_print() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let obj = { name: "Test" }
                let pretty = Json.prettyPrint(obj)
                // Pretty print should have newlines
                if String.contains(pretty, "\n") then Console.print("has newlines")
                else Console.print("no newlines")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "has newlines");
    }

    #[test]
    fn test_js_json_is_array() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let arr = [1, 2, 3]
                let obj = { x: 1 }
                if Json.isArray(arr) then Console.print("arr is array")
                else Console.print("arr is not array")
                if Json.isArray(obj) then Console.print("obj is array")
                else Console.print("obj is not array")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "arr is array\nobj is not array");
    }

    // ========================================================================
    // DOM Effect Tests (Node.js stubs - returns None/null for missing document)
    // ========================================================================

    #[test]
    fn test_js_dom_query_selector_stub() {
        // In Node.js, document is undefined, so querySelector returns None
        let source = r##"
            fn main(): Unit with {Console, Dom} = {
                match Dom.querySelector("#test") {
                    Some(el) => Console.print("Found element"),
                    None => Console.print("No element (expected in Node)")
                }
            }
        "##;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "No element (expected in Node)");
    }

    #[test]
    fn test_js_dom_get_element_by_id_stub() {
        let source = r#"
            fn main(): Unit with {Console, Dom} = {
                match Dom.getElementById("app") {
                    Some(el) => Console.print("Found"),
                    None => Console.print("Not found (Node.js)")
                }
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Not found (Node.js)");
    }

    #[test]
    fn test_js_dom_create_element_stub() {
        // createElement returns null in Node.js but should still be callable
        let source = r#"
            fn main(): Unit with {Console, Dom} = {
                let el = Dom.createElement("div")
                Console.print("createElement called")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "createElement called");
    }

    #[test]
    fn test_js_dom_window_size_stub() {
        let source = r#"
            fn main(): Unit with {Console, Dom} = {
                let size = Dom.getWindowSize()
                // In Node.js, returns { width: 0, height: 0 }
                Console.print("Width: " + toString(size.width))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Width: 0");
    }

    #[test]
    fn test_js_dom_get_body_stub() {
        let source = r#"
            fn main(): Unit with {Console, Dom} = {
                let body = Dom.getBody()
                // In Node.js, returns null
                Console.print("getBody called")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "getBody called");
    }

    #[test]
    fn test_js_dom_query_selector_all_stub() {
        let source = r#"
            fn main(): Unit with {Console, Dom} = {
                let elements = Dom.querySelectorAll("div")
                Console.print("Found: " + toString(List.length(elements)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Found: 0");
    }

    // ========================================================================
    // Html Module Tests
    // ========================================================================

    #[test]
    fn test_js_html_text() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.text("Hello, World!")
                Console.print("Tag: " + node.tag)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Tag: text");
    }

    #[test]
    fn test_js_html_element() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.div([], [])
                Console.print("Tag: " + node.tag)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Tag: div");
    }

    #[test]
    fn test_js_html_element_with_children() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let child1 = Html.text("Hello")
                let child2 = Html.span([], [Html.text("World")])
                let node = Html.div([], [child1, child2])
                Console.print("Children: " + toString(List.length(node.children)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Children: 2");
    }

    #[test]
    fn test_js_html_attributes() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.div([Html.class("container"), Html.id("main")], [])
                Console.print("Attrs: " + toString(List.length(node.attrs)))
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Attrs: 2");
    }

    #[test]
    fn test_js_html_render_text() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.text("Hello")
                let html = Html.render(node)
                Console.print(html)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Hello");
    }

    #[test]
    fn test_js_html_render_element() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.div([Html.class("test")], [Html.text("Content")])
                let html = Html.render(node)
                Console.print(html)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert!(output.contains("<div"));
        assert!(output.contains("class=\"test\""));
        assert!(output.contains("Content"));
        assert!(output.contains("</div>"));
    }

    #[test]
    fn test_js_html_render_nested() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let li1 = Html.li([], [Html.text("Item 1")])
                let li2 = Html.li([], [Html.text("Item 2")])
                let node = Html.ul([], [li1, li2])
                let html = Html.render(node)
                if String.contains(html, "<ul>") && String.contains(html, "<li>") then Console.print("Nested OK")
                else Console.print("Nested failed")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Nested OK");
    }

    #[test]
    fn test_js_html_escape() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let node = Html.text("<script>alert('xss')</script>")
                let html = Html.render(node)
                if String.contains(html, "&lt;") then Console.print("Escaped")
                else Console.print("Not escaped")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Escaped");
    }

    #[test]
    fn test_js_html_fragment() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let t1 = Html.text("Hello ")
                let t2 = Html.text("World")
                let node = Html.fragment([t1, t2])
                let html = Html.render(node)
                Console.print(html)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Hello World");
    }

    #[test]
    fn test_js_html_self_closing() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let br = Html.br()
                let html = Html.render(br)
                if String.contains(html, "/>") then Console.print("Self-closing")
                else Console.print("Not self-closing")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Self-closing");
    }

    #[test]
    fn test_js_html_button() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let attrs = [Html.class("btn")]
                let children = [Html.text("Click me")]
                let node = Html.button(attrs, children)
                let html = Html.render(node)
                if String.contains(html, "<button") && String.contains(html, "Click me") then Console.print("Button OK")
                else Console.print("Button failed")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Button OK");
    }

    #[test]
    fn test_js_html_input() {
        let source = r#"
            fn main(): Unit with {Console} = {
                let attrs = [Html.placeholder("Enter name")]
                let node = Html.input(attrs, [])
                let html = Html.render(node)
                if String.contains(html, "input") && String.contains(html, "placeholder") then Console.print("Input OK")
                else Console.print("Input failed")
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Input OK");
    }

    // ========================================================================
    // TEA Runtime Tests (verify generated code includes runtime)
    // ========================================================================

    #[test]
    fn test_js_runtime_generated() {
        // Test that the Lux runtime is properly generated
        use crate::parser::Parser;

        let source = r#"
            fn main(): Unit with {Console} = Console.print("Hello")
        "#;

        let program = Parser::parse_source(source).expect("Should parse");
        let mut backend = JsBackend::new();
        let js_code = backend.generate(&program).expect("Should generate");

        // Check that Lux runtime includes key functions
        assert!(js_code.contains("const Lux = {"), "Lux object should be defined");
        assert!(js_code.contains("Some:"), "Option Some should be defined");
        assert!(js_code.contains("None:"), "Option None should be defined");
        assert!(js_code.contains("renderHtml:"), "renderHtml should be defined");
        assert!(js_code.contains("renderToDom:"), "renderToDom should be defined");
        assert!(js_code.contains("escapeHtml:"), "escapeHtml should be defined");
        assert!(js_code.contains("app:"), "TEA app should be defined");
        assert!(js_code.contains("simpleApp:"), "simpleApp should be defined");
        assert!(js_code.contains("hasChanged:"), "hasChanged should be defined");
    }

    #[test]
    fn test_js_runtime_default_handlers() {
        // Test that default handlers are properly generated
        use crate::parser::Parser;

        let source = r#"
            fn main(): Unit with {Console} = Console.print("Hello")
        "#;

        let program = Parser::parse_source(source).expect("Should parse");
        let mut backend = JsBackend::new();
        let js_code = backend.generate(&program).expect("Should generate");

        // Check that default handlers include all effects
        assert!(js_code.contains("Console:"), "Console handler should exist");
        assert!(js_code.contains("Random:"), "Random handler should exist");
        assert!(js_code.contains("Time:"), "Time handler should exist");
        assert!(js_code.contains("Http:"), "Http handler should exist");
        assert!(js_code.contains("Dom:"), "Dom handler should exist");
    }

    #[test]
    fn test_js_tea_simple_model_view_update() {
        // Test a simple TEA-style program that compiles correctly
        let source = r#"
            type Model = | Counter(Int)

            fn init(): Model = Counter(0)

            fn getCount(m: Model): Int = match m { Counter(n) => n }

            fn view(model: Model): String =
                "<div>" + toString(getCount(model)) + "</div>"

            fn main(): Unit with {Console} = {
                let m = init()
                let html = view(m)
                Console.print(html)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "<div>0</div>");
    }

    #[test]
    fn test_js_html_with_event_handler() {
        // Test that event handlers are properly generated
        let source = r#"
            fn doNothing(): Unit = ()

            fn main(): Unit with {Console} = {
                let btn = Html.button([Html.onClick(doNothing)], [Html.text("Click")])
                Console.print("Tag: " + btn.tag)
            }
        "#;

        let output = compile_and_run(source).expect("Should compile and run");
        assert_eq!(output, "Tag: button");
    }
}
