pub mod ast;
pub mod parser;
pub mod generator;
pub mod optimizer;
pub mod dev_server;

use pest_derive::Parser;

/// Main Liquid parser generated using the Pest library.
#[derive(Parser)]
#[grammar = "liquid.pest"]
pub struct LiquidParser;

#[cfg(test)]
mod tests {
    use super::*;
    use pest::Parser;
    use crate::parser::parse_liquid;
    use crate::optimizer::optimize_ast;
    use crate::generator::web::generate_web;
    use crate::generator::js::generate_js;
    use crate::generator::swift::generate_swift;
    use crate::generator::kotlin::generate_kotlin;
    use crate::generator::winui::generate_winui;
    use crate::ast::*;

    /// Test direct Pest parsing of a valid component class.
    #[test]
    fn test_parse_class() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            String title = "Minimal Clicker";

            void increment() {
                count++;
            }

            @override
            Widget build() {
                return Column(
                    alignment: Alignment.center,
                    spacing: 16.0,
                    children: [
                        Text(
                            "$title: $count",
                            style: TextStyle(
                                fontSize: 24.0,
                                weight: FontWeight.bold
                            )
                        ),
                        Button(
                            label: "Tambah Data",
                            onPressed: increment
                        )
                    ]
                );
            }
        }
        "#;
        let parse_result = LiquidParser::parse(Rule::file, input);
        assert!(parse_result.is_ok(), "Failed to parse valid class: {:?}", parse_result.err());
    }

    /// Test validation of syntax error handling by the parser.
    #[test]
    fn test_parse_invalid_class() {
        let input = r#"
        class Counter extends Component 
            int count = 0;
        }
        "#; // Missing opening bracket `{` after Component
        let parse_result = LiquidParser::parse(Rule::file, input);
        assert!(parse_result.is_err(), "Should have failed parsing invalid syntax");
    }

    /// Test mapping of the AST and verifying all parsed properties.
    #[test]
    fn test_ast_mapping_valid() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;

            @override
            Widget build() {
                return Button(label: "Tambah");
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Should successfully map AST");
        
        assert_eq!(ast.imports.len(), 1);
        assert_eq!(ast.imports[0].path, "liquid:ui");

        assert_eq!(ast.classes.len(), 1);
        let class = &ast.classes[0];
        assert_eq!(class.name, "Counter");
        assert_eq!(class.extends_class, "Component");

        assert_eq!(class.properties.len(), 1);
        assert_eq!(class.properties[0].name, "count");
        assert_eq!(class.properties[0].type_name, "int");
        assert_eq!(class.properties[0].value, Value::NumberLit("0".to_string()));

        assert_eq!(class.render.name, "build");
        assert_eq!(class.render.type_name, "Widget");
        assert_eq!(class.render.annotation.as_deref(), Some("@override"));
        assert_eq!(class.render.element.name, "Button");
    }

    /// Test code generation for the Web target (Rust/WASM).
    #[test]
    fn test_web_generator() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            void increment() {
                count++;
            }

            @override
            Widget build() {
                return Button(label: "Tambah", onPressed: increment);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");
        let code = generate_web(&ast);

        assert!(code.contains("pub struct Counter"));
        assert!(code.contains("pub count: i32"));
        assert!(code.contains("pub fn increment(&mut self) -> ()"));
        assert!(code.contains("pub fn render(&mut self, parent_id: &str)"));
    }

    /// Test code generation for the iOS target (SwiftUI).
    #[test]
    fn test_swift_generator() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            void increment() {
                count++;
            }

            @override
            Widget build() {
                return Button(label: "Tambah", onPressed: increment);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");
        let code = generate_swift(&ast);

        assert!(code.contains("import SwiftUI"));
        assert!(code.contains("struct Counter: View"));
        assert!(code.contains("@State var count: Int = 0"));
        assert!(code.contains("func increment()"));
        assert!(code.contains("Button"));
    }

    /// Test code generation for the Android target (Jetpack Compose).
    #[test]
    fn test_kotlin_generator() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            void increment() {
                count++;
            }

            @override
            Widget build() {
                return Button(label: "Tambah", onPressed: increment);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");
        let code = generate_kotlin(&ast);

        assert!(code.contains("import androidx.compose.runtime.*"));
        assert!(code.contains("fun Counter()"));
        assert!(code.contains("var count by remember { mutableStateOf(0) }"));
        assert!(code.contains("fun increment()"));
        assert!(code.contains("Button(onClick ="));
    }

    /// Test code generation for the Windows Desktop target (WinUI 3).
    #[test]
    fn test_winui_generator() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            void increment() {
                count++;
            }

            @override
            Widget build() {
                return Button(label: "Tambah", onPressed: increment);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");
        let code = generate_winui(&ast);

        assert!(code.contains("<Button Content=\"Tambah\""));
        assert!(code.contains("namespace LiquidApp"));
        assert!(code.contains("public sealed partial class Counter : UserControl"));
        assert!(code.contains("private int _count = 0;"));
        assert!(code.contains("public int Count"));
    }

    /// Test dead-code and dead-method elimination optimizations.
    #[test]
    fn test_dead_code_elimination() {
        let input = r#"
        import "liquid:ui";

        class Counter extends Component {
            int count = 0;
            int unused_var = 42;
            String title = "Minimal Clicker";

            void increment() {
                count++;
            }

            void unused_method() {
                unused_var--;
            }

            @override
            Widget build() {
                return Column(
                    children: [
                        Text(text: title)
                    ]
                );
            }
        }
        "#;
        
        let ast = parse_liquid(input).expect("Parse success");
        let optimized_ast = optimize_ast(ast);
        
        let class = &optimized_ast.classes[0];
        // 'unused_method' is dead because it's not referenced in render block.
        // 'increment' is dead because it's not referenced in render block.
        // 'unused_var' is dead because it's only modified in 'unused_method' (which is dead).
        // 'count' is dead because it's only modified in 'increment' (which is dead).
        // 'title' is alive because it's used directly in the Text element.
        assert_eq!(class.properties.len(), 1, "Only 1 property (title) should be retained");
        assert_eq!(class.properties[0].name, "title");
        assert_eq!(class.methods.len(), 0, "All dead methods should be cleaned up");
    }

    /// Test TextStyle and dynamic layout spacing/alignment attributes in generators.
    #[test]
    fn test_styling_and_theming() {
        let input = r#"
        import "liquid:ui";

        class StyledApp extends Component {
            @override
            Widget build() {
                return Column(
                    alignment: Alignment.center,
                    spacing: 12.0,
                    children: [
                        Text(
                            "Styled Title",
                            style: TextStyle(
                                fontSize: 20.0,
                                weight: FontWeight.bold,
                                color: Colors.blue
                            )
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        // 1. Web Generator output assertions
        let web_code = generate_web(&ast);
        assert!(web_code.contains("font-size: 20px"));
        assert!(web_code.contains("font-weight: bold"));
        assert!(web_code.contains("color: blue"));
        assert!(web_code.contains("align-items: center"));
        assert!(web_code.contains("gap: 12px"));

        // 2. SwiftUI Generator output assertions
        let swift_code = generate_swift(&ast);
        assert!(swift_code.contains(".font(.system(size: 20.0))"));
        assert!(swift_code.contains(".fontWeight(.bold)"));
        assert!(swift_code.contains("spacing: 12.0"));

        // 3. Kotlin Compose Generator output assertions
        let kotlin_code = generate_kotlin(&ast);
        assert!(kotlin_code.contains("fontSize = 20.0.sp"));
        assert!(kotlin_code.contains("fontWeight = FontWeight.Bold"));
        assert!(kotlin_code.contains("color = Color.blue"));
        assert!(kotlin_code.contains("spacedBy(12.0.dp)"));

        // 4. WinUI Generator output assertions
        let winui_code = generate_winui(&ast);
        assert!(winui_code.contains("FontSize=\"20.0\""));
        assert!(winui_code.contains("FontWeight=\"Bold\""));
        assert!(winui_code.contains("Foreground=\"blue\""));
        assert!(winui_code.contains("Spacing=\"12.0\""));
    }

    /// Test Navigator and Route matching across all platform generators.
    #[test]
    fn test_routing_and_navigation() {
        let input = r#"
        import "liquid:ui";

        class NavApp extends Component {
            String activeRoute = "/home";

            @override
            Widget build() {
                return Navigator(
                    initialRoute: activeRoute,
                    children: [
                        Route(path: "/home", page: HomeScreen),
                        Route(path: "/details", page: DetailsScreen)
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        // 1. Web Generator assertions
        let web_code = generate_web(&ast);
        assert!(web_code.contains("let mut page = HomeScreen::new();"));
        assert!(web_code.contains("page.render(\"el_0_nav\")?;"));
        assert!(web_code.contains("let mut page = DetailsScreen::new();"));

        // 2. JS Generator assertions
        let js_code = generate_js(&ast);
        assert!(js_code.contains("const page = new HomeScreen();"));
        assert!(js_code.contains("page.render(\"el_0_nav\");"));

        // 3. SwiftUI Generator assertions
        let swift_code = generate_swift(&ast);
        assert!(swift_code.contains("if self.activeRoute == \"/home\" {"));
        assert!(swift_code.contains("HomeScreen()"));
        assert!(swift_code.contains("else if self.activeRoute == \"/details\" {"));
        assert!(swift_code.contains("DetailsScreen()"));

        // 4. Compose Generator assertions
        let kotlin_code = generate_kotlin(&ast);
        assert!(kotlin_code.contains("when (activeRoute) {"));
        assert!(kotlin_code.contains("\"/home\" -> HomeScreen()"));
        assert!(kotlin_code.contains("\"/details\" -> DetailsScreen()"));

        // 5. WinUI Generator assertions
        let winui_code = generate_winui(&ast);
        assert!(winui_code.contains("<ContentControl x:Name=\"NavigatorContainer\""));
        assert!(winui_code.contains("switch (this._activeRoute)"));
        assert!(winui_code.contains("case \"/home\":"));
        assert!(winui_code.contains("this.NavigatorContainer.Content = new HomeScreen();"));
    }

    #[test]
    fn test_global_reactive_stores() {
        let input = r#"
        import "liquid:ui";

        class AppStore extends Store {
            int count = 0;
            void increment() {
                count++;
            }
        }

        class CounterApp extends Component {
            void handleIncrement() {
                appStore.increment();
            }

            @override
            Widget build() {
                return Column(
                    children: [
                        Button(
                            label: "Count: $appStore.count",
                            onPressed: handleIncrement
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        // 1. Web Generator assertions
        let web_code = generate_web(&ast);
        assert!(web_code.contains("pub struct AppStore"));
        assert!(web_code.contains("pub static APPSTORE: RefCell<AppStore>"));
        assert!(web_code.contains("APPSTORE.with(|s| s.borrow_mut().increment());"));
        assert!(web_code.contains("APPSTORE.with(|s| s.borrow().count.clone())"));

        // 2. JS Generator assertions
        let js_code = generate_js(&ast);
        assert!(js_code.contains("class AppStore"));
        assert!(js_code.contains("const appStore = new AppStore();"));
        assert!(js_code.contains("appStore.subscribe(() => this.update_dom());"));
        assert!(js_code.contains("appStore.increment()"));

        // 3. SwiftUI Generator assertions
        let swift_code = generate_swift(&ast);
        assert!(swift_code.contains("class AppStore: ObservableObject"));
        assert!(swift_code.contains("static let shared = AppStore()"));
        assert!(swift_code.contains("@ObservedObject var appStore: AppStore = AppStore.shared"));
        assert!(swift_code.contains("self.appStore.count"));

        // 4. Compose Generator assertions
        let kotlin_code = generate_kotlin(&ast);
        assert!(kotlin_code.contains("object AppStore"));
        assert!(kotlin_code.contains("var count by mutableStateOf(0)"));
        assert!(kotlin_code.contains("AppStore.increment()"));
        assert!(kotlin_code.contains("AppStore.count"));

        // 5. WinUI Generator assertions
        let winui_code = generate_winui(&ast);
        assert!(winui_code.contains("public class AppStore : INotifyPropertyChanged"));
        assert!(winui_code.contains("public static readonly AppStore Instance = new AppStore();"));
        assert!(winui_code.contains("AppStore.Instance.PropertyChanged += (s, e) => OnPropertyChanged(\"\");"));
        assert!(winui_code.contains("AppStore.Instance.Count"));
    }

    #[test]
    fn test_dynamic_lists_for() {
        let input = r#"
        import "liquid:ui";
        class TodoApp extends Component {
            @override
            Widget build() {
                return Column(
                    children: [
                        For(
                            items: todoStore.todos,
                            item: "todo",
                            child: Text("$todo")
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("todoStore.todos"));
        assert!(js.contains("items.map"));

        let web = generate_web(&ast);
        assert!(web.contains(".iter().map"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("ForEach"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("for (todo in TodoStore.todos)"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("<StackPanel x:Name=\"TodoStoreTodosContainer\" />"));
        assert!(winui.contains("private void UpdateTodoStoreTodosContainer()"));
    }

    #[test]
    fn test_text_field() {
        let input = r#"
        import "liquid:ui";
        class InputApp extends Component {
            String textVal = "";
            void handleChange() {}
            @override
            Widget build() {
                return Column(
                    children: [
                        TextField(
                            value: textVal,
                            onChanged: handleChange
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("input"));
        assert!(js.contains("handleChange"));

        let web = generate_web(&ast);
        assert!(web.contains("input"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("TextField"));
        assert!(swift.contains("onChange"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("OutlinedTextField"));
        assert!(kotlin.contains("onValueChange"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("<TextBox Text=\"{x:Bind TextVal, Mode=TwoWay}\" TextChanged=\"TextVal_TextChanged\" />"));
        assert!(winui.contains("private void TextVal_TextChanged"));
    }

    #[test]
    fn test_container_layout() {
        let input = r#"
        import "liquid:ui";
        class LayoutApp extends Component {
            @override
            Widget build() {
                return Column(
                    children: [
                        Container(
                            padding: 16.0,
                            color: Colors.blue,
                            borderRadius: 8.0,
                            child: Text("Hello")
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("padding: 16"));
        assert!(js.contains("background-color: blue"));
        assert!(js.contains("border-radius: 8"));

        let web = generate_web(&ast);
        assert!(web.contains("padding: 16"));

        let swift = generate_swift(&ast);
        assert!(swift.contains(".padding(16"));
        assert!(swift.contains(".background(.blue)"));
        assert!(swift.contains(".cornerRadius(8"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("padding(16"));
        assert!(kotlin.contains("background(color = Color.blue"));
        assert!(kotlin.contains("RoundedCornerShape(8"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("Padding=\"16"));
        assert!(winui.contains("Background=\"blue\""));
        assert!(winui.contains("CornerRadius=\"8"));
    }

    #[test]
    fn test_lifecycle_hooks() {
        let input = r#"
        import "liquid:ui";
        class HookApp extends Component {
            void onInit() {}
            void onDispose() {}
            @override
            Widget build() {
                return Text("Lifecycle");
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("this.onInit()"));

        let web = generate_web(&ast);
        assert!(web.contains("self.onInit()"));
        assert!(web.contains("old_s.onDispose()"));

        let swift = generate_swift(&ast);
        assert!(swift.contains(".onAppear"));
        assert!(swift.contains(".onDisappear"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("DisposableEffect(Unit)"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("this.Loaded += (s, e) => { this.OnInit(); }"));
        assert!(winui.contains("this.Unloaded += (s, e) => { this.OnDispose(); }"));
    }

    #[test]
    fn test_storage_bridge() {
        let input = r#"
        import "liquid:ui";
        class StorageApp extends Component {
            void save() {
                Storage.set("name", "Antigravity");
            }
            void load() {
                Storage.get("name");
            }
            @override
            Widget build() {
                return Text("Storage");
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("localStorage.setItem"));
        assert!(js.contains("localStorage.getItem"));

        let web = generate_web(&ast);
        assert!(web.contains("local_storage().unwrap().unwrap().set_item"));
        assert!(web.contains("local_storage().unwrap().unwrap().get_item"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("UserDefaults.standard.set"));
        assert!(swift.contains("UserDefaults.standard.string"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("Storage.set"));
        assert!(kotlin.contains("Storage.get"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("Storage.Set"));
        assert!(winui.contains("Storage.Get"));
    }

    #[test]
    fn test_conditional_if() {
        let input = r#"
        import "liquid:ui";
        class IfApp extends Component {
            bool visible = true;
            @override
            Widget build() {
                return Column(
                    children: [
                        If(
                            condition: visible,
                            child: Text("Is Visible"),
                            fallback: Text("Is Hidden")
                        )
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("el_1_if"));
        assert!(js.contains("this.visible"));

        let web = generate_web(&ast);
        println!("WEB OUTPUT:\n{}", web);
        assert!(web.contains("self.visible"));
        assert!(web.contains("else {"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("Group {"));
        assert!(swift.contains("if self.visible {"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("if (visible) {"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("<ContentControl x:Name=\"IfContainer_1\" />"));
        assert!(winui.contains("UpdateIfContainer_1()"));
    }

    #[test]
    fn test_custom_components_props() {
        let input = r#"
        import "liquid:ui";
        class Card extends Component {
            String name = "";
            @override
            Widget build() {
                return Text("Name: $name");
            }
        }

        class ProfileApp extends Component {
            @override
            Widget build() {
                return Column(
                    children: [
                        Card(name: "John")
                    ]
                );
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("new Card()"));
        assert!(js.contains("comp_2.name = \"John\""));

        let web = generate_web(&ast);
        assert!(web.contains("Card::new()"));
        assert!(web.contains("comp_2.name = \"John\""));

        let swift = generate_swift(&ast);
        assert!(swift.contains("Card(name: \"John\")"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("Card(name = \"John\")"));

        let winui = generate_winui(&ast);
        println!("WINUI OUTPUT:\n{}", winui);
        assert!(winui.contains("<local:Card Name=\"John\""));
    }

    #[test]
    fn test_method_returns() {
        let input = r#"
        import "liquid:ui";
        class CalcStore extends Store {
            int factor = 2;
            int getFactor() {
                return factor;
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("getFactor() {"));
        assert!(js.contains("return this.factor;"));

        let web = generate_web(&ast);
        assert!(web.contains("pub fn getFactor(&mut self) -> i32 {"));
        assert!(web.contains("return self.factor.clone();"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("func getFactor() -> Int"));
        assert!(swift.contains("return factor"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("fun getFactor(): Int"));
        assert!(kotlin.contains("return factor"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("public int GetFactor()"));
        assert!(winui.contains("return this.Factor;"));
    }

    #[test]
    fn test_http_client() {
        let input = r#"
        import "liquid:ui";
        class NetworkApp extends Component {
            String data = "";
            void handleResponse() {
            }
            void fetchData() {
                Http.get("https://api.example.com/data", handleResponse);
            }
            @override
            Widget build() {
                return Button(label: data, onPressed: fetchData);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("fetch("));
        assert!(js.contains("this.handleResponse"));

        let web = generate_web(&ast);
        assert!(web.contains("Http.get mock"));
        assert!(web.contains("self.handleResponse"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("Http.get mock"));
        assert!(swift.contains("self.handleResponse"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("Http.get mock"));
        assert!(kotlin.contains("handleResponse"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("Http.get mock"));
        assert!(winui.contains("this.HandleResponse"));
    }

    #[test]
    fn test_device_bridges() {
        let input = r#"
        import "liquid:ui";
        class BridgeApp extends Component {
            String location = "";
            void handleLocation() {
            }
            void runBridges() {
                Location.current(handleLocation);
                Notification.show("Title", "Body");
            }
            @override
            Widget build() {
                return Button(label: location, onPressed: runBridges);
            }
        }
        "#;
        let ast = parse_liquid(input).expect("Parse success");

        let js = generate_js(&ast);
        assert!(js.contains("navigator.geolocation.getCurrentPosition"));
        assert!(js.contains("new Notification"));

        let web = generate_web(&ast);
        assert!(web.contains("Location.current mock"));
        assert!(web.contains("Notification.show mock"));

        let swift = generate_swift(&ast);
        assert!(swift.contains("Location.current mock"));
        assert!(swift.contains("Notification.show mock"));

        let kotlin = generate_kotlin(&ast);
        assert!(kotlin.contains("Location.current mock"));
        assert!(kotlin.contains("Notification.show mock"));

        let winui = generate_winui(&ast);
        assert!(winui.contains("Location.current mock"));
        assert!(winui.contains("Notification.show mock"));
    }
}
