use crate::ast::*;

/// Generates Kotlin/Jetpack Compose code for the Android target.
/// 
/// # Arguments
/// * `ast` - Processed LiquidAST tree.
pub fn generate_kotlin(ast: &LiquidAST) -> String {
    let mut output = String::new();
    
    // Import necessary Android Jetpack Compose packages
    output.push_str("import androidx.compose.runtime.*\n");
    output.push_str("import androidx.compose.foundation.layout.*\n");
    output.push_str("import androidx.compose.foundation.shape.RoundedCornerShape\n");
    output.push_str("import androidx.compose.material3.*\n");
    output.push_str("import androidx.compose.ui.Alignment\n");
    output.push_str("import androidx.compose.ui.Modifier\n");
    output.push_str("import androidx.compose.ui.unit.dp\n");
    output.push_str("import androidx.compose.ui.unit.sp\n");
    output.push_str("import androidx.compose.ui.text.font.FontWeight\n");
    output.push_str("import androidx.compose.ui.graphics.Color\n\n");
    
    // Define the Storage mock object
    output.push_str("object Storage {\n");
    output.push_str("    private val store = mutableMapOf<String, String>()\n");
    output.push_str("    fun set(key: String, value: String) {\n");
    output.push_str("        store[key] = value\n");
    output.push_str("    }\n");
    output.push_str("    fun get(key: String): String {\n");
    output.push_str("        return store[key] ?: \"\"\n");
    output.push_str("    }\n");
    output.push_str("}\n\n");
    
    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, &custom_components));
        }
    }
    
    output
}

/// Generates a @Composable function for a component class.
fn generate_component(comp: &ClassDecl, custom_components: &[String]) -> String {
    let mut code = String::new();
    
    code.push_str("@Composable\n");
    code.push_str(&format!("fun {}() {{\n", comp.name));
    
    // Initialize state variables using remember { mutableStateOf(...) }
    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(),
            _ => "\"\"".to_string(),
        };
        code.push_str(&format!("    var {} by remember {{ mutableStateOf({}) }}\n", prop.name, val_str));
    }
    
    if !comp.properties.is_empty() {
        code.push_str("\n");
    }
    
    // Component internal logic functions (methods)
    for method in &comp.methods {
        let return_type_str = match method.type_name.as_str() {
            "void" | "" => "".to_string(),
            "int" | "Int" => ": Int".to_string(),
            "double" | "Double" => ": Double".to_string(),
            "bool" | "Bool" => ": Boolean".to_string(),
            "String" => ": String".to_string(),
            other => format!(": {}", other),
        };
        code.push_str(&format!("    fun {}(){}{{\n", method.name, return_type_str));
        for stmt in &method.statements {
            let translated = translate_kotlin_statement(stmt);
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("    }\n\n");
    }
    
    let has_on_init = comp.methods.iter().any(|m| m.name == "onInit");
    let has_on_dispose = comp.methods.iter().any(|m| m.name == "onDispose");
    
    if has_on_init || has_on_dispose {
        code.push_str("    DisposableEffect(Unit) {\n");
        if has_on_init {
            code.push_str("        onInit()\n");
        }
        code.push_str("        onDispose {\n");
        if has_on_dispose {
            code.push_str("            onDispose()\n");
        }
        code.push_str("        }\n");
        code.push_str("    }\n\n");
    }
    
    // Declarative UI render
    code.push_str(&generate_kotlin_element(&comp.render.element, 1, custom_components));
    code.push_str("}\n");
    
    code
}

/// Recursively generates Compose element code.
fn generate_kotlin_element(el: &Element, indent_level: usize, custom_components: &[String]) -> String {
    let indent = "    ".repeat(indent_level);
    let mut code = String::new();
    
    match el.name.as_str() {
        "Column" | "Row" => {
            let layout = el.name.as_str();
            
            let mut align_str = String::new();
            let mut spacing_str = String::new();
            let mut children_val = None;
            
            for prop in &el.props {
                match prop.name.as_str() {
                    "alignment" => {
                        let val = translate_kotlin_value(&prop.value);
                        if el.name == "Column" {
                            if val.contains("center") || val.contains("Center") {
                                align_str = "horizontalAlignment = Alignment.CenterHorizontally".to_string();
                            }
                        } else {
                            if val.contains("center") || val.contains("Center") {
                                align_str = "verticalAlignment = Alignment.CenterVertically".to_string();
                            }
                        }
                    }
                    "spacing" => {
                        let val = translate_kotlin_value(&prop.value);
                        if el.name == "Column" {
                            spacing_str = format!("verticalArrangement = Arrangement.spacedBy({}.dp)", val);
                        } else {
                            spacing_str = format!("horizontalArrangement = Arrangement.spacedBy({}.dp)", val);
                        }
                    }
                    "children" => {
                        children_val = Some(&prop.value);
                    }
                    _ => {}
                }
            }
            
            let mut params = Vec::new();
            if !align_str.is_empty() { params.push(align_str); }
            if !spacing_str.is_empty() { params.push(spacing_str); }
            
            let params_str = if params.is_empty() {
                "".to_string()
            } else {
                format!("(params = {})", params.join(", "))
            };
            
            let final_params = if params.is_empty() {
                "".to_string()
            } else {
                format!("({})", params.join(", "))
            };
            
            code.push_str(&format!("{}{}{} {{\n", indent, layout, final_params));
            if let Some(Value::Array(arr)) = children_val {
                for v in arr {
                    if let Value::Element(child_el) = v {
                        code.push_str(&generate_kotlin_element(child_el, indent_level + 1, custom_components));
                    }
                }
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "Text" => {
            let mut text_val = String::from("\"\"");
            let mut modifiers = Vec::new();
            
            for prop in &el.props {
                match prop.name.as_str() {
                    "label" | "text" => {
                        text_val = translate_kotlin_value(&prop.value);
                    }
                    "style" => {
                        if let Value::Element(style_el) = &prop.value {
                            if style_el.name == "TextStyle" {
                                for style_prop in &style_el.props {
                                    match style_prop.name.as_str() {
                                        "fontSize" => {
                                            let size = translate_kotlin_value(&style_prop.value);
                                            modifiers.push(format!("fontSize = {}.sp", size));
                                        }
                                        "weight" => {
                                            let weight = translate_kotlin_value(&style_prop.value);
                                            let bold = if weight.contains("bold") || weight.contains("Bold") {
                                                "FontWeight.Bold"
                                            } else {
                                                "FontWeight.Normal"
                                            };
                                            modifiers.push(format!("fontWeight = {}", bold));
                                        }
                                        "color" => {
                                            let color = translate_kotlin_value(&style_prop.value);
                                            let clean_color = color.replace("Colors.", "").replace("Color.", "").replace("\"", "");
                                            let comp_color = format!("Color.{}", clean_color);
                                            modifiers.push(format!("color = {}", comp_color));
                                        }
                                        _ => {}
                                    }
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }
            let mut params = vec![format!("text = {}", text_val)];
            params.extend(modifiers);
            code.push_str(&format!("{}Text({})\n", indent, params.join(", ")));
        }
        "Button" => {
            let mut label_val = String::from("\"\"");
            let mut action_val = String::from("");
            
            for prop in &el.props {
                match prop.name.as_str() {
                    "label" => {
                        label_val = translate_kotlin_value(&prop.value);
                    }
                    "onPressed" => {
                        if let Value::Identifier(id) = &prop.value {
                            let call_expr = if id.contains('.') {
                                format!("{}()", translate_kotlin_store_expr(id))
                            } else {
                                format!("{}()", id)
                            };
                            action_val = call_expr;
                        }
                    }
                    _ => {}
                }
            }
            
            code.push_str(&format!("{}Button(onClick = {{ {} }}) {{\n", indent, action_val));
            code.push_str(&format!("{}    Text({})\n", indent, label_val));
            code.push_str(&format!("{}}}\n", indent));
        }
        "Navigator" => {
            let mut routes = Vec::new();
            let mut route_var = "currentRoute".to_string();
            
            for prop in &el.props {
                if prop.name == "initialRoute" {
                    route_var = translate_kotlin_value(&prop.value);
                }
                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));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            code.push_str(&format!("{}when ({}) {{\n", indent, route_var));
            for (path, page) in &routes {
                code.push_str(&format!("{}    \"{}\" -> {}()\n", indent, path, page));
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "For" => {
            let mut items_val = String::from("listOf()");
            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_kotlin_value(&prop.value);
                    }
                    "item" => {
                        if let Value::StringLit(s) = &prop.value {
                            item_var = s.clone();
                        }
                    }
                    "child" => {
                        child_val = Some(&prop.value);
                    }
                    _ => {}
                }
            }
            
            code.push_str(&format!("{}for ({} in {}) {{\n", indent, item_var, items_val));
            if let Some(Value::Element(child_el)) = child_val {
                code.push_str(&generate_kotlin_element(child_el, indent_level + 1, custom_components));
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "TextField" => {
            let mut value_var = String::new();
            let mut on_changed_cb = String::new();
            
            for prop in &el.props {
                if prop.name == "value" {
                    value_var = translate_kotlin_value(&prop.value);
                } else if prop.name == "onChanged" {
                    if let Value::Identifier(id) = &prop.value {
                        let mapped = if id.contains('.') {
                            translate_kotlin_store_expr(id)
                        } else {
                            id.clone()
                        };
                        on_changed_cb = format!("{}()", mapped);
                    }
                }
            }
            
            let update_stmt = if on_changed_cb.is_empty() {
                format!("{} = it", value_var)
            } else {
                format!("{} = it; {}", value_var, on_changed_cb)
            };
            
            code.push_str(&format!("{}OutlinedTextField(value = {}, onValueChange = {{ {} }})\n", indent, value_var, update_stmt));
        }
        "Container" => {
            let mut padding_val = None;
            let mut color_val = None;
            let mut radius_val = None;
            let mut child_val = None;
            
            for prop in &el.props {
                match prop.name.as_str() {
                    "padding" => {
                        padding_val = Some(translate_kotlin_value(&prop.value));
                    }
                    "color" => {
                        let val = translate_kotlin_value(&prop.value);
                        let clean_color = val.replace("Colors.", "").replace("Color.", "").replace("\"", "");
                        color_val = Some(format!("Color.{}", clean_color));
                    }
                    "borderRadius" => {
                        radius_val = Some(translate_kotlin_value(&prop.value));
                    }
                    "child" => {
                        child_val = Some(&prop.value);
                    }
                    _ => {}
                }
            }
            
            let mut modifiers = Vec::new();
            
            if let Some(ref p) = padding_val {
                modifiers.push(format!("padding({}.dp)", p));
            }
            
            if let Some(ref c) = color_val {
                if let Some(ref r) = radius_val {
                    modifiers.push(format!("background(color = {}, shape = RoundedCornerShape({}.dp))", c, r));
                } else {
                    modifiers.push(format!("background(color = {})", c));
                }
            } else if let Some(ref r) = radius_val {
                modifiers.push(format!("clip(RoundedCornerShape({}.dp))", r));
            }
            
            let modifier_str = if modifiers.is_empty() {
                "".to_string()
            } else {
                format!("(modifier = Modifier.{})", modifiers.join("."))
            };
            
            code.push_str(&format!("{}Box{} {{\n", indent, modifier_str));
            if let Some(Value::Element(child_el)) = child_val {
                code.push_str(&generate_kotlin_element(child_el, indent_level + 1, custom_components));
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "If" => {
            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_kotlin_value(&prop.value);
                    }
                    "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!("{}if ({}) {{\n", indent, condition_val));
            if let Some(child_el) = child_el_opt {
                code.push_str(&generate_kotlin_element(child_el, indent_level + 1, custom_components));
            }
            code.push_str(&format!("{}}}", indent));
            if let Some(fallback_el) = fallback_el_opt {
                code.push_str(" else {\n");
                code.push_str(&generate_kotlin_element(fallback_el, indent_level + 1, custom_components));
                code.push_str(&format!("{}}}\n", indent));
            } else {
                code.push_str("\n");
            }
        }
        name if custom_components.contains(&name.to_string()) => {
            let mut params = Vec::new();
            for prop in &el.props {
                let val = translate_kotlin_value(&prop.value);
                params.push(format!("{} = {}", prop.name, val));
            }
            let params_str = if params.is_empty() { "".to_string() } else { format!("({})", params.join(", ")) };
            code.push_str(&format!("{}{}{}\n", indent, name, params_str));
        }
        "Route" => {}
        _ => {
            code.push_str(&format!("{}// Unknown Compose Element {}\n", indent, el.name));
        }
    }
    
    code
}

/// Translates Value into its Kotlin representation.
fn translate_kotlin_value(val: &Value) -> String {
    match val {
        Value::StringLit(s) => translate_kotlin_string(s),
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => {
            if id.contains('.') {
                translate_kotlin_store_expr(id)
            } else {
                id.clone()
            }
        }
        _ => "\"\"".to_string(),
    }
}

fn translate_kotlin_store_expr(id: &str) -> String {
    if id.contains('.') {
        let parts: Vec<&str> = id.split('.').collect();
        if parts.len() == 2 {
            let store_inst = parts[0];
            let member = parts[1];
            let object_name = format!("{}{}", &store_inst[0..1].to_uppercase(), &store_inst[1..]);
            return format!("{}.{}", object_name, member);
        }
    }
    id.to_string()
}

fn translate_kotlin_string(lit: &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('.') {
                    let mapped = translate_kotlin_store_expr(&var_name);
                    fmt_string.push_str(&format!("${{{}}}", mapped));
                } else {
                    fmt_string.push_str(&format!("${}", var_name));
                }
            } else {
                fmt_string.push(chars[i]);
                i += 1;
            }
        }
        format!("\"{}\"", fmt_string)
    } else {
        format!("\"{}\"", lit)
    }
}

/// Generates a Jetpack Compose object singleton for a reactive store.
fn generate_store(comp: &ClassDecl) -> String {
    let mut code = String::new();
    
    code.push_str(&format!("object {} {{\n", comp.name));
    
    // Properties using mutableStateOf delegate
    for prop in &comp.properties {
        let val_str = match &prop.value {
            Value::NumberLit(n) => n.clone(),
            Value::StringLit(s) => translate_kotlin_string(s),
            Value::Identifier(id) => id.clone(),
            _ => "\"\"".to_string(),
        };
        code.push_str(&format!("    var {} by mutableStateOf({})\n", prop.name, val_str));
    }
    
    if !comp.properties.is_empty() {
        code.push_str("\n");
    }
    
    // Methods
    for method in &comp.methods {
        let return_type_str = match method.type_name.as_str() {
            "void" | "" => "".to_string(),
            "int" | "Int" => ": Int".to_string(),
            "double" | "Double" => ": Double".to_string(),
            "bool" | "Bool" => ": Boolean".to_string(),
            "String" => ": String".to_string(),
            other => format!(": {}", other),
        };
        code.push_str(&format!("    fun {}(){}{{\n", method.name, return_type_str));
        for stmt in &method.statements {
            let translated = translate_kotlin_statement(stmt);
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("    }\n\n");
    }
    
    code.push_str("}\n\n");
    code
}

fn translate_kotlin_statement(stmt: &Statement) -> String {
    match stmt {
        Statement::Return(val) => {
            let val_str = translate_kotlin_value(val);
            format!("return {}\n", val_str)
        }
        Statement::Expr(expr) => {
            if expr.name == "Http.get" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let url = translate_kotlin_value(&args[0]);
                        if let Value::Identifier(ref cb) = args[1] {
                            return format!("// Http.get mock\n        val _url = {}\n        {} = \"Mock HTTP GET Response data\"\n", url, cb);
                        }
                    }
                }
            } else if expr.name == "Http.post" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 3 {
                        let url = translate_kotlin_value(&args[0]);
                        let body = translate_kotlin_value(&args[1]);
                        if let Value::Identifier(ref cb) = args[2] {
                            return format!("// Http.post mock\n        val _url = {}\n        val _body = {}\n        {} = \"Mock HTTP POST Response data\"\n", url, body, 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!("// Location.current mock\n        {} = \"-6.2088,106.8456\"\n", cb);
                        }
                    }
                }
            } else if expr.name == "Notification.show" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let title = translate_kotlin_value(&args[0]);
                        let body = translate_kotlin_value(&args[1]);
                        return format!("// Notification.show mock\n        println(\"$title: $body\")\n");
                    }
                }
            } else if expr.name == "Storage.set" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let key = translate_kotlin_value(&args[0]);
                        let val = translate_kotlin_value(&args[1]);
                        return format!("Storage.set({}, {})\n", key, val);
                    }
                }
            } else if expr.name == "Storage.get" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 1 {
                        let key = translate_kotlin_value(&args[0]);
                        return format!("Storage.get({})\n", key);
                    }
                }
            }

            let mapped_name = if expr.name.contains('.') {
                translate_kotlin_store_expr(&expr.name)
            } else {
                expr.name.clone()
            };

            if let Some(op) = &expr.op {
                if op == "++" {
                    format!("{}++\n", mapped_name)
                } else if op == "--" {
                    format!("{}--\n", mapped_name)
                } else {
                    format!("{}\n", mapped_name)
                }
            } else if let Some(ref args) = expr.args {
                let args_strs: Vec<String> = args.iter().map(|v| translate_kotlin_value(v)).collect();
                format!("{}({})\n", mapped_name, args_strs.join(", "))
            } else {
                format!("{}()\n", mapped_name)
            }
        }
    }
}
