// 37_compiler_plugins.ruchy - Compiler plugins and metaprogramming

import std::compiler
import std::ast
import std::macro

fn main() {
    println("=== Compiler Plugins & Metaprogramming ===\n")

    // AST manipulation
    println("=== AST Manipulation ===")

    // Parse code into AST
    let code = "fn add(x, y) { x + y }"
    let ast = compiler::parse(code)
    println(f"AST: {ast}")

    // Walk the AST
    ast.walk(node => {
        match node {
            Function(name, params, body) => {
                println(f"Found function: {name}")
                println(f"  Parameters: {params}")
            },
            BinaryOp(op, left, right) => {
                println(f"Found binary operation: {op}")
            },
            _ => {}
        }
    })

    // Transform AST
    let transformed = ast.transform(node => {
        match node {
            BinaryOp("+", left, right) => {
                // Replace addition with multiplication
                BinaryOp("*", left, right)
            },
            _ => node
        }
    })

    println(f"Transformed code: {compiler::unparse(transformed)}")

    // Code generation
    println("\n=== Code Generation ===")

    struct CodeGenerator {
        output: string = "",
        indent_level: int = 0
    }

    impl CodeGenerator {
        fn indent(self) -> string {
            "    " * self.indent_level
        }

        fn emit(mut self, code: string) {
            self.output += self.indent() + code + "\n"
        }

        fn emit_function(mut self, name: string, params: list, body: fn()) {
            let params_str = params.join(", ")
            self.emit(f"fn {name}({params_str}) {{")
            self.indent_level += 1
            body()
            self.indent_level -= 1
            self.emit("}")
        }

        fn emit_struct(mut self, name: string, fields: list) {
            self.emit(f"struct {name} {{")
            self.indent_level += 1
            for field in fields {
                self.emit(f"{field.name}: {field.type},")
            }
            self.indent_level -= 1
            self.emit("}")
        }
    }

    let gen = CodeGenerator {}

    gen.emit_struct("Person", [
        { name: "name", type: "string" },
        { name: "age", type: "int" },
        { name: "email", type: "Option<string>" }
    ])

    gen.emit_function("greet", ["self"], || {
        gen.emit('println(f"Hello, my name is {self.name}")')
    })

    println("Generated code:")
    println(gen.output)

    // Compile-time computation
    println("\n=== Compile-Time Computation ===")

    // Const functions evaluated at compile time
    const fn factorial(n: int) -> int {
        if n <= 1 { 1 } else { n * factorial(n - 1) }
    }

    const FACTORIAL_10 = factorial(10)
    println(f"10! = {FACTORIAL_10}")  // Computed at compile time

    // Const generics
    struct Matrix<const ROWS: int, const COLS: int> {
        data: [[float; COLS]; ROWS]
    }

    impl<const ROWS: int, const COLS: int> Matrix<ROWS, COLS> {
        fn transpose(self) -> Matrix<COLS, ROWS> {
            let mut result = Matrix::zeros()
            for i in 0..ROWS {
                for j in 0..COLS {
                    result.data[j][i] = self.data[i][j]
                }
            }
            result
        }
    }

    // Build script
    println("\n=== Build Script ===")

    #[build]
    fn generate_version_info() {
        let version = env::var("VERSION").unwrap_or("0.0.0")
        let commit = git::current_commit()
        let timestamp = datetime::now()

        compiler::emit(f"""
            const VERSION = "{version}"
            const COMMIT = "{commit}"
            const BUILD_TIME = "{timestamp}"
        """)
    }

    // Procedural macros
    println("\n=== Procedural Macros ===")

    #[proc_macro]
    fn derive_builder(input: TokenStream) -> TokenStream {
        let ast = parse_struct(input)
        let name = ast.name
        let builder_name = f"{name}Builder"
        let fields = ast.fields

        let mut output = TokenStream::new()

        // Generate builder struct
        output.append(f"struct {builder_name} {{")
        for field in fields {
            output.append(f"    {field.name}: Option<{field.type}>,")
        }
        output.append("}")

        // Generate builder methods
        output.append(f"impl {builder_name} {{")
        output.append("    fn new() -> Self {")
        output.append(f"        {builder_name} {{")
        for field in fields {
            output.append(f"            {field.name}: None,")
        }
        output.append("        }")
        output.append("    }")

        for field in fields {
            output.append(f"    fn {field.name}(mut self, value: {field.type}) -> Self {{")
            output.append(f"        self.{field.name} = Some(value);")
            output.append("        self")
            output.append("    }")
        }

        output.append(f"    fn build(self) -> Result<{name}, String> {{")
        for field in fields {
            if !field.optional {
                output.append(f'        let {field.name} = self.{field.name}.ok_or("{field.name} is required")?;')
            }
        }
        output.append(f"        Ok({name} {{")
        for field in fields {
            output.append(f"            {field.name},")
        }
        output.append("        })")
        output.append("    }")
        output.append("}")

        output
    }

    // Usage
    #[derive_builder]
    struct Config {
        host: string,
        port: int,
        timeout: Option<int>
    }

    let config = ConfigBuilder::new()
        .host("localhost")
        .port(8080)
        .timeout(30)
        .build()
        .unwrap()

    // Attribute macros
    println("\n=== Attribute Macros ===")

    #[benchmark]
    fn slow_function() {
        sleep_ms(100)
        println("Done!")
    }

    #[memoize]
    fn expensive_computation(n: int) -> int {
        println(f"Computing for {n}...")
        sleep_ms(1000)
        n * n
    }

    // First call computes
    expensive_computation(5)
    // Second call returns cached result
    expensive_computation(5)

    #[retry(attempts = 3, delay = 1000)]
    fn flaky_network_call() -> Result<String, Error> {
        if random() < 0.7 {
            Err(Error::NetworkError)
        } else {
            Ok("Success!")
        }
    }

    // Compiler plugins
    println("\n=== Compiler Plugins ===")

    struct LintPlugin {
        rules: list<LintRule>
    }

    impl CompilerPlugin for LintPlugin {
        fn process(self, ast: AST) -> list<Diagnostic> {
            let mut diagnostics = []

            for rule in self.rules {
                diagnostics.extend(rule.check(ast))
            }

            diagnostics
        }
    }

    struct NoUnwrapRule {}

    impl LintRule for NoUnwrapRule {
        fn check(self, ast: AST) -> list<Diagnostic> {
            let mut diagnostics = []

            ast.walk(node => {
                if let MethodCall(_, "unwrap", _) = node {
                    diagnostics.append(Diagnostic {
                        level: Warning,
                        message: "Avoid using unwrap(), use expect() or pattern matching",
                        location: node.span
                    })
                }
            })

            diagnostics
        }
    }

    // Register plugin
    compiler::register_plugin(LintPlugin {
        rules: [NoUnwrapRule {}]
    })

    // Syntax extensions
    println("\n=== Syntax Extensions ===")

    // Define custom syntax
    syntax! {
        rule query = "SELECT" fields:field_list
                     "FROM" table:ident
                     where_clause?
                     ";" => {
            SqlQuery {
                fields: fields,
                table: table,
                where: where_clause
            }
        }

        rule field_list = field ("," field)* => collect()
        rule field = ident | "*"
        rule where_clause = "WHERE" condition:expr => condition
    }

    // Use custom syntax
    let users = query! {
        SELECT name, email
        FROM users
        WHERE age >= 18;
    }

    // Type providers
    println("\n=== Type Providers ===")

    // Generate types from external schema
    #[type_provider("schema.json")]
    struct ApiTypes {}

    // This generates:
    // struct User { ... }
    // struct Product { ... }
    // etc. based on JSON schema

    let user: ApiTypes::User = fetch_user(123)

    // Compile-time validation
    println("\n=== Compile-Time Validation ===")

    // Regex validation at compile time
    #[regex("^[a-zA-Z0-9]+$")]
    const USERNAME_PATTERN: Regex

    // SQL validation at compile time
    #[sql("SELECT * FROM users WHERE id = ?")]
    const GET_USER_QUERY: SqlQuery

    // Format string validation
    #[format_args("Hello, {name}! You have {count} messages.")]
    fn greeting_template(name: string, count: int) -> string

    // Contract programming
    println("\n=== Contract Programming ===")

    #[requires(x > 0, "x must be positive")]
    #[ensures(result >= 0, "result must be non-negative")]
    fn sqrt(x: float) -> float {
        x.sqrt()
    }

    #[invariant(self.size >= 0, "size must be non-negative")]
    struct Stack<T> {
        items: Vec<T>,
        size: int
    }

    // Plugin configuration
    println("\n=== Plugin Configuration ===")

    // ruchy.plugins.toml
    let plugin_config = r#"
        [[plugins]]
        name = "ruchy-formatter"
        version = "1.0.0"

        [[plugins]]
        name = "ruchy-linter"
        version = "2.3.1"
        config = { max_line_length = 100, indent = 4 }

        [[plugins]]
        name = "ruchy-test-generator"
        version = "0.5.0"
        only_in = ["test"]
    "#

    println("Compiler plugins configured!")
}