use crate::ast::*;

/// Generates pure Javascript DOM code for live development preview.
pub fn generate_js(ast: &LiquidAST) -> String {
    let mut output = String::new();
    
    let store_names: Vec<String> = ast.classes.iter()
        .filter(|c| c.extends_class == "Store")
        .map(|c| c.name.clone())
        .collect();
    
    let custom_components: Vec<String> = ast.classes.iter()
        .filter(|c| c.extends_class == "Component")
        .map(|c| c.name.clone())
        .collect();
    
    for comp in &ast.classes {
        if comp.extends_class == "Store" {
            output.push_str(&generate_store(comp));
        } else {
            output.push_str(&generate_component(comp, &store_names, &custom_components));
        }
    }
    
    output
}

/// Generates a Javascript class for a component.
fn generate_component(comp: &ClassDecl, store_names: &[String], custom_components: &[String]) -> String {
    let mut code = String::new();
    
    code.push_str(&format!("class {} {{\n", comp.name));
    
    // Constructor
    code.push_str("    constructor() {\n");
    for prop in &comp.properties {
        let val_str = match &prop.value {
            Value::NumberLit(n) => n.clone(),
            Value::StringLit(s) => format!("\"{}\"", s),
            Value::Identifier(id) => id.clone(),
            _ => "null".to_string(),
        };
        code.push_str(&format!("        this.{} = {};\n", prop.name, val_str));
    }
    code.push_str("    }\n\n");
    
    // Methods
    for method in &comp.methods {
        code.push_str(&format!("    {}() {{\n", method.name));
        for stmt in &method.statements {
            let translated = translate_js_statement(stmt, "this.");
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("        this.update_dom();\n");
        code.push_str("    }\n\n");
    }
    
    let mut update_list = Vec::new();
    let mut el_counter = 0;
    
    let root_element = &comp.render.element;
    let mut dom_code = String::new();
    
    let current_var = format!("el_{}", el_counter);
    el_counter += 1;
    
    let tag = match root_element.name.as_str() {
        "Column" | "Row" | "Navigator" => "div",
        _ => "div",
    };
    
    dom_code.push_str(&format!("        const {} = document.createElement(\"{}\");\n", current_var, tag));
    if root_element.name == "Column" || root_element.name == "Row" {
        let (align_str, spacing_str) = generate_js_layout_properties(root_element);
        let layout_dir = if root_element.name == "Column" { "column" } else { "row" };
        dom_code.push_str(&format!("        {}.style = \"display: flex; flex-direction: {}; align-items: {}; gap: {};\";\n", current_var, layout_dir, align_str, spacing_str));
    }
    
    if root_element.name == "Navigator" {
        let mut routes = Vec::new();
        let mut route_var = "this.currentRoute".to_string();
        
        for prop in &root_element.props {
            if prop.name == "initialRoute" {
                route_var = match &prop.value {
                    Value::Identifier(_) => translate_js_value_with_prefix(&prop.value, "this."),
                    Value::StringLit(s) => format!("\"{}\"", s),
                    _ => "this.currentRoute".to_string(),
                };
            }
            if prop.name == "children" {
                if let Value::Array(arr) = &prop.value {
                    for val in arr {
                        if let Value::Element(route_el) = val {
                            if route_el.name == "Route" {
                                let mut path = String::new();
                                let mut page = String::new();
                                for rp in &route_el.props {
                                    if rp.name == "path" {
                                        if let Value::StringLit(p) = &rp.value {
                                            path = p.clone();
                                        }
                                    }
                                    if rp.name == "page" {
                                        if let Value::Identifier(pg) = &rp.value {
                                            page = pg.clone();
                                        }
                                    }
                                }
                                if !path.is_empty() && !page.is_empty() {
                                    routes.push((path, page));
                                }
                            }
                        }
                    }
                }
            }
        }
        
        let placeholder_id = format!("{}_nav", current_var);
        dom_code.push_str(&format!("        {}.id = \"{}\";\n", current_var, placeholder_id));
        
        let mut route_render_logic = String::new();
        for (path, page) in &routes {
            route_render_logic.push_str(&format!("            if ({} === \"{}\") {{\n", route_var, path));
            route_render_logic.push_str(&format!("                const page = new {}();\n", page));
            route_render_logic.push_str(&format!("                page.render(\"{}\");\n", placeholder_id));
            route_render_logic.push_str("            }\n");
        }
        
        update_list.push((placeholder_id.clone(), format!("(function() {{
            const container = document.getElementById(\"{}\");
            if (!container) return \"\";
            container.innerHTML = \"\";
{}
            return \"\";
        }})()", placeholder_id, route_render_logic)));
    } else {
        for prop in &root_element.props {
            if prop.name == "children" {
                if let Value::Array(arr) = &prop.value {
                    for val in arr {
                        if let Value::Element(child_el) = val {
                            let child_code = generate_js_dom_element(child_el, comp, &current_var, &mut el_counter, &mut update_list, custom_components);
                            dom_code.push_str(&child_code);
                        }
                    }
                }
            }
        }
    }
    
    dom_code.push_str(&format!("        parent.appendChild({});\n", current_var));
    
    // update_dom method
    code.push_str("    update_dom() {\n");
    for (id, js_expr) in &update_list {
        code.push_str(&format!("        const el_{} = document.getElementById(\"{}\");\n", id, id));
        code.push_str(&format!("        if (el_{}) el_{}.innerHTML = {};\n", id, id, js_expr));
    }
    code.push_str("    }\n\n");
    
    // render method
    let has_on_init = comp.methods.iter().any(|m| m.name == "onInit");
    code.push_str("    render(parentId) {\n");
    code.push_str("        const parent = document.getElementById(parentId);\n");
    code.push_str("        parent.innerHTML = \"\";\n");
    for store in store_names {
        let instance_name = format!("{}{}", &store[0..1].to_lowercase(), &store[1..]);
        code.push_str(&format!("        if (typeof {} !== 'undefined') {{\n", instance_name));
        code.push_str(&format!("            {}.subscribe(() => this.update_dom());\n", instance_name));
        code.push_str("        }\n");
    }
    code.push_str(&dom_code);
    code.push_str("        this.update_dom();\n");
    if has_on_init {
        code.push_str("        this.onInit();\n");
    }
    code.push_str("    }\n");
    
    code.push_str("}\n");
    code
}

/// Generates a Javascript class for a reactive store.
fn generate_store(comp: &ClassDecl) -> String {
    let mut code = String::new();
    code.push_str(&format!("class {} {{\n", comp.name));
    
    // Constructor
    code.push_str("    constructor() {\n");
    for prop in &comp.properties {
        let val_str = match &prop.value {
            Value::NumberLit(n) => n.clone(),
            Value::StringLit(s) => format!("\"{}\"", s),
            Value::Identifier(id) => id.clone(),
            _ => "null".to_string(),
        };
        code.push_str(&format!("        this.{} = {};\n", prop.name, val_str));
    }
    code.push_str("        this.listeners = [];\n");
    code.push_str("    }\n\n");
    
    // Observer methods
    code.push_str("    subscribe(listener) {\n");
    code.push_str("        this.listeners.push(listener);\n");
    code.push_str("    }\n\n");
    code.push_str("    notify() {\n");
    code.push_str("        this.listeners.forEach(l => l());\n");
    code.push_str("    }\n\n");
    
    // Methods
    for method in &comp.methods {
        code.push_str(&format!("    {}() {{\n", method.name));
        for stmt in &method.statements {
            let translated = translate_js_statement(stmt, "this.");
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("        this.notify();\n");
        code.push_str("    }\n\n");
    }
    
    code.push_str("}\n");
    
    // Instantiate global singleton
    let instance_name = format!("{}{}", &comp.name[0..1].to_lowercase(), &comp.name[1..]);
    code.push_str(&format!("const {} = new {}();\n\n", instance_name, comp.name));
    
    code
}

/// Recursively generates JS DOM elements.
fn generate_js_dom_element(
    el: &Element,
    comp: &ClassDecl,
    parent_var: &str,
    el_counter: &mut usize,
    update_list: &mut Vec<(String, String)>,
    custom_components: &[String],
) -> String {
    if el.name == "Route" {
        return String::new();
    }
    if el.name == "Empty" {
        return String::new();
    }
    
    // Check if Custom Component
    if custom_components.contains(&el.name) {
        let mut code = String::new();
        let current_var = format!("el_{}", el_counter);
        *el_counter += 1;
        
        let comp_inst_var = format!("comp_{}", el_counter);
        *el_counter += 1;
        
        code.push_str(&format!("        const {} = document.createElement(\"div\");\n", current_var));
        let comp_container_id = format!("{}_comp", current_var);
        code.push_str(&format!("        {}.id = \"{}\";\n", current_var, comp_container_id));
        code.push_str(&format!("        const {} = new {}();\n", comp_inst_var, el.name));
        
        let mut prop_updates = String::new();
        for prop in &el.props {
            let val_str = translate_js_value_with_prefix(&prop.value, "this.");
            code.push_str(&format!("        {}.{} = {};\n", comp_inst_var, prop.name, val_str));
            prop_updates.push_str(&format!("this.{}.{} = {}; ", comp_inst_var, prop.name, val_str));
        }
        
        code.push_str(&format!("        this.{} = {};\n", comp_inst_var, comp_inst_var));
        
        code.push_str(&format!("        {}.appendChild({});\n", parent_var, current_var));
        code.push_str(&format!("        {}.render(\"{}\");\n", comp_inst_var, comp_container_id));
        
        // Add a dummy sibling updater to run child update_dom safely
        let updater_var = format!("updater_{}", el_counter);
        *el_counter += 1;
        let updater_id = format!("{}_updater", current_var);
        code.push_str(&format!("        const {} = document.createElement(\"span\");\n", updater_var));
        code.push_str(&format!("        {}.id = \"{}\";\n", updater_var, updater_id));
        code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, updater_var));
        
        update_list.push((updater_id, format!("(function() {{
            {}
            this.{}.update_dom();
            return \"\";
        }}).bind(this)()", prop_updates, comp_inst_var)));
        
        return code;
    }
    
    // Check if Conditional If Widget
    if el.name == "If" {
        let mut code = String::new();
        let current_var = format!("el_{}", el_counter);
        *el_counter += 1;
        
        let mut condition_val = String::new();
        let mut child_el_opt = None;
        let mut fallback_el_opt = None;
        for prop in &el.props {
            match prop.name.as_str() {
                "condition" => {
                    condition_val = translate_js_value_with_prefix(&prop.value, "this.");
                }
                "child" => {
                    if let Value::Element(child_el) = &prop.value {
                        child_el_opt = Some(child_el);
                    }
                }
                "fallback" => {
                    if let Value::Element(fallback_el) = &prop.value {
                        fallback_el_opt = Some(fallback_el);
                    }
                }
                _ => {}
            }
        }
        
        code.push_str(&format!("        const {} = document.createElement(\"div\");\n", current_var));
        let container_id = format!("{}_if", current_var);
        code.push_str(&format!("        {}.id = \"{}\";\n", current_var, container_id));
        
        let child_html = match child_el_opt {
            Some(child_el) => generate_html_string(child_el, "", custom_components),
            None => "".to_string(),
        };
        let fallback_html = match fallback_el_opt {
            Some(fallback_el) => generate_html_string(fallback_el, "", custom_components),
            None => "".to_string(),
        };
        
        update_list.push((container_id.clone(), format!("(function() {{
            return ({}) ? `{}` : `{}`;
        }}).bind(this)()", condition_val, child_html, fallback_html)));
        
        code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, current_var));
        return code;
    }
    
    let mut code = String::new();
    let current_var = format!("el_{}", el_counter);
    *el_counter += 1;
    
    let tag = match el.name.as_str() {
        "Button" => "button",
        "Text" => "p",
        "TextField" => "input",
        "Column" | "Row" | "Navigator" | "Container" | "For" => "div",
        _ => "div",
    };
    
    code.push_str(&format!("        const {} = document.createElement(\"{}\");\n", current_var, tag));
    
    if el.name == "For" {
        let mut items_val = String::new();
        let mut item_var = "item".to_string();
        let mut child_val = None;
        
        for prop in &el.props {
            match prop.name.as_str() {
                "items" => {
                    items_val = translate_js_value_with_prefix(&prop.value, "this.");
                }
                "item" => {
                    if let Value::StringLit(s) = &prop.value {
                        item_var = s.clone();
                    }
                }
                "child" => {
                    if let Value::Element(child_el) = &prop.value {
                        child_val = Some(child_el);
                    }
                }
                _ => {}
            }
        }
        
        let container_id = format!("{}_for", current_var);
        code.push_str(&format!("        {}.id = \"{}\";\n", current_var, container_id));
        
        if let Some(child_el) = child_val {
            let html_expr = generate_html_string(child_el, &item_var, custom_components);
            update_list.push((container_id.clone(), format!("(function() {{
                const items = {};
                if (!Array.isArray(items)) return \"\";
                return items.map({} => `{}`).join(\"\");
            }}).bind(this)()", items_val, item_var, html_expr)));
        }
        
        code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, current_var));
        return code;
    }
    
    if el.name == "TextField" {
        let mut value_var = String::new();
        let mut on_changed_cb = String::new();
        
        for prop in &el.props {
            if prop.name == "value" {
                if let Value::Identifier(id) = &prop.value {
                    value_var = id.clone();
                }
            } else if prop.name == "onChanged" {
                if let Value::Identifier(id) = &prop.value {
                    on_changed_cb = id.clone();
                }
            }
        }
        
        let value_prefix = if value_var.contains('.') { "" } else { "this." };
        let cb_prefix = if on_changed_cb.contains('.') { "" } else { "this." };
        
        code.push_str(&format!("        {}.type = \"text\";\n", current_var));
        code.push_str(&format!("        {}.value = {}{};\n", current_var, value_prefix, value_var));
        code.push_str(&format!("        {}.oninput = (e) => {{\n", current_var));
        code.push_str(&format!("            {}{} = e.target.value;\n", value_prefix, value_var));
        if !on_changed_cb.is_empty() {
            code.push_str(&format!("            {}{}();\n", cb_prefix, on_changed_cb));
        } else {
            code.push_str("            this.update_dom();\n");
        }
        code.push_str("        };\n");
        
        let id_html = format!("{}_dyn", current_var);
        code.push_str(&format!("        {}.id = \"{}\";\n", current_var, id_html));
        update_list.push((id_html.clone(), format!("(function() {{
            const input = document.getElementById(\"{}\");
            if (input && document.activeElement !== input) {{
                input.value = {}{};
            }}
            return \"\";
        }}).bind(this)()", id_html, value_prefix, value_var)));
        
        code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, current_var));
        return code;
    }
    
    if el.name == "Container" {
        let mut css_styles = Vec::new();
        let mut child_val = None;
        for prop in &el.props {
            match prop.name.as_str() {
                "padding" => {
                    let val = translate_js_value(&prop.value).replace("\"", "");
                    css_styles.push(format!("padding: {}px", val));
                }
                "color" => {
                    let val = translate_js_value(&prop.value);
                    let clean_color = val.replace("Colors.", "").replace("Color.", "").replace("\"", "").to_lowercase();
                    css_styles.push(format!("background-color: {}", clean_color));
                }
                "borderRadius" => {
                    let val = translate_js_value(&prop.value).replace("\"", "");
                    css_styles.push(format!("border-radius: {}px", val));
                }
                "child" => {
                    child_val = Some(&prop.value);
                }
                _ => {}
            }
        }
        if !css_styles.is_empty() {
            code.push_str(&format!("        {}.style.cssText += \"{};\";\n", current_var, css_styles.join("; ")));
        }
        if let Some(Value::Element(child_el)) = child_val {
            let child_code = generate_js_dom_element(child_el, comp, &current_var, el_counter, update_list, custom_components);
            code.push_str(&child_code);
        }
        code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, current_var));
        return code;
    }
    
    if el.name == "Column" || el.name == "Row" {
        let (align_str, spacing_str) = generate_js_layout_properties(el);
        let layout_dir = if el.name == "Column" { "column" } else { "row" };
        code.push_str(&format!("        {}.style = \"display: flex; flex-direction: {}; align-items: {}; gap: {};\";\n", current_var, layout_dir, align_str, spacing_str));
    }
    
    if el.name == "Navigator" {
        let mut routes = Vec::new();
        let mut route_var = "this.currentRoute".to_string();
        
        for prop in &el.props {
            if prop.name == "initialRoute" {
                route_var = match &prop.value {
                    Value::Identifier(id) => format!("this.{}", id),
                    Value::StringLit(s) => format!("\"{}\"", s),
                    _ => "this.currentRoute".to_string(),
                };
            }
            if prop.name == "children" {
                if let Value::Array(arr) = &prop.value {
                    for val in arr {
                        if let Value::Element(route_el) = val {
                            if route_el.name == "Route" {
                                let mut path = String::new();
                                let mut page = String::new();
                                for rp in &route_el.props {
                                    if rp.name == "path" {
                                        if let Value::StringLit(p) = &rp.value {
                                            path = p.clone();
                                        }
                                    }
                                    if rp.name == "page" {
                                        if let Value::Identifier(pg) = &rp.value {
                                            page = pg.clone();
                                        }
                                    }
                                }
                                if !path.is_empty() && !page.is_empty() {
                                    routes.push((path, page));
                                }
                            }
                        }
                    }
                }
            }
        }
        
        let placeholder_id = format!("{}_nav", current_var);
        code.push_str(&format!("        {}.id = \"{}\";\n", current_var, placeholder_id));
        
        let mut route_render_logic = String::new();
        for (path, page) in &routes {
            route_render_logic.push_str(&format!("            if ({} === \"{}\") {{\n", route_var, path));
            route_render_logic.push_str(&format!("                if (this.activePage && typeof this.activePage.onDispose === 'function') {{ this.activePage.onDispose(); }}\n"));
            route_render_logic.push_str(&format!("                const page = new {}();\n", page));
            route_render_logic.push_str(&format!("                this.activePage = page;\n"));
            route_render_logic.push_str(&format!("                page.render(\"{}\");\n", placeholder_id));
            route_render_logic.push_str("            }\n");
        }
        
        update_list.push((placeholder_id.clone(), format!("(function() {{
            const container = document.getElementById(\"{}\");
            if (!container) return \"\";
            container.innerHTML = \"\";
{}
            return \"\";
        }}).bind(this)()", placeholder_id, route_render_logic)));
    } else {
        for prop in &el.props {
            match prop.name.as_str() {
                "label" | "text" => {
                    let val_str = match &prop.value {
                        Value::StringLit(s) => {
                            if s.contains('$') {
                                let id_html = format!("{}_dyn", current_var);
                                code.push_str(&format!("        {}.id = \"{}\";\n", current_var, id_html));
                                let js_expr = translate_js_string_interpolation(s, "this.");
                                update_list.push((id_html, js_expr.clone()));
                                js_expr
                            } else {
                                format!("\"{}\"", s)
                            }
                        }
                        Value::Identifier(id) => {
                            let id_html = format!("{}_dyn", current_var);
                            code.push_str(&format!("        {}.id = \"{}\";\n", current_var, id_html));
                            let expr = if id.contains('.') { id.clone() } else { format!("this.{}", id) };
                            update_list.push((id_html, expr.clone()));
                            expr
                        }
                        _ => "\"\"".to_string(),
                    };
                    code.push_str(&format!("        {}.innerHTML = {};\n", current_var, val_str));
                }
                "onPressed" => {
                    if let Value::Identifier(method_name) = &prop.value {
                        let call_expr = if method_name.contains('.') { format!("{}()", method_name) } else { format!("this.{}()", method_name) };
                        code.push_str(&format!("        {}.onclick = () => {};\n", current_var, call_expr));
                    }
                }
                "style" => {
                    if let Value::Element(style_el) = &prop.value {
                        if style_el.name == "TextStyle" {
                            let mut css_styles = Vec::new();
                            for style_prop in &style_el.props {
                                match style_prop.name.as_str() {
                                    "fontSize" => {
                                        let size = translate_js_value(&style_prop.value);
                                        let clean_size = size.replace("\"", "").replace(".0", "");
                                        let px_size = if clean_size.chars().all(|c| c.is_numeric()) {
                                            format!("{}px", clean_size)
                                        } else {
                                            clean_size
                                        };
                                        css_styles.push(format!("font-size: {}", px_size));
                                    }
                                    "weight" => {
                                        let weight = translate_js_value(&style_prop.value);
                                        let clean_weight = weight.replace("FontWeight.", "").to_lowercase();
                                        let fw = match clean_weight.as_str() {
                                            "bold" => "bold",
                                            "normal" => "normal",
                                            _ => &clean_weight,
                                        };
                                        css_styles.push(format!("font-weight: {}", fw));
                                    }
                                    "color" => {
                                        let color = translate_js_value(&style_prop.value);
                                        let clean_color = color.replace("Colors.", "").replace("Color.", "").to_lowercase();
                                        css_styles.push(format!("color: {}", clean_color));
                                    }
                                    _ => {}
                                }
                            }
                            if !css_styles.is_empty() {
                                code.push_str(&format!("        {}.style.cssText += \"{};\";\n", current_var, css_styles.join("; ")));
                            }
                        }
                    }
                }
                "children" => {
                    if let Value::Array(arr) = &prop.value {
                        for val in arr {
                            if let Value::Element(child_el) = val {
                                let child_code = generate_js_dom_element(child_el, comp, &current_var, el_counter, update_list, custom_components);
                                code.push_str(&child_code);
                            }
                        }
                    }
                }
                _ => {}
            }
        }
    }
    
    code.push_str(&format!("        {}.appendChild({});\n\n", parent_var, current_var));
    code
}

/// Translates string interpolation format inside string literal for JS.
fn translate_js_string_interpolation(lit: &str, prefix: &str) -> String {
    if lit.contains('$') {
        let mut fmt_string = String::new();
        let chars: Vec<char> = lit.chars().collect();
        let mut i = 0;
        
        while i < chars.len() {
            if chars[i] == '$' && i + 1 < chars.len() && chars[i+1].is_alphabetic() {
                let mut var_name = String::new();
                i += 1;
                while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == '.') {
                    var_name.push(chars[i]);
                    i += 1;
                }
                if var_name.contains('.') {
                    fmt_string.push_str(&format!("${{{}}}", var_name));
                } else {
                    fmt_string.push_str(&format!("${{{}{}}}", prefix, var_name));
                }
            } else {
                fmt_string.push(chars[i]);
                i += 1;
            }
        }
        format!("`{}`", fmt_string)
    } else {
        format!("\"{}\"", lit)
    }
}

/// Translates standard values for Javascript generator.
fn translate_js_value(val: &Value) -> String {
    match val {
        Value::StringLit(s) => format!("\"{}\"", s),
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => id.clone(),
        _ => "\"\"".to_string(),
    }
}

/// Translates Layout elements properties for Javascript generator.
fn generate_js_layout_properties(el: &Element) -> (String, String) {
    let mut align_str = "center".to_string();
    let mut spacing_str = "16px".to_string();
    
    for prop in &el.props {
        match prop.name.as_str() {
            "alignment" => {
                let val = translate_js_value(&prop.value);
                let clean_val = val.replace("Alignment.", "").replace("\"", "").to_lowercase();
                align_str = match clean_val.as_str() {
                    "start" | "left" => "flex-start".to_string(),
                    "end" | "right" => "flex-end".to_string(),
                    "center" => "center".to_string(),
                    _ => clean_val,
                };
            }
            "spacing" => {
                let val = translate_js_value(&prop.value);
                let val_clean = val.replace("\"", "").replace(".0", "");
                spacing_str = if val_clean.chars().all(|c| c.is_numeric()) {
                    format!("{}px", val_clean)
                } else {
                    val_clean
                };
            }
            _ => {}
        }
    }
    (align_str, spacing_str)
}

fn translate_js_value_with_prefix(val: &Value, prefix: &str) -> String {
    match val {
        Value::StringLit(s) => {
            if s.contains('$') {
                translate_js_string_interpolation(s, prefix)
            } else {
                format!("\"{}\"", s)
            }
        }
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => {
            if id.contains('.') {
                id.clone()
            } else {
                format!("{}{}", prefix, id)
            }
        }
        _ => "\"\"".to_string(),
    }
}

fn translate_js_statement(stmt: &Statement, prefix: &str) -> String {
    match stmt {
        Statement::Return(val) => {
            let val_str = translate_js_value_with_prefix(val, prefix);
            format!("return {};\n", val_str)
        }
        Statement::Expr(expr) => {
            if expr.name == "Storage.set" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let key = translate_js_value_with_prefix(&args[0], prefix);
                        let val = translate_js_value_with_prefix(&args[1], prefix);
                        return format!("localStorage.setItem({}, {});\n", key, val);
                    }
                }
            } else if expr.name == "Storage.get" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 1 {
                        let key = translate_js_value_with_prefix(&args[0], prefix);
                        return format!("localStorage.getItem({});\n", key);
                    }
                }
            } else if expr.name == "Http.get" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let url = translate_js_value_with_prefix(&args[0], prefix);
                        if let Value::Identifier(ref cb) = args[1] {
                            return format!("fetch({}).then(r => r.text()).then(data => {}{}(data));\n", url, prefix, cb);
                        }
                    }
                }
            } else if expr.name == "Http.post" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 3 {
                        let url = translate_js_value_with_prefix(&args[0], prefix);
                        let body = translate_js_value_with_prefix(&args[1], prefix);
                        if let Value::Identifier(ref cb) = args[2] {
                            return format!("fetch({}, {{ method: 'POST', body: {} }}).then(r => r.text()).then(data => {}{}(data));\n", url, body, prefix, cb);
                        }
                    }
                }
            } else if expr.name == "Location.current" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 1 {
                        if let Value::Identifier(ref cb) = args[0] {
                            return format!("if (navigator.geolocation) {{ navigator.geolocation.getCurrentPosition(pos => {{ {}{}(pos.coords.latitude + ',' + pos.coords.longitude); }}); }}\n", prefix, cb);
                        }
                    }
                }
            } else if expr.name == "Notification.show" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let title = translate_js_value_with_prefix(&args[0], prefix);
                        let body = translate_js_value_with_prefix(&args[1], prefix);
                        return format!("if (Notification.permission === 'granted') {{ new Notification({}, {{ body: {} }}); }} else if (Notification.permission !== 'denied') {{ Notification.requestPermission().then(permission => {{ if (permission === 'granted') {{ new Notification({}, {{ body: {} }}); }} }}); }}\n", title, body, title, body);
                    }
                }
            }
            
            let actual_prefix = if expr.name.contains('.') { "" } else { prefix };
            if let Some(op) = &expr.op {
                if op == "++" {
                    format!("{}{} += 1;\n", actual_prefix, expr.name)
                } else if op == "--" {
                    format!("{}{} -= 1;\n", actual_prefix, expr.name)
                } else {
                    format!("{}{};\n", actual_prefix, expr.name)
                }
            } else if let Some(ref args) = expr.args {
                let args_strs: Vec<String> = args.iter().map(|v| translate_js_value_with_prefix(v, prefix)).collect();
                format!("{}{}({});\n", actual_prefix, expr.name, args_strs.join(", "))
            } else {
                format!("{}{}();\n", actual_prefix, expr.name)
            }
        }
    }
}

fn translate_html_value(val: &Value, item_var: &str) -> String {
    match val {
        Value::StringLit(s) => {
            let mut clean = s.clone();
            if clean.contains('$') {
                let target = format!("${}", item_var);
                clean = clean.replace(&target, &format!("${{{}}}", item_var));
                if clean.contains('$') {
                    let chars: Vec<char> = clean.chars().collect();
                    let mut i = 0;
                    let mut res = String::new();
                    while i < chars.len() {
                        if chars[i] == '$' && i + 1 < chars.len() && chars[i+1].is_alphabetic() {
                            let mut var_name = String::new();
                            i += 1;
                            while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == '.') {
                                var_name.push(chars[i]);
                                i += 1;
                            }
                            if var_name == item_var {
                                res.push_str(&format!("${{{}}}", var_name));
                            } else if var_name.contains('.') {
                                res.push_str(&format!("${{{}}}", var_name));
                            } else {
                                res.push_str(&format!("${{this.{}}}", var_name));
                            }
                        } else {
                            res.push(chars[i]);
                            i += 1;
                        }
                    }
                    clean = res;
                }
            }
            clean
        }
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => {
            if id == item_var {
                format!("${{{}}}", id)
            } else if id.contains('.') {
                format!("${{{}}}", id)
            } else {
                format!("${{this.{}}}", id)
            }
        }
        _ => "".to_string(),
    }
}

fn generate_html_string(el: &Element, item_var: &str, custom_components: &[String]) -> String {
    match el.name.as_str() {
        "Text" => {
            let mut text_val = String::new();
            let mut font_size = "".to_string();
            let mut font_weight = "".to_string();
            let mut color = "".to_string();
            for prop in &el.props {
                if prop.name == "text" || prop.name == "label" {
                    text_val = translate_html_value(&prop.value, item_var);
                } else if prop.name == "style" {
                    if let Value::Element(style_el) = &prop.value {
                        if style_el.name == "TextStyle" {
                            for sp in &style_el.props {
                                if sp.name == "fontSize" {
                                    font_size = format!("font-size: {}px;", translate_html_value(&sp.value, item_var).replace("\"", ""));
                                } else if sp.name == "weight" {
                                    font_weight = format!("font-weight: {};", translate_html_value(&sp.value, item_var).replace("FontWeight.", "").to_lowercase());
                                } else if sp.name == "color" {
                                    color = format!("color: {};", translate_html_value(&sp.value, item_var).replace("Colors.", "").replace("Color.", "").to_lowercase());
                                }
                            }
                        }
                    }
                }
            }
            format!("<p style=\"{}{}{}\">{}</p>", font_size, font_weight, color, text_val)
        }
        "Button" => {
            let mut label = "".to_string();
            let mut click = "".to_string();
            for prop in &el.props {
                if prop.name == "label" {
                    label = translate_html_value(&prop.value, item_var);
                } else if prop.name == "onPressed" {
                    if let Value::Identifier(id) = &prop.value {
                        click = if id.contains('.') {
                            format!("onclick=\"{}()\"", id)
                        } else {
                            format!("onclick=\"this.{}()\"", id)
                        };
                    }
                }
            }
            format!("<button {}>{}</button>", click, label)
        }
        "Container" => {
            let mut padding = "".to_string();
            let mut bg_color = "".to_string();
            let mut border_radius = "".to_string();
            let mut child_str = "".to_string();
            for prop in &el.props {
                if prop.name == "padding" {
                    padding = format!("padding: {}px;", translate_html_value(&prop.value, item_var));
                } else if prop.name == "color" {
                    bg_color = format!("background-color: {};", translate_html_value(&prop.value, item_var).replace("Colors.", "").replace("Color.", "").to_lowercase());
                } else if prop.name == "borderRadius" {
                    border_radius = format!("border-radius: {}px;", translate_html_value(&prop.value, item_var));
                } else if prop.name == "child" {
                    if let Value::Element(child_el) = &prop.value {
                        child_str = generate_html_string(child_el, item_var, custom_components);
                    }
                }
            }
            format!("<div style=\"{}{}{}\">{}</div>", padding, bg_color, border_radius, child_str)
        }
        "Column" | "Row" => {
            let mut children_str = String::new();
            let layout_dir = if el.name == "Column" { "column" } else { "row" };
            for prop in &el.props {
                if prop.name == "children" {
                    if let Value::Array(arr) = &prop.value {
                        for v in arr {
                            if let Value::Element(c_el) = v {
                                children_str.push_str(&generate_html_string(c_el, item_var, custom_components));
                            }
                        }
                    }
                }
            }
            format!("<div style=\"display: flex; flex-direction: {};\">{}</div>", layout_dir, children_str)
        }
        "If" => {
            let mut condition_val = String::new();
            let mut child_str = String::new();
            let mut fallback_str = String::new();
            for prop in &el.props {
                if prop.name == "condition" {
                    condition_val = translate_html_value(&prop.value, item_var);
                } else if prop.name == "child" {
                    if let Value::Element(child_el) = &prop.value {
                        child_str = generate_html_string(child_el, item_var, custom_components);
                    }
                } else if prop.name == "fallback" {
                    if let Value::Element(fallback_el) = &prop.value {
                        fallback_str = generate_html_string(fallback_el, item_var, custom_components);
                    }
                }
            }
            let mut cond_expr = condition_val.clone();
            if cond_expr.starts_with("${") && cond_expr.ends_with('}') {
                cond_expr = cond_expr[2..cond_expr.len()-1].to_string();
            }
            format!("${{ ({}) ? `{}` : `{}` }}", cond_expr, child_str, fallback_str)
        }
        name if custom_components.contains(&name.to_string()) => {
            format!("<div class=\"{}\"></div>", name)
        }
        _ => "".to_string()
    }
}
