import "../../runtime/eltest.el"
// tests/native/test_compiler.el — comprehensive tests for the El compiler pipeline.
//
// Tests the lexer (lexer.el), parser (parser.el), and codegen (codegen.el)
// through the compile() entry point in compiler.el.
//
// Compiled and run via the native test harness:
//   elc --test tests/native/test_compiler.el > /tmp/el_compiler_tests.c
//   gcc -O2 -I runtime /tmp/el_compiler_tests.c runtime/el_runtime.c -lcurl -lpthread -lm -o /tmp/el_compiler_tests
//   /tmp/el_compiler_tests

import "../../el-compiler/src/lexer.el"
import "../../el-compiler/src/parser.el"
import "../../el-compiler/src/codegen.el"
import "../../el-compiler/src/codegen-js.el"
import "../../el-compiler/src/compiler.el"

// ── Lexer helpers ─────────────────────────────────────────────────────────────

fn tok_count(tokens: [Any]) -> Int {
    // A token is (kind, value, line). This helper carried its own copy of the
    // stride, so it escaped a search scoped to the compiler sources.
    native_list_len(tokens) / 3
}

// ── Codegen helper: capture compile() stdout to a string ─────────────────────

fn compile_capture(src: String) -> String {
    let tmp: String = "/tmp/el_compiler_test_" + int_to_str(time_now()) + ".c"
    stdout_to_file(tmp)
    compile(src)
    stdout_restore()
    fs_read(tmp)
}

// ── Lexer tests ───────────────────────────────────────────────────────────────

test "lex-empty" {
    let tokens: [Any] = lex("")
    assert tok_count(tokens) == 1, "empty source yields only Eof"
    assert tok_kind(tokens, 0) == "Eof", "single token is Eof"
}

test "lex-whitespace-stripped" {
    let tokens: [Any] = lex("   \t\n\r   ")
    assert tok_count(tokens) == 1, "whitespace-only yields only Eof"
}

test "lex-comment-stripped" {
    let tokens: [Any] = lex("// this is a comment\n// another")
    assert tok_count(tokens) == 1, "comments stripped — only Eof"
}

test "lex-int-literals" {
    let tokens: [Any] = lex("0 1 42 100 999")
    assert tok_count(tokens) == 6, "five int literals + Eof"
    assert tok_kind(tokens, 0) == "Int", "first is Int"
    assert tok_value(tokens, 0) == "0", "value is 0"
    assert tok_kind(tokens, 2) == "Int", "third is Int"
    assert tok_value(tokens, 2) == "42", "value is 42"
    assert tok_kind(tokens, 4) == "Int", "fifth is Int"
    assert tok_value(tokens, 4) == "999", "value is 999"
}

test "lex-float-literals" {
    let tokens: [Any] = lex("3.14 0.0 1.5")
    assert tok_count(tokens) == 4, "three float literals + Eof"
    assert tok_kind(tokens, 0) == "Float", "first is Float"
    assert tok_value(tokens, 0) == "3.14", "value is 3.14"
    assert tok_kind(tokens, 1) == "Float", "second is Float"
    assert tok_value(tokens, 1) == "0.0", "value is 0.0"
}

test "lex-string-literals" {
    let tokens: [Any] = lex("\"hello\" \"world\" \"\"")
    assert tok_count(tokens) == 4, "three string literals + Eof"
    assert tok_kind(tokens, 0) == "Str", "first is Str"
    assert tok_value(tokens, 0) == "hello", "value is hello"
    assert tok_kind(tokens, 2) == "Str", "third is Str"
    assert tok_value(tokens, 2) == "", "empty string value is empty"
}

test "lex-string-escape-newline" {
    let tokens: [Any] = lex("\"hello\\nworld\"")
    assert tok_count(tokens) == 2, "one Str token + Eof"
    assert tok_kind(tokens, 0) == "Str", "is Str"
    let val: String = tok_value(tokens, 0)
    assert str_contains(val, "hello"), "value contains hello"
    assert str_contains(val, "world"), "value contains world"
    assert str_len(val) == 11, "hello + newline + world = 11 chars"
}

test "lex-string-escape-tab" {
    let tokens: [Any] = lex("\"a\\tb\"")
    assert tok_count(tokens) == 2, "one Str + Eof"
    let val: String = tok_value(tokens, 0)
    assert str_len(val) == 3, "a + tab + b = 3 chars"
}

test "lex-string-escape-backslash" {
    let tokens: [Any] = lex("\"a\\\\b\"")
    assert tok_count(tokens) == 2, "one Str + Eof"
    let val: String = tok_value(tokens, 0)
    assert str_len(val) == 3, "a + backslash + b = 3 chars"
}

test "lex-bool-literals" {
    let tokens: [Any] = lex("true false")
    assert tok_count(tokens) == 3, "two Bool tokens + Eof"
    assert tok_kind(tokens, 0) == "Bool", "first is Bool"
    assert tok_value(tokens, 0) == "true", "first is true"
    assert tok_kind(tokens, 1) == "Bool", "second is Bool"
    assert tok_value(tokens, 1) == "false", "second is false"
}

test "lex-identifier" {
    let tokens: [Any] = lex("foo bar _under _123")
    assert tok_count(tokens) == 5, "four idents + Eof"
    assert tok_kind(tokens, 0) == "Ident", "foo is Ident"
    assert tok_value(tokens, 0) == "foo", "value is foo"
    assert tok_kind(tokens, 2) == "Ident", "underscore ident recognized"
    assert tok_value(tokens, 2) == "_under", "value is _under"
}

test "lex-keywords" {
    let tokens: [Any] = lex("let fn if else while for return import type enum match")
    assert tok_count(tokens) == 12, "eleven keywords + Eof"
    assert tok_kind(tokens, 0) == "Let", "let keyword"
    assert tok_kind(tokens, 1) == "Fn", "fn keyword"
    assert tok_kind(tokens, 2) == "If", "if keyword"
    assert tok_kind(tokens, 3) == "Else", "else keyword"
    assert tok_kind(tokens, 4) == "While", "while keyword"
    assert tok_kind(tokens, 5) == "For", "for keyword"
    assert tok_kind(tokens, 6) == "Return", "return keyword"
    assert tok_kind(tokens, 7) == "Import", "import keyword"
    assert tok_kind(tokens, 8) == "Type", "type keyword"
    assert tok_kind(tokens, 9) == "Enum", "enum keyword"
    assert tok_kind(tokens, 10) == "Match", "match keyword"
}

test "lex-more-keywords" {
    let tokens: [Any] = lex("extern break continue")
    assert tok_count(tokens) == 4, "three keywords + Eof"
    assert tok_kind(tokens, 0) == "Extern", "extern keyword"
    assert tok_kind(tokens, 1) == "Break", "break keyword"
    assert tok_kind(tokens, 2) == "Continue", "continue keyword"
}

test "lex-keyword-values" {
    let tokens: [Any] = lex("let fn return")
    assert tok_value(tokens, 0) == "let", "let value is let"
    assert tok_value(tokens, 1) == "fn", "fn value is fn"
    assert tok_value(tokens, 2) == "return", "return value is return"
}

test "lex-arithmetic-operators" {
    let tokens: [Any] = lex("+ - * / %")
    assert tok_count(tokens) == 6, "five ops + Eof"
    assert tok_kind(tokens, 0) == "Plus", "plus"
    assert tok_kind(tokens, 1) == "Minus", "minus"
    assert tok_kind(tokens, 2) == "Star", "star"
    assert tok_kind(tokens, 3) == "Slash", "slash"
    assert tok_kind(tokens, 4) == "Percent", "percent"
}

test "lex-comparison-operators" {
    let tokens: [Any] = lex("== != < > <= >=")
    assert tok_count(tokens) == 7, "six ops + Eof"
    assert tok_kind(tokens, 0) == "EqEq", "eqeq"
    assert tok_value(tokens, 0) == "==", "eqeq value"
    assert tok_kind(tokens, 1) == "NotEq", "noteq"
    assert tok_kind(tokens, 2) == "Lt", "lt"
    assert tok_kind(tokens, 3) == "Gt", "gt"
    assert tok_kind(tokens, 4) == "LtEq", "lteq"
    assert tok_kind(tokens, 5) == "GtEq", "gteq"
}

test "lex-logical-operators" {
    let tokens: [Any] = lex("&& || !")
    assert tok_count(tokens) == 4, "three logical ops + Eof"
    assert tok_kind(tokens, 0) == "And", "and"
    assert tok_value(tokens, 0) == "&&", "and value"
    assert tok_kind(tokens, 1) == "Or", "or"
    assert tok_kind(tokens, 2) == "Not", "not"
}

test "lex-arrow-tokens" {
    let tokens: [Any] = lex("-> =>")
    assert tok_count(tokens) == 3, "arrow + fat-arrow + Eof"
    assert tok_kind(tokens, 0) == "Arrow", "thin arrow"
    assert tok_value(tokens, 0) == "->", "arrow value"
    assert tok_kind(tokens, 1) == "FatArrow", "fat arrow"
}

test "lex-delimiters" {
    let tokens: [Any] = lex("( ) [ ] { } , : ; .")
    assert tok_count(tokens) == 11, "ten delimiters + Eof"
    assert tok_kind(tokens, 0) == "LParen", "lparen"
    assert tok_kind(tokens, 1) == "RParen", "rparen"
    assert tok_kind(tokens, 2) == "LBracket", "lbracket"
    assert tok_kind(tokens, 3) == "RBracket", "rbracket"
    assert tok_kind(tokens, 4) == "LBrace", "lbrace"
    assert tok_kind(tokens, 5) == "RBrace", "rbrace"
    assert tok_kind(tokens, 6) == "Comma", "comma"
    assert tok_kind(tokens, 7) == "Colon", "colon"
    assert tok_kind(tokens, 8) == "Semicolon", "semicolon"
    assert tok_kind(tokens, 9) == "Dot", "dot"
}

test "lex-double-colon" {
    let tokens: [Any] = lex("::")
    assert tok_count(tokens) == 2, "colons + Eof"
    assert tok_kind(tokens, 0) == "ColonColon", "double colon"
    assert tok_value(tokens, 0) == "::", "double colon value"
}

test "lex-dot-dot" {
    let tokens: [Any] = lex(".. ..=")
    assert tok_count(tokens) == 3, "two range tokens + Eof"
    assert tok_kind(tokens, 0) == "DotDot", "dotdot"
    assert tok_kind(tokens, 1) == "DotDotEq", "dotdoteq"
}

test "lex-pipe-operators" {
    let tokens: [Any] = lex("| || |>")
    assert tok_count(tokens) == 4, "three pipe tokens + Eof"
    assert tok_kind(tokens, 0) == "Pipe", "pipe"
    assert tok_kind(tokens, 1) == "Or", "or"
    assert tok_kind(tokens, 2) == "PipeOp", "pipe-op"
}

test "lex-at-and-question" {
    let tokens: [Any] = lex("@ ?")
    assert tok_count(tokens) == 3, "at + question + Eof"
    assert tok_kind(tokens, 0) == "At", "at sign"
    assert tok_kind(tokens, 1) == "QuestionMark", "question mark"
}

test "lex-eof-always-last" {
    let t1: [Any] = lex("x")
    let t2: [Any] = lex("let x = 1")
    let t3: [Any] = lex("")
    let n1: Int = tok_count(t1)
    let n2: Int = tok_count(t2)
    let n3: Int = tok_count(t3)
    assert tok_kind(t1, n1 - 1) == "Eof", "eof last after single ident"
    assert tok_kind(t2, n2 - 1) == "Eof", "eof last after let stmt"
    assert tok_kind(t3, n3 - 1) == "Eof", "eof last after empty"
}

test "lex-string-with-spaces" {
    let tokens: [Any] = lex("\"hello world\"")
    assert tok_count(tokens) == 2, "string with space: 1 Str + Eof"
    assert tok_value(tokens, 0) == "hello world", "internal space preserved"
}

test "lex-multiline-source" {
    let src: String = "let x: Int = 1\nlet y: Int = 2\n"
    let tokens: [Any] = lex(src)
    assert tok_count(tokens) > 5, "multiline source produces multiple tokens"
    assert tok_kind(tokens, 0) == "Let", "first token is Let"
}

test "lex-flat-stride-3-layout" {
    // A token is (kind, value, line): token i has kind at 3*i, value at 3*i+1,
    // line at 3*i+2. Before 2026-08-17 a token carried no position at all, so
    // no diagnostic in El could name a place.
    let tokens: [Any] = lex("fn foo")
    let raw_len: Int = native_list_len(tokens)
    assert raw_len == 9, "fn + foo + Eof = 3 tokens = 9 raw entries"
    assert native_list_get(tokens, 0) == "Fn", "raw[0] is the kind"
    assert native_list_get(tokens, 1) == "fn", "raw[1] is the value"
    assert native_list_get(tokens, 2) == "1", "raw[2] is the line"
    assert native_list_get(tokens, 3) == "Ident", "raw[3] is the next kind"
    assert native_list_get(tokens, 5) == "1", "still line 1"
}

test "lexer-tracks-line-numbers" {
    let tokens: [Any] = lex("fn a\nfn b\nfn c")
    assert tok_line(tokens, 0) == "1", "first fn is on line 1"
    assert tok_line(tokens, 2) == "2", "second fn is on line 2"
    assert tok_line(tokens, 4) == "3", "third fn is on line 3"
}


// ── Parser tests ──────────────────────────────────────────────────────────────

fn get_first_stmt_kind(src: String) -> String {
    let tokens: [Any] = lex(src)
    let stmts: [Map<String, Any>] = parse(tokens)
    if native_list_len(stmts) == 0 { return "" }
    let first: Map<String, Any> = native_list_get(stmts, 0)
    first["stmt"]
}

fn get_first_stmt(src: String) -> Map<String, Any> {
    let tokens: [Any] = lex(src)
    let stmts: [Map<String, Any>] = parse(tokens)
    native_list_get(stmts, 0)
}

test "parse-let-stmt" {
    assert get_first_stmt_kind("let x: Int = 5") == "Let", "let int stmt"
    assert get_first_stmt_kind("let s: String = \"hi\"") == "Let", "let string stmt"
    assert get_first_stmt_kind("let b: Bool = true") == "Let", "let bool stmt"
    let stmt: Map<String, Any> = get_first_stmt("let x: Int = 42")
    let name: String = stmt["name"]
    assert name == "x", "let name is x"
}

test "parse-fn-decl" {
    assert get_first_stmt_kind("fn foo() -> Void { }") == "FnDef", "fn declaration"
    assert get_first_stmt_kind("fn bar(x: Int) -> Int { return x }") == "FnDef", "fn with param"
    let stmt: Map<String, Any> = get_first_stmt("fn foo() -> Void { }")
    let name: String = stmt["name"]
    assert name == "foo", "fn name is foo"
}

test "parse-fn-params" {
    let stmt: Map<String, Any> = get_first_stmt("fn bar(x: Int, y: String) -> Int { return 0 }")
    let params = stmt["params"]
    let n: Int = native_list_len(params)
    assert n == 2, "fn has 2 params"
    let p0: Map<String, Any> = native_list_get(params, 0)
    let p1: Map<String, Any> = native_list_get(params, 1)
    assert p0["name"] == "x", "first param name x"
    assert p1["name"] == "y", "second param name y"
}

test "parse-return-stmt" {
    let tokens: [Any] = lex("fn f() -> Int { return 42 }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let n: Int = native_list_len(body)
    assert n > 0, "fn body non-empty"
    let ret: Map<String, Any> = native_list_get(body, 0)
    assert ret["stmt"] == "Return", "return stmt kind"
}

test "parse-if-stmt" {
    // In El, `if` is an expression. Standalone `if` in a fn body is wrapped
    // as Expr stmt with value.expr == "If".
    let tokens: [Any] = lex("fn f() -> Int { if x > 0 { return 1 } return 0 }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let first_body: Map<String, Any> = native_list_get(body, 0)
    assert first_body["stmt"] == "Expr", "if stmt in fn body is Expr wrapper"
    let val = first_body["value"]
    assert val["expr"] == "If", "Expr wraps If expression"
}

test "parse-if-else" {
    let tokens: [Any] = lex("fn f() -> Int { if x > 0 { return 1 } else { return 0 } }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    // if-else is also an Expr stmt wrapping an If expression
    let expr_stmt: Map<String, Any> = native_list_get(body, 0)
    assert expr_stmt["stmt"] == "Expr", "if-else is Expr stmt"
    let if_node = expr_stmt["value"]
    assert if_node["expr"] == "If", "Expr wraps If expression"
    let has_else: Bool = if_node["has_else"]
    assert has_else, "if-else has else branch"
}

test "parse-while-stmt" {
    let tokens: [Any] = lex("fn f() -> Void { while i < 10 { i = i + 1 } }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let while_node: Map<String, Any> = native_list_get(body, 0)
    assert while_node["stmt"] == "While", "while stmt kind"
}

test "parse-import-stmt" {
    assert get_first_stmt_kind("import \"some/module.el\"") == "Import", "import stmt"
}

test "parse-extern-fn" {
    assert get_first_stmt_kind("extern fn native_op(x: Int) -> Int") == "ExternFn", "extern fn"
}

test "parse-let-int-value" {
    let stmt: Map<String, Any> = get_first_stmt("let n: Int = 99")
    let val = stmt["value"]
    let val_kind: String = val["expr"]
    assert val_kind == "Int", "let value is Int expr"
    let v: String = val["value"]
    assert v == "99", "int literal value 99"
}

test "parse-let-string-value" {
    let stmt: Map<String, Any> = get_first_stmt("let s: String = \"hello\"")
    let val = stmt["value"]
    let val_kind: String = val["expr"]
    assert val_kind == "Str", "let value is Str expr"
    let v: String = val["value"]
    assert v == "hello", "string literal value hello"
}

test "parse-let-bool-value" {
    let stmt: Map<String, Any> = get_first_stmt("let b: Bool = true")
    let val = stmt["value"]
    let val_kind: String = val["expr"]
    assert val_kind == "Bool", "let value is Bool expr"
}

test "parse-binop-expr" {
    let stmt: Map<String, Any> = get_first_stmt("let x: Int = 1 + 2")
    let val = stmt["value"]
    let val_kind: String = val["expr"]
    assert val_kind == "BinOp", "let value is BinOp"
    let op: String = val["op"]
    assert op == "Plus", "binop is Plus"
}

test "parse-call-expr" {
    let tokens: [Any] = lex("fn f() -> Void { println(\"hi\") }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let expr_stmt: Map<String, Any> = native_list_get(body, 0)
    assert expr_stmt["stmt"] == "Expr", "call is Expr stmt"
    let val = expr_stmt["value"]
    let val_kind: String = val["expr"]
    assert val_kind == "Call", "expr is Call"
}

test "parse-multiple-fns" {
    let src: String = "fn a() -> Int { return 1 }\nfn b() -> Int { return 2 }"
    let tokens: [Any] = lex(src)
    let stmts: [Map<String, Any>] = parse(tokens)
    assert native_list_len(stmts) == 2, "two fn declarations parsed"
    let s0: Map<String, Any> = native_list_get(stmts, 0)
    let s1: Map<String, Any> = native_list_get(stmts, 1)
    assert s0["name"] == "a", "first fn name a"
    assert s1["name"] == "b", "second fn name b"
}

test "parse-assign-stmt" {
    let tokens: [Any] = lex("fn f() -> Void { x = 42 }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let a: Map<String, Any> = native_list_get(body, 0)
    assert a["stmt"] == "Assign", "assign stmt kind"
    assert a["name"] == "x", "assign target x"
}

test "parse-for-stmt" {
    let tokens: [Any] = lex("fn f() -> Void { for x in items { println(x) } }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let for_node: Map<String, Any> = native_list_get(body, 0)
    assert for_node["stmt"] == "For", "for stmt kind"
    assert for_node["item"] == "x", "for item is x"
}

test "parse-unary-not" {
    let tokens: [Any] = lex("fn f() -> Bool { return !x }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let ret: Map<String, Any> = native_list_get(body, 0)
    let val = ret["value"]
    assert val["expr"] == "Not", "unary not is Not expr"
}

test "parse-unary-neg" {
    let tokens: [Any] = lex("fn f() -> Int { return -5 }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let ret: Map<String, Any> = native_list_get(body, 0)
    let val = ret["value"]
    assert val["expr"] == "Neg", "unary minus is Neg expr"
}

test "parse-array-literal" {
    let tokens: [Any] = lex("fn f() -> [Int] { return [1, 2, 3] }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let ret: Map<String, Any> = native_list_get(body, 0)
    let val = ret["value"]
    assert val["expr"] == "Array", "array literal is Array expr"
    let elems = val["elems"]
    assert native_list_len(elems) == 3, "array has 3 elements"
}

test "parse-empty-array" {
    let tokens: [Any] = lex("fn f() -> [Int] { return [] }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let ret: Map<String, Any> = native_list_get(body, 0)
    let val = ret["value"]
    assert val["expr"] == "Array", "empty array is Array expr"
    let elems = val["elems"]
    assert native_list_len(elems) == 0, "empty array has 0 elements"
}

test "parse-index-expr" {
    let tokens: [Any] = lex("fn f() -> Any { return arr[0] }")
    let stmts: [Map<String, Any>] = parse(tokens)
    let fn_node: Map<String, Any> = native_list_get(stmts, 0)
    let body = fn_node["body"]
    let ret: Map<String, Any> = native_list_get(body, 0)
    let val = ret["value"]
    assert val["expr"] == "Index", "array index is Index expr"
}

// ── Codegen tests ─────────────────────────────────────────────────────────────

test "codegen-includes" {
    let out: String = compile_capture("fn main() -> Void { }")
    assert str_contains(out, "#include"), "output has #include"
    assert str_contains(out, "el_runtime.h"), "output includes el_runtime.h"
}

test "codegen-int-main" {
    let out: String = compile_capture("fn main() -> Void { }")
    assert str_contains(out, "int main("), "output has int main()"
}

test "codegen-runtime-init" {
    let out: String = compile_capture("fn main() -> Void { }")
    assert str_contains(out, "el_runtime_init_args("), "runtime init in main"
}

test "codegen-void-function-signature" {
    let out: String = compile_capture("fn f() -> Int { return 0 }")
    assert str_contains(out, "f(void)"), "no-param fn uses void signature"
}

test "codegen-function-with-params" {
    let out: String = compile_capture("fn add(x: Int, y: Int) -> Int { return x + y }")
    assert str_contains(out, "add("), "function add in output"
    assert str_contains(out, "el_val_t x"), "param x in output"
    assert str_contains(out, "el_val_t y"), "param y in output"
}

test "codegen-int-literal" {
    let out: String = compile_capture("fn answer() -> Int { return 42 }")
    assert str_contains(out, "42"), "integer literal 42 in output"
    assert str_contains(out, "return"), "return statement in output"
}

test "codegen-string-literal" {
    let out: String = compile_capture("fn greet() -> String { return \"hello\" }")
    assert str_contains(out, "hello"), "string literal hello in output"
}

test "codegen-if-statement" {
    let src: String = "fn check(x: Int) -> Int { if x > 0 { return 1 } return 0 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "if ("), "if statement in C output"
}

test "codegen-if-else" {
    let src: String = "fn check(x: Int) -> Int { if x > 0 { return 1 } else { return 0 } }"
    let out: String = compile_capture(src)
    assert str_contains(out, "if ("), "if in output"
    assert str_contains(out, "} else {"), "else branch in output"
}

test "codegen-while-loop" {
    let src: String = "fn f() -> Int { let i: Int = 0 while i < 10 { i = i + 1 } return i }"
    let out: String = compile_capture(src)
    assert str_contains(out, "while ("), "while loop in C output"
}

test "codegen-let-binding" {
    let src: String = "fn f() -> Int { let n: Int = 5 return n }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_val_t n"), "let binding in output"
}

test "codegen-function-call" {
    let src: String = "fn f() -> Void { println(\"hi\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "println("), "function call in output"
}

test "codegen-string-concat" {
    let src: String = "fn f() -> String { let a: String = \"x\" let b: String = \"y\" return a + b }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_str_concat"), "string concat uses el_str_concat"
}

test "codegen-int-arithmetic" {
    let src: String = "fn f(x: Int, y: Int) -> Int { return x + y }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "el_str_concat(x"), "int add does not use el_str_concat"
}

test "codegen-comparison" {
    let src: String = "fn f(x: Int) -> Bool { return x > 0 }"
    let out: String = compile_capture(src)
    assert str_contains(out, ">"), "comparison in output"
}

test "codegen-string-equality" {
    let src: String = "fn f(s: String) -> Bool { return s == \"hello\" }"
    let out: String = compile_capture(src)
    assert str_contains(out, "str_eq("), "string equality uses str_eq"
}

test "codegen-logical-and" {
    let src: String = "fn f(a: Bool, b: Bool) -> Bool { return a && b }"
    let out: String = compile_capture(src)
    assert str_contains(out, "&&"), "logical and in output"
}

test "codegen-logical-or" {
    let src: String = "fn f(a: Bool, b: Bool) -> Bool { return a || b }"
    let out: String = compile_capture(src)
    assert str_contains(out, "||"), "logical or in output"
}

test "codegen-unary-not" {
    let src: String = "fn f(b: Bool) -> Bool { return !b }"
    let out: String = compile_capture(src)
    assert str_contains(out, "!"), "unary not in output"
}

test "codegen-string-escape-in-c" {
    let src: String = "fn msg() -> String { return \"hello\\nworld\\t!\" }"
    let out: String = compile_capture(src)
    assert str_contains(out, "\\n"), "newline escape in C output"
    assert str_contains(out, "\\t"), "tab escape in C output"
}

test "codegen-many-functions" {
    // Multiple functions — exercises streaming loop + per-function arena scoping
    let src: String = "fn a() -> Int { return 1 }\nfn b() -> Int { return 2 }\nfn c() -> Int { return 3 }\nfn d() -> Int { return 4 }\nfn e() -> Int { return 5 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_val_t a("), "function a in output"
    assert str_contains(out, "el_val_t b("), "function b in output"
    assert str_contains(out, "el_val_t c("), "function c in output"
    assert str_contains(out, "el_val_t d("), "function d in output"
    assert str_contains(out, "el_val_t e("), "function e in output"
}

test "codegen-deep-expression" {
    // Deeply nested arithmetic — exercises recursive cg_expr + per-statement arena
    let src: String = "fn deep() -> Int { return 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "return"), "deep expr: return present"
    assert str_contains(out, "8"), "deep expr: literal 8 present"
}

test "codegen-forward-declarations" {
    // Functions should have forward declarations before definitions
    let src: String = "fn b() -> Int { return a() }\nfn a() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_val_t a("), "function a in output"
    assert str_contains(out, "el_val_t b("), "function b in output"
}

test "codegen-for-loop" {
    let src: String = "fn f() -> Void { let items: [Int] = native_list_empty() for item in items { println(item) } }"
    let out: String = compile_capture(src)
    assert str_contains(out, "for ("), "for loop in C output"
    assert str_contains(out, "el_list_get("), "for loop uses el_list_get"
}

test "codegen-extern-fn" {
    let src: String = "extern fn my_native(x: Int) -> Int\nfn use_it() -> Int { return my_native(1) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "my_native("), "extern fn referenced in output"
}

test "codegen-nested-calls" {
    let src: String = "fn f() -> String { return str_concat(int_to_str(42), \" ok\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "str_concat"), "nested calls: str_concat in output"
    assert str_contains(out, "int_to_str"), "nested calls: int_to_str in output"
}

// ── Self-host / smoke tests ───────────────────────────────────────────────────

test "compiler-minimal-program" {
    let src: String = "fn main() -> Void { println(\"ok\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "#include"), "has #include"
    assert str_contains(out, "int main("), "has int main()"
    assert str_contains(out, "println("), "calls println"
    assert str_contains(out, "el_runtime.h"), "links el_runtime.h"
}

test "compiler-pure-library" {
    // No fn main = library mode: codegen_streaming returns before emitting main()
    let src: String = "fn helper(x: Int) -> Int { return x + 1 }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "int main("), "library: no int main"
    assert str_contains(out, "#include"), "library: has includes"
    assert str_contains(out, "helper("), "library: helper function present"
}

test "compiler-multiple-fns-with-main" {
    let src: String = "fn greet(name: String) -> String { return \"Hello \" + name }\nfn main() -> Void { println(greet(\"world\")) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "greet("), "greet in output"
    assert str_contains(out, "int main("), "main in output"
    assert str_contains(out, "println("), "println in output"
}

test "compiler-let-in-main" {
    let src: String = "fn main() -> Void { let x: Int = 42 println(int_to_str(x)) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_val_t x"), "let binding x in output"
    assert str_contains(out, "42"), "literal 42 in output"
}

test "compiler-string-concat-chain" {
    let src: String = "fn f() -> String { let a: String = \"x\" let b: String = \"y\" let c: String = \"z\" return a + b + c }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_str_concat"), "string chain uses el_str_concat"
}

test "compiler-negative-literal" {
    let src: String = "fn f() -> Int { return -42 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "42"), "negative literal value in output"
}

test "compiler-stdint-include" {
    // The generated C should include stdint.h for int64_t
    let src: String = "fn f() -> Int { return 0 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "stdint.h"), "output includes stdint.h"
}

// ── Decorator seam: boundary-beat attribution ────────────────────────────────
//
// The beat carries the CONSTRUCT that caused it, not only the fn that beat.
// Without the second argument the graph accumulates boundary events with no
// way to attribute them to the decorator responsible, so no construct can ever
// be measured and "is this decorator earning its keep" stays an argument
// instead of a query.



test "decorator-undecorated-fn-has-no-beat" {
    let src: String = "fn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "engram_boundary_beat"), "an undecorated fn does not beat"
}

// ── Decorator seam: the twelve inert names ───────────────────────────────────
//
// PINS A KNOWN DEFECT. codegen calls fn_has_decorator for exactly three names
// (manager, accessor, route). Twelve others parse, attach as {name,args}, and
// compile to nothing — including four that look like protection:
// @authenticate (6 uses), @authorize (3), @rate_limit (3), @validate (2).
//
// This test asserts the CURRENT behaviour so that fixing it is a visible
// change rather than a silent one. When a pass wires or rejects these, this
// test flips and that flip is the proof.

test "decorator-authenticate-compiles-to-nothing" {
    let src: String = "@authenticate\nfn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    let bare: String = compile_capture("fn f() -> Int { return 1 }")
    assert str_eq(out, bare), "KNOWN DEFECT: @authenticate emits identical C to no decorator at all"
}

// ── Declared constructs ──────────────────────────────────────────────────────
//
// A construct declares its own meaning and codegen reads it. Adding a
// construct is a declaration in the program; it does not touch the compiler.


test "declared-construct-name-unknown-to-codegen" {
    // The name is arbitrary. Nothing in the compiler mentions it.
    let src: String = "@decorator(\"injects_at_entry\", \"engram_boundary_beat\")\nfn zzq_unlikely_name() {}\n@zzq_unlikely_name\nfn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "EL_STR(\"zzq_unlikely_name\")"), "an arbitrary construct name works"
}

test "undeclared-construct-still-injects-nothing" {
    let src: String = "@nobody_declared_this\nfn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "engram_boundary_beat"), "an undeclared construct injects nothing"
}


// ── Declared constructs: guards ──────────────────────────────────────────────
//
// A guard is an injection that may refuse. Non-zero return short-circuits the
// decorated fn. This is what @authenticate/@authorize/@rate_limit/@validate
// needed and never had — fourteen applications that read as protection and
// emitted no instruction.




test "undeclared-guard-emits-nothing" {
    let src: String = "@not_a_declared_guard\nfn handler() -> Int { return 7 }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "if (__g)"), "an undeclared construct guards nothing"
}

// ── Declared constructs: exit injection and composition ──────────────────────





// ── Declared constructs: wraps and prohibitions ──────────────────────────────





// ── Runtime seam ─────────────────────────────────────────────────────────────
//
// CONTROL for the finding that a crossing can be resolved at execution rather
// than at emission. Codegen emits one unconditional indirection per fn; which
// constructs apply is read from a table written after the binary exists.

test "seam-indirection-emitted-on-every-fn" {
    let src: String = "fn a() -> Int { return 1 }\nfn b() -> Int { return 2 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_seam_run(EL_STR(\"a\"), 0, 0);"), "fn a carries the indirection"
    assert str_contains(out, "el_seam_run(EL_STR(\"b\"), 0, 0);"), "fn b carries the indirection"
}

test "seam-emitted-without-any-decorator" {
    // The point of the seam: source need not mention a construct at all.
    let src: String = "fn undecorated() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_seam_run"), "an undecorated fn is still bindable at runtime"
    assert !str_contains(out, "engram_boundary_beat"), "and nothing is inlined for it"
}


// ── Runtime seam: what replaced the compile-time entry mechanism ─────────────
//
// Entry injection and refusal moved from emission to execution. These assert
// the emitted shape; the BEHAVIOUR — that a construct declared after the build
// applies, refuses, composes, and that an unlinked target is skipped — is
// covered by tests/integration/seam_binding.sh, which needs a built binary and
// an environment and therefore cannot be a compile_capture test.

test "seam-replaces-inlined-entry-injection" {
    let src: String = "@manager\nfn m() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_seam_run(EL_STR(\"m\")"), "the crossing goes through the seam"
    assert !str_contains(out, "engram_boundary_beat(EL_STR(\"m\")"), "nothing is inlined at the crossing any more"
}

test "seam-entry-is-refusable" {
    let src: String = "fn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "if (__s) return __s;"), "a bound construct can short-circuit the fn"
}

test "seam-is-emitted-for-undecorated-fns" {
    let src: String = "fn plain() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_seam_run(EL_STR(\"plain\")"), "any fn is bindable later, decorated or not"
}


// ── Exit crossings resolve at runtime too ────────────────────────────────────
//
// The wrapper is now UNCONDITIONAL. It has to be: early returns must route
// through something for an exit construct to see them, and codegen cannot know
// which fns will be bound after the binary exists. Measured cost of always
// emitting it: 0.37s -> 0.38s across ten self-compiles.

test "every-fn-gets-a-body-helper-and-wrapper" {
    let src: String = "fn plain(k: Int) -> Int { if k > 0 { return 1 } return 2 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "static el_val_t __el_body_plain"), "the body is a helper"
    assert str_contains(out, "el_val_t plain(el_val_t k) {"), "the visible fn is a wrapper"
}

test "exit-crossing-goes-through-the-seam" {
    let src: String = "fn f() -> Int { return 1 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "__r = el_seam_run(EL_STR(\"f\"), 1, __r);"), "the exit crossing is resolved at execution and may replace the result"
}

test "early-returns-route-through-the-exit-seam" {
    let src: String = "fn early(k: Int) -> Int { if k > 0 { return 99 } return 1 }"
    let out: String = compile_capture(src)
    let helper: Int = str_index_of(out, "__el_body_early")
    let seam: Int = str_index_of(out, "el_seam_run(EL_STR(\"early\"), 1")
    assert helper < seam, "the early return is inside the helper, so it passes through the exit seam"
}


// ── Invocation control resolves at runtime ───────────────────────────────────
//
// Every fn gets an env struct and a thunk, because codegen cannot know which
// fns a wrap construct will be bound to after the binary exists. That the bound
// construct can invoke the body zero or N times is behaviour, so it lives in
// tests/integration/seam_binding.sh.

test "every-fn-gets-a-closure" {
    let src: String = "fn f(k: Int) -> Int { return k }"
    let out: String = compile_capture(src)
    assert str_contains(out, "struct __env_f {  el_val_t k; };"), "captured environment"
    assert str_contains(out, "static el_val_t __thunk_f(void* __v)"), "thunk over that environment"
    assert str_contains(out, "el_seam_wrap(EL_STR(\"f\"), __thunk_f, &__env)"), "invocation goes through the seam"
}

test "zero-param-fn-emits-valid-c" {
    // An empty struct is a GNU extension and an empty initialiser is C23.
    let src: String = "fn noargs() -> Int { return 3 }"
    let out: String = compile_capture(src)
    assert str_contains(out, "struct __env_noargs {  char __e0; };"), "zero-param env has a field"
    assert !str_contains(out, "__env = {  }"), "and no empty initialiser"
}


// ── Prohibition is a query, not an emission ─────────────────────────────────
//
// The compiler records what it saw -- who calls what, who carries what, who
// prohibits what. Whether that is legal is decided by tools/check/prohibitions.sh
// against the emitted relations, at build time. An emitter that also adjudicates
// has to contain every rule anyone will ever want.

test "compiler-no-longer-emits-prohibition-errors" {
    let src: String = "@decorator(\"prohibits_outside\", \"raw_sql\")\nfn repository() {}\nfn sneaky() -> Int { raw_sql(\"DROP\") return 1 }"
    let out: String = compile_capture(src)
    assert !str_contains(out, "boundary violation"), "the emitter does not adjudicate"
}

// ── Int return types drive + dispatch ────────────────────────────────────────
//
// El has one type, so `a + b` must be dispatched from what the operands ARE.
// The 35 Int-returning builtins moved to signatures.rel; the dispatch stayed,
// because choosing between arithmetic and concatenation is emission.

test "int-returning-builtin-drives-arithmetic-dispatch" {
    let src: String = "fn main() { let a = str_len(\"hello\") let b = str_len(\"hi\") let c = a + b println(int_to_str(c)) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "(a + b)"), "Int + Int is arithmetic"
    assert !str_contains(out, "el_str_concat(a, b)"), "and NOT concatenation"
}

test "string-plus-string-still-concatenates" {
    let src: String = "fn main() { let s = \"a\" + \"b\" println(s) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_str_concat"), "String + String still concatenates"
}

// ── Reserved words that reserved nothing ─────────────────────────────────────
//
// sealed, activate, seed, protocol and impl were keywords in the lexer and were
// consumed by no parser or codegen path. Each stole an identifier from users
// for nothing, and using one silently miscompiled: `let seed = 42` compiled
// clean and produced the wrong value with no diagnostic at any layer.

test "freed-identifiers-compile-as-identifiers" {
    let src: String = "fn main() { let seed = 42 let impl = seed + 1 println(int_to_str(impl)) }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_val_t seed"), "seed is an identifier"
    assert str_contains(out, "el_val_t impl"), "impl is an identifier"
    assert str_contains(out, "(seed + 1)"), "and arithmetic on them dispatches correctly"
}

test "test-keyword-is-still-reserved" {
    // `test` LOOKED inert by the same measure and is not: codegen consumes it
    // for --test mode, 408 uses in the tree. Measuring only parser.el would
    // have removed it.
    let src: String = "fn main() { println(\"x\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "int main"), "the suite still compiles, which requires test to remain a keyword"
}

// ── A bare literal is a magnitude with no axis ───────────────────────────────
//
// Duration + Int was already refused because an Int carries no unit. Adding one
// to a POINT is worse: it moves the instant by an unspecified amount. The
// asymmetry had no justification; it was simply never written.

test "instant-plus-bare-int-is-refused" {
    let src: String = "fn main() { let t: Instant = now() let u: Instant = t + 3 println(\"x\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "TIME_TYPE_ERROR: Instant + Int"), "3 of what?"
}

test "instant-plus-unit-suffix-is-allowed" {
    // .hour supplies the axis, so the magnitude becomes a displacement.
    let src: String = "fn main() { let t: Instant = now() let u: Instant = t + 1.hour println(\"x\") }"
    let out: String = compile_capture(src)
    assert str_contains(out, "el_instant_add_dur"), "a unit suffix makes it a Duration"
    assert !str_contains(out, "TIME_TYPE_ERROR"), "and the addition is legal"
}
