use crate::ast::*;

/// Generates Swift/SwiftUI code for the iOS target.
/// 
/// # Arguments
/// * `ast` - Processed LiquidAST tree.
pub fn generate_swift(ast: &LiquidAST) -> String {
    let mut output = String::new();
    
    output.push_str("import SwiftUI\n\n");
    
    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 the SwiftUI View code for a component class.
fn generate_component(comp: &ClassDecl, store_names: &[String], custom_components: &[String]) -> String {
    let mut code = String::new();
    
    code.push_str(&format!("struct {}: View {{\n", comp.name));
    
    // ObservedObjects for stores
    for store in store_names {
        let instance_name = format!("{}{}", &store[0..1].to_lowercase(), &store[1..]);
        code.push_str(&format!("    @ObservedObject var {}: {} = {}.shared\n", instance_name, store, store));
    }
    if !store_names.is_empty() {
        code.push_str("\n");
    }
    
    // State properties declared using @State
    for prop in &comp.properties {
        let swift_type = match prop.type_name.as_str() {
            "int" | "Int" => "Int",
            "double" | "Double" => "Double",
            "bool" | "Bool" => "Bool",
            "String" => "String",
            _ => &prop.type_name,
        };
        let val_str = match &prop.value {
            Value::NumberLit(n) => n.clone(),
            Value::StringLit(s) => translate_swift_string(s),
            Value::Identifier(id) => id.clone(),
            _ => "\"\"".to_string(),
        };
        code.push_str(&format!("    @State var {}: {} = {}\n", prop.name, swift_type, val_str));
    }
    
    if !comp.properties.is_empty() {
        code.push_str("\n");
    }
    
    // Internal logic methods
    for method in &comp.methods {
        let return_arrow = match method.type_name.as_str() {
            "void" | "" => "".to_string(),
            "int" | "Int" => " -> Int".to_string(),
            "double" | "Double" => " -> Double".to_string(),
            "bool" | "Bool" => " -> Bool".to_string(),
            "String" => " -> String".to_string(),
            other => format!(" -> {}", other),
        };
        code.push_str(&format!("    func {}(){}{{\n", method.name, return_arrow));
        for stmt in &method.statements {
            let translated = translate_swift_statement(stmt);
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("    }\n\n");
    }
    
    // SwiftUI View Body
    let has_on_init = comp.methods.iter().any(|m| m.name == "onInit");
    let has_on_dispose = comp.methods.iter().any(|m| m.name == "onDispose");
    
    code.push_str("    var body: some View {\n");
    let root_code = generate_swift_element_local(&comp.render.element, 2, &mut Vec::new(), custom_components);
    let mut trimmed_root = root_code.trim_end().to_string();
    if has_on_init {
        trimmed_root.push_str("\n        .onAppear {\n            self.onInit()\n        }");
    }
    if has_on_dispose {
        trimmed_root.push_str("\n        .onDisappear {\n            self.onDispose()\n        }");
    }
    trimmed_root.push_str("\n");
    code.push_str(&trimmed_root);
    code.push_str("    }\n}\n");
    
    code
}

/// Recursively generates SwiftUI UI elements.
#[allow(dead_code)]
fn generate_swift_element(el: &Element, indent_level: usize, custom_components: &[String]) -> String {
    generate_swift_element_local(el, indent_level, &mut Vec::new(), custom_components)
}

fn generate_swift_element_local(el: &Element, indent_level: usize, local_vars: &mut Vec<String>, custom_components: &[String]) -> String {
    let indent = "    ".repeat(indent_level);
    let mut code = String::new();
    
    match el.name.as_str() {
        "Column" | "Row" => {
            let layout = if el.name == "Column" { "VStack" } else { "HStack" };
            
            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_swift_value_local(&prop.value, local_vars);
                        let clean_val = val.replace("Alignment.", ".");
                        align_str = format!("alignment: {}", clean_val);
                    }
                    "spacing" => {
                        let val = translate_swift_value_local(&prop.value, local_vars);
                        spacing_str = format!("spacing: {}", 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.join(", "))
            };
            
            code.push_str(&format!("{}{}{} {{\n", indent, layout, params_str));
            if let Some(Value::Array(arr)) = children_val {
                for v in arr {
                    if let Value::Element(child_el) = v {
                        code.push_str(&generate_swift_element_local(child_el, indent_level + 1, local_vars, custom_components));
                    }
                }
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "Text" => {
            let mut text_val = String::from("\"\"");
            let mut modifiers = String::new();
            
            for prop in &el.props {
                match prop.name.as_str() {
                    "label" | "text" => {
                        text_val = translate_swift_value_local(&prop.value, local_vars);
                    }
                    "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_swift_value_local(&style_prop.value, local_vars);
                                            modifiers.push_str(&format!(".font(.system(size: {}))", size));
                                        }
                                        "weight" => {
                                            let weight = translate_swift_value_local(&style_prop.value, local_vars);
                                            let clean_weight = weight.replace("FontWeight.", ".").to_lowercase();
                                            modifiers.push_str(&format!(".fontWeight({})", clean_weight));
                                        }
                                        _ => {}
                                    }
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }
            code.push_str(&format!("{}Text({}){}\n", indent, text_val, modifiers));
        }
        "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_swift_value_local(&prop.value, local_vars);
                    }
                    "onPressed" => {
                        if let Value::Identifier(id) = &prop.value {
                            action_val = format!("self.{}()", id);
                        }
                    }
                    _ => {}
                }
            }
            
            code.push_str(&format!("{}{} {{\n", indent, "Button"));
            code.push_str(&format!("{}    {}\n", indent, action_val));
            code.push_str(&format!("{}}} label: {{\n", indent));
            code.push_str(&format!("{}    Text({})\n", indent, label_val));
            code.push_str(&format!("{}}}\n", indent));
        }
        "For" => {
            let mut items_val = String::from("[]");
            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_swift_value_local(&prop.value, local_vars);
                    }
                    "item" => {
                        if let Value::StringLit(s) = &prop.value {
                            item_var = s.clone();
                        }
                    }
                    "child" => {
                        child_val = Some(&prop.value);
                    }
                    _ => {}
                }
            }
            
            code.push_str(&format!("{}ForEach({}, id: \\.self) {{ {} in\n", indent, items_val, item_var));
            local_vars.push(item_var.clone());
            if let Some(Value::Element(child_el)) = child_val {
                code.push_str(&generate_swift_element_local(child_el, indent_level + 1, local_vars, custom_components));
            }
            local_vars.pop();
            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" {
                    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 binding_var = if value_var.contains('.') {
                let parts: Vec<&str> = value_var.split('.').collect();
                let store_inst = parts[0];
                let prop = parts[1];
                format!("self.${}.{}", store_inst, prop)
            } else {
                format!("self.${}", value_var)
            };
            
            let value_var_self = if value_var.contains('.') {
                format!("self.{}", value_var)
            } else {
                format!("self.{}", value_var)
            };
            
            code.push_str(&format!("{}TextField(\"\", text: {})\n", indent, binding_var));
            if !on_changed_cb.is_empty() {
                code.push_str(&format!("{}.onChange(of: {}) {{ _ in\n", indent, value_var_self));
                code.push_str(&format!("{}    self.{}()\n", indent, on_changed_cb));
                code.push_str(&format!("{}}}\n", indent));
            }
        }
        "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_swift_value_local(&prop.value, local_vars));
                    }
                    "color" => {
                        let val = translate_swift_value_local(&prop.value, local_vars);
                        let clean_color = val.replace("Colors.", ".").replace("Color.", ".");
                        color_val = Some(clean_color);
                    }
                    "borderRadius" => {
                        radius_val = Some(translate_swift_value_local(&prop.value, local_vars));
                    }
                    "child" => {
                        child_val = Some(&prop.value);
                    }
                    _ => {}
                }
            }
            
            code.push_str(&format!("{}VStack {{\n", indent));
            if let Some(Value::Element(child_el)) = child_val {
                code.push_str(&generate_swift_element_local(child_el, indent_level + 1, local_vars, custom_components));
            }
            code.push_str(&format!("{}}}\n", indent));
            
            if let Some(p) = padding_val {
                code.push_str(&format!("{}.padding({})\n", indent, p));
            }
            if let Some(c) = color_val {
                code.push_str(&format!("{}.background({})\n", indent, c));
            }
            if let Some(r) = radius_val {
                code.push_str(&format!("{}.cornerRadius({})\n", indent, r));
            }
        }
        "Navigator" => {
            let mut routes = Vec::new();
            let mut route_var = "self.currentRoute".to_string();
            
            for prop in &el.props {
                if prop.name == "initialRoute" {
                    let val = translate_swift_value_local(&prop.value, local_vars);
                    route_var = val;
                }
                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!("{}Group {{\n", indent));
            let mut is_first = true;
            for (path, page) in &routes {
                let cond = if is_first { "if" } else { "else if" };
                is_first = false;
                code.push_str(&format!("{}    {} {} == \"{}\" {{\n", indent, cond, route_var, path));
                code.push_str(&format!("{}        {}()\n", indent, page));
                code.push_str(&format!("{}    }}\n", indent));
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        "Route" => {}
        "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_swift_value_local(&prop.value, local_vars);
                    }
                    "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!("{}Group {{\n", indent));
            code.push_str(&format!("{}    if {} {{\n", indent, condition_val));
            if let Some(child_el) = child_el_opt {
                code.push_str(&generate_swift_element_local(child_el, indent_level + 2, local_vars, custom_components));
            }
            code.push_str(&format!("{}    }}", indent));
            if let Some(fallback_el) = fallback_el_opt {
                code.push_str(" else {\n");
                code.push_str(&generate_swift_element_local(fallback_el, indent_level + 2, local_vars, custom_components));
                code.push_str(&format!("{}    }}\n", indent));
            } else {
                code.push_str("\n");
            }
            code.push_str(&format!("{}}}\n", indent));
        }
        name if custom_components.contains(&name.to_string()) => {
            let mut params = Vec::new();
            for prop in &el.props {
                let val = translate_swift_value_local(&prop.value, local_vars);
                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));
        }
        _ => {
            code.push_str(&format!("{}// Unknown element {}\n", indent, el.name));
        }
    }
    
    code
}

/// Translates Value into its Swift representation string.
fn translate_swift_value(val: &Value) -> String {
    match val {
        Value::StringLit(s) => translate_swift_string(s),
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => id.clone(),
        _ => "\"\"".to_string(),
    }
}

/// Translates string literals containing variable interpolation ($var) into a SwiftUI string interpolation.
fn translate_swift_string(lit: &str) -> String {
    if lit.contains('$') {
        let chars: Vec<char> = lit.chars().collect();
        let mut i = 0;
        let mut new_str = 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;
                }
                new_str.push_str(&format!("\\(self.{})", var_name));
            } else {
                new_str.push(chars[i]);
                i += 1;
            }
        }
        format!("\"{}\"", new_str)
    } else {
        format!("\"{}\"", lit)
    }
}

/// Generates a SwiftUI ObservableObject class for a reactive store.
fn generate_store(comp: &ClassDecl) -> String {
    let mut code = String::new();
    
    code.push_str(&format!("class {}: ObservableObject {{\n", comp.name));
    code.push_str(&format!("    static let shared = {}()\n\n", comp.name));
    
    // Properties marked with @Published
    for prop in &comp.properties {
        let swift_type = match prop.type_name.as_str() {
            "int" | "Int" => "Int",
            "double" | "Double" => "Double",
            "bool" | "Bool" => "Bool",
            "String" => "String",
            _ => &prop.type_name,
        };
        let val_str = match &prop.value {
            Value::NumberLit(n) => n.clone(),
            Value::StringLit(s) => translate_swift_string(s),
            Value::Identifier(id) => id.clone(),
            _ => "\"\"".to_string(),
        };
        code.push_str(&format!("    @Published var {}: {} = {}\n", prop.name, swift_type, val_str));
    }
    code.push_str("\n");
    
    // Methods
    for method in &comp.methods {
        let return_arrow = match method.type_name.as_str() {
            "void" | "" => "".to_string(),
            "int" | "Int" => " -> Int".to_string(),
            "double" | "Double" => " -> Double".to_string(),
            "bool" | "Bool" => " -> Bool".to_string(),
            "String" => " -> String".to_string(),
            other => format!(" -> {}", other),
        };
        code.push_str(&format!("    func {}(){}{{\n", method.name, return_arrow));
        for stmt in &method.statements {
            let translated = translate_swift_statement(stmt);
            code.push_str(&format!("        {}", translated.trim_start()));
        }
        code.push_str("    }\n\n");
    }
    
    code.push_str("}\n\n");
    code
}

fn is_store_reference(id: &str) -> bool {
    id.contains('.') && id.chars().next().map_or(false, |c| c.is_lowercase())
}

fn translate_swift_value_local(val: &Value, local_vars: &[String]) -> String {
    match val {
        Value::StringLit(s) => translate_swift_string_local(s, local_vars),
        Value::NumberLit(n) => n.clone(),
        Value::Identifier(id) => {
            if local_vars.iter().any(|v| id == v || id.starts_with(&format!("{}.", v))) {
                id.clone()
            } else if is_store_reference(id) {
                format!("self.{}", id)
            } else if id.contains('.') {
                id.clone()
            } else {
                format!("self.{}", id)
            }
        }
        _ => "\"\"".to_string(),
    }
}

fn translate_swift_string_local(lit: &str, local_vars: &[String]) -> String {
    if lit.contains('$') {
        let chars: Vec<char> = lit.chars().collect();
        let mut i = 0;
        let mut new_str = 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 local_vars.iter().any(|v| var_name == *v || var_name.starts_with(&format!("{}.", v))) {
                    new_str.push_str(&format!("\\({})", var_name));
                } else {
                    new_str.push_str(&format!("\\(self.{})", var_name));
                }
            } else {
                new_str.push(chars[i]);
                i += 1;
            }
        }
        format!("\"{}\"", new_str)
    } else {
        format!("\"{}\"", lit)
    }
}

fn translate_swift_statement(stmt: &Statement) -> String {
    match stmt {
        Statement::Return(val) => {
            let val_str = translate_swift_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_swift_value(&args[0]);
                        if let Value::Identifier(ref cb) = args[1] {
                            return format!("// Http.get mock\n        let _url = {}\n        self.{}(data: \"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_swift_value(&args[0]);
                        let body = translate_swift_value(&args[1]);
                        if let Value::Identifier(ref cb) = args[2] {
                            return format!("// Http.post mock\n        let _url = {}\n        let _body = {}\n        self.{}(data: \"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        self.{}(location: \"-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_swift_value(&args[0]);
                        let body = translate_swift_value(&args[1]);
                        return format!("// Notification.show mock\n        print(\"\\({}): \\({})\")\n", title, body);
                    }
                }
            } else if expr.name == "Storage.set" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 2 {
                        let key = translate_swift_value(&args[0]);
                        let val = translate_swift_value(&args[1]);
                        return format!("UserDefaults.standard.set({}, forKey: {})\n", val, key);
                    }
                }
            } else if expr.name == "Storage.get" {
                if let Some(ref args) = expr.args {
                    if args.len() >= 1 {
                        let key = translate_swift_value(&args[0]);
                        return format!("UserDefaults.standard.string(forKey: {})\n", key);
                    }
                }
            }

            let actual_name = if expr.name.contains('.') {
                format!("self.{}", expr.name)
            } else {
                format!("self.{}", expr.name)
            };

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