#![deny(missing_docs)]

//! Ty→T backend: lowers Ty AST to T AST via monomorphization and resolution.

/// Compile-time constant evaluator for `const fn`.
pub mod const_eval;
/// Unified const expression evaluation.
pub mod const_expression;
mod lint;
mod lower;
mod monomorphize;
mod reorder;
mod resolve;

use std::collections::{HashMap, HashSet};

use ty_ree::declaration as ty;
use ty_ree::types as ty_types;

/// Lowers a Ty module to a T module via monomorphization, resolution, and lowering.
pub fn lower_module(module: ty::Module) -> Result<t_ree::declaration::Module, String> {
    let lint_errors = lint::check_unused_bindings(&module);
    if !lint_errors.is_empty() {
        return Err(lint_errors.join("\n"));
    }
    let module = monomorphize::monomorphize(module)?;

    let mut newtypes: HashMap<String, ty_types::Type> = HashMap::new();
    let mut callable_targets: HashSet<String> = HashSet::new();
    let mut public_types: HashSet<String> = HashSet::new();

    for declaration in &module {
        if let ty::Declaration::Type(newtype) = declaration {
            newtypes.insert(newtype.name.clone(), newtype.inner_type.clone());
            if newtype.public {
                public_types.insert(newtype.name.clone());
            }
        }
        if let ty::Declaration::Impl(impl_block) = declaration
            && let ty_types::Type::Named(name, _) = &impl_block.target_type
        {
            callable_targets.insert(name.clone());
        }
    }

    let all_callables: HashSet<String> = callable_targets.clone();
    let resolve_context = resolve::ResolveContext {
        impl_targets: callable_targets,
        extern_targets: HashSet::new(),
        all_callables,
        newtypes: newtypes.clone(),
    };
    let module = resolve::resolve_callable_constructions(module, &resolve_context);

    let mut output = Vec::new();
    for declaration in module {
        lower::lower_declaration(declaration, &mut output, &newtypes, &public_types)?;
    }
    reorder::reorder_functions(&mut output);
    Ok(output)
}

fn is_field_of(
    struct_name: &str,
    field_name: &str,
    newtypes: &HashMap<String, ty_types::Type>,
) -> bool {
    let mut current = struct_name;
    loop {
        match newtypes.get(current) {
            Some(ty_types::Type::Tuple(fields)) => {
                return fields.iter().any(
                    |field| matches!(field, ty_types::Type::Named(name, _) if name == field_name),
                );
            }
            Some(ty_types::Type::Named(next, _)) => current = next,
            _ => return false,
        }
    }
}

fn is_struct_backed(name: &str, newtypes: &HashMap<String, ty_types::Type>) -> bool {
    let mut current = name;
    loop {
        match newtypes.get(current) {
            Some(ty_types::Type::Tuple(_)) => return true,
            Some(ty_types::Type::Named(next, _)) => current = next,
            _ => return false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ty_ree::expression as ty_expression;
    use ty_ree::expression::operator::{ArithmeticOperator, BinaryOperator};
    use ty_ree::expression::{Block, Expression, ExpressionKind, Literal, Statement};
    use ty_ree::types::{IntWidth, Signedness, Type};

    fn int32() -> Type {
        Type::Int(IntWidth::W32, Signedness::Signed)
    }

    fn unit() -> Type {
        Type::unit()
    }

    fn int_literal(value: u128) -> Expression {
        Expression::new(ExpressionKind::Literal(Literal::Integer(value)), int32())
    }

    fn variable(name: &str, variable_type: Type) -> Expression {
        Expression::new(ExpressionKind::Variable(name.into()), variable_type)
    }

    fn call(callee: Expression, arguments: Vec<Expression>, return_type: Type) -> Expression {
        Expression::new(
            ExpressionKind::Call(Box::new(callee), arguments),
            return_type,
        )
    }

    use std::sync::atomic::{AtomicU64, Ordering};
    static COUNTER: AtomicU64 = AtomicU64::new(0);

    fn compile_and_run(module: ty::Module) -> String {
        let mut t_module = lower_module(module).unwrap();
        let errors = t_ree::resolve::resolve_module(&mut t_module, false);
        assert!(errors.is_empty(), "resolve errors: {errors:?}");
        let c_code = t_backend_c::compile(&t_module);

        let id = COUNTER.fetch_add(1, Ordering::Relaxed);
        let temp_directory = std::env::temp_dir();
        let c_file = temp_directory.join(format!("ty_test_{id}.c"));
        let binary = temp_directory.join(format!("ty_test_{id}"));

        std::fs::write(&c_file, &c_code).unwrap();

        let status = std::process::Command::new("cc")
            .args([
                c_file.to_str().unwrap(),
                "-o",
                binary.to_str().unwrap(),
                "-std=gnu11",
                "-Wall",
                "-Werror",
                "-Wno-builtin-declaration-mismatch",
            ])
            .status()
            .unwrap();
        assert!(status.success(), "C compilation failed:\n{c_code}");

        let output = std::process::Command::new(binary.to_str().unwrap())
            .output()
            .unwrap();
        let _ = std::fs::remove_file(&c_file);
        let _ = std::fs::remove_file(&binary);
        assert!(
            output.status.success(),
            "Program failed with {:?}",
            output.status.code()
        );

        String::from_utf8(output.stdout).unwrap()
    }

    fn parse_and_run(source: &str) -> String {
        let module = ty_parser::parse(source).unwrap();
        compile_and_run(module)
    }

    fn compile_example(name: &str) -> String {
        let path = format!("../examples/ty/{name}.c.ty");
        let source = std::fs::read_to_string(&path)
            .unwrap_or_else(|error| panic!("cannot read {path}: {error}"));
        let module = ty_parser::parse(&source)
            .unwrap_or_else(|error| panic!("parse error in {name}.ty: {error}"));
        compile_and_run(module)
    }

    #[test]
    fn end_to_end_simple_function() {
        let module = vec![
            ty::Declaration::Type(ty::Newtype {
                name: "n".into(),
                generic_parameters: vec![],
                inner_type: int32(),
                public: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "twice".into(),
                generic_parameters: vec![],
                inner_type: Type::Named("n".into(), vec![]),
                public: false,
            }),
            ty::Declaration::Impl(ty::ImplBlock {
                generic_parameters: vec![],
                target_type: Type::Named("twice".into(), vec![]),
                callable: ty::Callable {
                    return_type: int32(),
                    body: Block::expression(Expression::new(
                        ExpressionKind::BinaryOperation(
                            BinaryOperator::Arithmetic(ArithmeticOperator::Multiply),
                            Box::new(variable("n", int32())),
                            Box::new(int_literal(2)),
                        ),
                        int32(),
                    )),
                    parameters: Some(vec![ty::Parameter {
                        name: "n".into(),
                        parameter_type: int32(),
                    }]),
                },
                const_fn: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "main".into(),
                generic_parameters: vec![],
                inner_type: Type::unit(),
                public: true,
            }),
            ty::Declaration::Impl(ty::ImplBlock {
                generic_parameters: vec![],
                target_type: Type::Named("main".into(), vec![]),
                callable: ty::Callable {
                    return_type: unit(),
                    body: Block {
                        statements: vec![Statement::Expression(Expression::new(
                            ExpressionKind::Print(vec![call(
                                variable("twice", int32()),
                                vec![int_literal(5)],
                                int32(),
                            )]),
                            unit(),
                        ))],
                        result: None,
                    },
                    parameters: Some(vec![]),
                },
                const_fn: false,
            }),
        ];

        let output = compile_and_run(module);
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_newtype() {
        let module = vec![
            ty::Declaration::Type(ty::Newtype {
                name: "x".into(),
                generic_parameters: vec![],
                inner_type: int32(),
                public: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "y".into(),
                generic_parameters: vec![],
                inner_type: int32(),
                public: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "Point".into(),
                generic_parameters: vec![],
                inner_type: Type::Tuple(vec![
                    Type::Named("x".into(), vec![]),
                    Type::Named("y".into(), vec![]),
                ]),
                public: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "main".into(),
                generic_parameters: vec![],
                inner_type: Type::unit(),
                public: true,
            }),
            ty::Declaration::Impl(ty::ImplBlock {
                generic_parameters: vec![],
                target_type: Type::Named("main".into(), vec![]),
                callable: ty::Callable {
                    return_type: unit(),
                    body: Block {
                        statements: vec![
                            Statement::Let {
                                name: "p".into(),
                                binding: ty_expression::Binding::Value,
                                declared_type: None,
                                line: 0,
                                value: Expression::new(
                                    ExpressionKind::TypeConstruction(
                                        "Point".into(),
                                        vec![],
                                        vec![
                                            ("x".into(), int_literal(3)),
                                            ("y".into(), int_literal(4)),
                                        ],
                                    ),
                                    Type::Named("Point".into(), vec![]),
                                ),
                            },
                            Statement::Expression(Expression::new(
                                ExpressionKind::Print(vec![
                                    Expression::new(
                                        ExpressionKind::Field(
                                            Box::new(variable(
                                                "p",
                                                Type::Named("Point".into(), vec![]),
                                            )),
                                            "x".into(),
                                        ),
                                        Type::Named("x".into(), vec![]),
                                    ),
                                    Expression::new(
                                        ExpressionKind::Field(
                                            Box::new(variable(
                                                "p",
                                                Type::Named("Point".into(), vec![]),
                                            )),
                                            "y".into(),
                                        ),
                                        Type::Named("y".into(), vec![]),
                                    ),
                                ]),
                                unit(),
                            )),
                        ],
                        result: None,
                    },
                    parameters: Some(vec![]),
                },
                const_fn: false,
            }),
        ];

        let output = compile_and_run(module);
        assert_eq!(output.trim(), "3 4");
    }

    #[test]
    fn lower_simple_function() {
        use t_ree::declaration as t;
        let module = vec![
            ty::Declaration::Type(ty::Newtype {
                name: "n".into(),
                generic_parameters: vec![],
                inner_type: int32(),
                public: false,
            }),
            ty::Declaration::Type(ty::Newtype {
                name: "twice".into(),
                generic_parameters: vec![],
                inner_type: Type::Named("n".into(), vec![]),
                public: false,
            }),
            ty::Declaration::Impl(ty::ImplBlock {
                generic_parameters: vec![],
                target_type: Type::Named("twice".into(), vec![]),
                callable: ty::Callable {
                    return_type: int32(),
                    body: Block::expression(Expression::new(
                        ExpressionKind::BinaryOperation(
                            BinaryOperator::Arithmetic(ArithmeticOperator::Multiply),
                            Box::new(Expression::new(
                                ExpressionKind::Variable("n".into()),
                                int32(),
                            )),
                            Box::new(int_literal(2)),
                        ),
                        int32(),
                    )),
                    parameters: Some(vec![ty::Parameter {
                        name: "n".into(),
                        parameter_type: int32(),
                    }]),
                },
                const_fn: false,
            }),
        ];

        let lowered = lower_module(module).unwrap();
        let functions: Vec<_> = lowered
            .iter()
            .filter(|d| matches!(d, t::Declaration::Function(_)))
            .collect();
        assert_eq!(functions.len(), 1);
        match functions[0] {
            t::Declaration::Function(function) => {
                assert_eq!(function.name, "impl_twice");
                assert_eq!(function.parameters.len(), 1);
            }
            other => panic!("expected Function, got {other:?}"),
        }
    }

    #[test]
    fn unused_generic_function_dropped() {
        let module = vec![
            ty::Declaration::Type(ty::Newtype {
                name: "identity".into(),
                generic_parameters: vec![ty_types::GenericParameter {
                    name: "T".into(),
                    kind: ty_types::GenericParameterKind::Type,
                }],
                inner_type: Type::Generic("T".into()),
                public: false,
            }),
            ty::Declaration::Impl(ty::ImplBlock {
                generic_parameters: vec![ty_types::GenericParameter {
                    name: "T".into(),
                    kind: ty_types::GenericParameterKind::Type,
                }],
                target_type: Type::Named(
                    "identity".into(),
                    vec![ty_types::GenericArgument::Type(Type::Generic("T".into()))],
                ),
                callable: ty::Callable {
                    return_type: Type::Generic("T".into()),
                    body: Block::expression(Expression::new(
                        ExpressionKind::Variable("value".into()),
                        Type::Generic("T".into()),
                    )),
                    parameters: None,
                },
                const_fn: false,
            }),
        ];

        let lowered = lower_module(module).unwrap();
        assert!(lowered.is_empty());
    }

    #[test]
    fn lower_reference_to_pointer() {
        let block = Block {
            statements: Vec::new(),
            result: Some(Box::new(Expression::new(
                ExpressionKind::Literal(ty_ree::expression::Literal::Integer(42)),
                Type::Int(ty_types::IntWidth::W32, ty_types::Signedness::Signed),
            ))),
        };
        let lowered = lower::lower_block(block, &HashMap::new()).unwrap();
        assert!(lowered.result.is_some());
    }

    #[test]
    fn end_to_end_parsed_function() {
        let output = parse_and_run(
            "type n = i32;
             fn twice n -> i32 { n.i32 * 2 }
             print(twice(5));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_parsed_newtype() {
        let output = parse_and_run(
            "type x, y = i32;
             type Point = x & y;
             let p = Point { x: 3, y: 4 };
             print(p.x, p.y);",
        );
        assert_eq!(output.trim(), "3 4");
    }

    #[test]
    fn end_to_end_parsed_impl() {
        let output = parse_and_run(
            "type n = i32;
             type twice = n;
             impl twice -> i32 { it.n * 2 }
             print(twice(5));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_parsed_impl_tuple() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             type add = a & b;
             impl add -> i32 { it.a + it.b }
             print(add(3, 7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_parsed_if() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             fn max a & b -> i32 {
                 if a.i32 > b.i32 { a.i32 } else { b.i32 }
             }
             print(max(3, 7));",
        );
        assert_eq!(output.trim(), "7");
    }

    #[test]
    fn end_to_end_generic_function() {
        let output = parse_and_run(
            "fn identity[T] T -> T { it }
             print(identity(42));",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn end_to_end_generic_function_multiple_types() {
        let output = parse_and_run(
            "fn identity[T] T -> T { it }
             print(identity(42), identity(true));",
        );
        assert_eq!(output.trim(), "42 true");
    }

    #[test]
    fn end_to_end_generic_function_two_params() {
        let output = parse_and_run(
            "type a;
             type b;
             fn first[A, B] a[A] & b[B] -> A { a }
             print(first[i32, bool](10, true));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn example_hello() {
        let output = compile_example("hello");
        assert_eq!(output.trim(), "hello world");
    }

    #[test]
    fn example_functions() {
        let output = compile_example("functions");
        assert_eq!(output.trim(), "10 7");
    }

    #[test]
    fn example_newtypes() {
        let output = compile_example("newtypes");
        assert_eq!(output.trim(), "42 1.5");
    }

    #[test]
    fn example_tuples() {
        let output = compile_example("tuples");
        assert_eq!(output.trim(), "3 4");
    }

    #[test]
    fn example_callable_tuples() {
        let output = compile_example("callable_tuples");
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn example_array_callable() {
        let output = compile_example("array_callable");
        assert_eq!(output.trim(), "10\n4");
    }

    #[test]
    fn example_impl_tuple() {
        let output = compile_example("impl_tuple");
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn example_generics() {
        let output = compile_example("generics");
        assert_eq!(output.trim(), "42 true");
    }

    #[test]
    fn example_generic_types() {
        let output = compile_example("generic_types");
        assert_eq!(output.trim(), "3 4");
    }

    #[test]
    fn example_generics_multi() {
        let output = compile_example("generics_multi");
        assert_eq!(output.trim(), "10 true");
    }

    #[test]
    fn end_to_end_generic_wrapper_type() {
        let output = parse_and_run(
            "type Wrapper;
             let w = Wrapper[i32]: 42;
             print(w);",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn end_to_end_generic_tuple_type() {
        let output = parse_and_run(
            "type x;
             type y;
             type Point[T] = x[T] & y[T];
             let p = Point[i32] { x: 3, y: 4 };
             print(p.x, p.y);",
        );
        assert_eq!(output.trim(), "3 4");
    }

    #[test]
    fn end_to_end_generic_type_multiple_instantiations() {
        let output = parse_and_run(
            "type Wrapper;
             let a = Wrapper[i32]: 10;
             let b = Wrapper[bool]: true;
             print(a, b);",
        );
        assert_eq!(output.trim(), "10 true");
    }

    #[test]
    fn monomorphize_generic_types_c() {
        let module = ty_parser::parse(
            "type x;
             type y;
             type Point[T] = x[T] & y[T];
             let p = Point[i32] { x: 3, y: 4 };
             print(p.x, p.y);",
        )
        .unwrap();
        let t_module = lower_module(module).unwrap();
        let c_code = t_backend_c::compile(&t_module);
        assert!(
            c_code.contains("Point$i32"),
            "expected mangled type name in:\n{c_code}"
        );
        assert!(
            c_code.contains("x$i32"),
            "expected mangled field type in:\n{c_code}"
        );
        assert!(
            !c_code.contains("Point["),
            "generic args should be resolved in:\n{c_code}"
        );
    }

    #[test]
    fn monomorphize_produces_correct_c() {
        let module = ty_parser::parse(
            "fn identity[T] T -> T { it }
             print(identity(42));",
        )
        .unwrap();
        let t_module = lower_module(module).unwrap();
        let c_code = t_backend_c::compile(&t_module);
        assert!(
            c_code.contains("identity$i32"),
            "expected mangled name in:\n{c_code}"
        );
        assert!(
            !c_code.contains("identity("),
            "generic call should be rewritten in:\n{c_code}"
        );
    }

    #[test]
    fn end_to_end_impl_block() {
        let output = parse_and_run(
            "type n = i32;
             type twice = n;
             impl twice -> i32 { it.n * 2 }
             print(twice(5));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_generic_impl_block() {
        let output = parse_and_run(
            "type Wrapper;
             impl[T] Wrapper[T] -> T { it }
             print(Wrapper[i32](42));",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn end_to_end_generic_impl_tuple() {
        let output = parse_and_run(
            "type x;
             type y;
             type Point[T] = x[T] & y[T];
             impl[T] Point[T] -> T { it.x + it.y }
             print(Point[i32] { x: 3, y: 4 }!);",
        );
        assert_eq!(output.trim(), "7");
    }

    #[test]
    fn end_to_end_const_generic_array_type() {
        let output = parse_and_run(
            "type N = u64;
             type Array[T]N = [T]N;
             let a: Array[i32]3 = [10, 20, 30];
             print(a[0], a[1], a[2]);",
        );
        assert_eq!(output.trim(), "10 20 30");
    }

    #[test]
    fn end_to_end_const_generic_function() {
        let output = parse_and_run(
            "type a;
             type b;
             type c;
             fn sum3[T] a[T] & b[T] & c[T] -> T {
                 a + b + c
             }
             print(sum3[i32](10, 20, 30));",
        );
        assert_eq!(output.trim(), "60");
    }

    #[test]
    fn end_to_end_const_generic_type_with_const() {
        let output = parse_and_run(
            "type N = u64;
             type Buffer[T]N = [T]N;
             let b: Buffer[i32]4 = [1, 2, 3, 4];
             print(b[0], b[3]);",
        );
        assert_eq!(output.trim(), "1 4");
    }

    #[test]
    fn end_to_end_array_callable() {
        let output = parse_and_run(
            "type add = [i32]2;
             impl add -> i32 { it[0] + it[1] }
             print(add(3, 7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_generic_array_callable() {
        let output = parse_and_run(
            "type add[T] = [T]2;
             impl[T] add[T] -> T { it[0] + it[1] }
             print(add[i32](3, 7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn end_to_end_let_type_inference_int() {
        let output = parse_and_run(
            "let x = 42;
             print(x);",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn end_to_end_let_type_inference_float() {
        let output = parse_and_run(
            "let x = 42.0;
             print(x);",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn end_to_end_identity_function() {
        let output = parse_and_run(
            "fn identity[T] T -> T { it }
             print(identity(42));
             print(identity(3.14));",
        );
        assert_eq!(output.trim(), "42\n3.14");
    }

    #[test]
    fn field_access_in_type_construction() {
        let output = parse_and_run(
            "type e0 = f64;
             type e1 = f64;
             type Rotor = e0 & e1;
             let r = Rotor { e0: 1.0, e1: 2.0 };
             let r2 = Rotor { e0: r.e0, e1: r.e1 };
             print(r2.e0.f64, r2.e1.f64);",
        );
        assert_eq!(output.trim(), "1 2");
    }

    #[test]
    fn callable_tuple_with_named_fields() {
        let output = parse_and_run(
            "type value = f64;
             type factor = f64;
             type scale = value & factor;
             impl scale -> f64 { it.value.f64 * it.factor.f64 }
             print(scale(3.0, 2.0));",
        );
        assert_eq!(output.trim(), "6");
    }

    #[test]
    fn implicit_fields_in_impl() {
        let output = parse_and_run(
            "type n = i32;
             type twice = n;
             impl twice -> i32 { n * 2 }
             print(twice(5));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn implicit_fields_tuple() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             type add = a & b;
             impl add -> i32 { a + b }
             print(add(3, 7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn at_syntax_array_callable() {
        let output = parse_and_run(
            "type add = [i32]2;
             impl add -> i32 { @0 + @1 }
             print(add(3, 7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn ufcs_function() {
        let output = parse_and_run(
            "type n = i32;
             fn twice n -> i32 { n.i32 * 2 }
             print(5.twice());",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn ufcs_callable_tuple() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             type add = a & b;
             impl add -> i32 { a + b }
             print(3.add(7));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn ufcs_chaining() {
        let output = parse_and_run(
            "type n = i32;
             fn twice n -> i32 { n.i32 * 2 }
             fn inc n -> i32 { n.i32 + 1 }
             print(5.twice().inc());",
        );
        assert_eq!(output.trim(), "11");
    }

    #[test]
    fn construct_without_eval() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             type pair = a & b;
             let data = pair { a: 3, b: 7 };
             print(data.a.i32, data.b.i32);",
        );
        assert_eq!(output.trim(), "3 7");
    }

    #[test]
    fn example_ref_construction() {
        let output = compile_example("ref_construction");
        assert_eq!(output.trim(), "3 7\n10");
    }

    #[test]
    fn construct_callable_evaluates() {
        let output = parse_and_run(
            "type a = i32;
             type b = i32;
             type add = a & b;
             impl add -> i32 { it.a.i32 + it.b.i32 }
             let result = add { a: 3, b: 7 }!;
             print(result);",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn example_ref_ufcs() {
        let output = compile_example("ref_ufcs");
        assert_eq!(output.trim(), "10\n11");
    }

    #[test]
    fn example_ref_implicit_fields() {
        let output = compile_example("ref_implicit_fields");
        assert_eq!(output.trim(), "25");
    }

    #[test]
    fn example_ref_at_syntax() {
        let output = compile_example("ref_at_syntax");
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn example_ref_impl_block() {
        let output = compile_example("ref_impl_block");
        assert_eq!(output.trim(), "212");
    }

    #[test]
    fn single_field_newtype_over_tuple() {
        let output = parse_and_run(
            "type x = f64;
             type y = f64;
             type Vec2 = x & y;
             type wrap = Vec2;
             let v = Vec2 { x: 3.0, y: 4.0 };
             let w: wrap = wrap: v;
             print(w.x.f64);",
        );
        assert_eq!(output.trim(), "3");
    }

    #[test]
    fn generic_type_in_function_return() {
        let output = parse_and_run(
            "type wrapper;
             type value;
             fn wrap[T] value[T] -> wrapper[T] { wrapper[T]: value }
             let w = wrap[i32](42);
             print(w.i32);",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn generic_type_in_generic_impl_return() {
        let output = parse_and_run(
            "type wrapper;
             type make_wrapper;
             impl[T] make_wrapper[T] -> wrapper[T] { wrapper[T]: it }
             let r = make_wrapper[i32](42);
             print(r.i32);",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn turbofish_explicit_type_arguments() {
        let output = parse_and_run(
            "fn identity[T] T -> T { it }
             print(identity[f64](3.14));",
        );
        assert_eq!(output.trim(), "3.14");
    }

    #[test]
    fn while_loop_lowers_correctly() {
        let module = ty_parser::parse(
            "let x: bool = true;
             while x {
                 print(1);
             }",
        )
        .unwrap();
        let t_module = lower_module(module).unwrap();
        let has_label = t_module.iter().any(|declaration| {
            if let t_ree::declaration::Declaration::Function(function_definition) = declaration {
                function_definition.body.statements.iter().any(|statement| {
                    matches!(statement, t_ree::expression::Statement::Label { .. })
                })
            } else {
                false
            }
        });
        assert!(has_label, "while loop should produce a label in T output");
    }

    #[test]
    fn var_assign_works() {
        let output = parse_and_run(
            "fn main() {
                 var x: i32 = 5;
                 x := 10;
                 print(x);
             }",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn while_loop_end_to_end() {
        let output = parse_and_run(
            "fn main() {
                 var i: i32 = 0;
                 var sum: i32 = 0;
                 while i < 10 {
                     sum += i;
                     i += 1;
                 }
                 print(sum);
             }",
        );
        assert_eq!(output.trim(), "45");
    }

    #[test]
    fn const_generic_in_impl_body() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             type wrapper[T]N = MV[T]N;
             fn main() {
                 let v: wrapper[f64]3 = [1.0, 2.0, 3.0];
                 print(v[0], v[2]);
             }",
        );
        assert_eq!(output.trim(), "1 3");
    }

    #[test]
    fn const_generic_in_function_body() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             fn sum[T]N MV[T]N -> T {
                 var result: T = 0.0;
                 var i: u64 = 0;
                 while i < N {
                     result += MV[i];
                     i += 1;
                 }
                 result
             }
             let a: MV[f64]3 = [1.0, 2.0, 3.0];
             print(sum[f64]3(a));",
        );
        assert_eq!(output.trim(), "6");
    }

    #[test]
    fn it_on_array_newtype() {
        let output = parse_and_run(
            "type MV3 = [f64]3;
             type mv_first = MV3;
             impl mv_first -> f64 { it[0] }
             let v: MV3 = [10.0, 20.0, 30.0];
             print(mv_first(v));",
        );
        assert_eq!(output.trim(), "10");
    }

    #[test]
    fn generic_function_uses_generic_type() {
        let output = parse_and_run(
            "type point;
             type value;
             fn make_point[T] value[T] -> point[T] { point[T]: value }
             print(make_point[i32](99).i32);",
        );
        assert_eq!(output.trim(), "99");
    }

    #[test]
    fn const_generic_suffix_type_definition() {
        let output = parse_and_run(
            "type Blades = u64;
             type MV[T]Blades = [T]Blades;
             type Vec3 = MV[f64]3;
             let v: Vec3 = [1.0, 2.0, 3.0];
             print(v[0], v[1], v[2]);",
        );
        assert_eq!(output.trim(), "1 2 3");
    }

    #[test]
    fn const_generic_suffix_function_parameters() {
        let output = parse_and_run(
            "type Blades = u64;
             type MV[T]Blades = [T]Blades;
             fn first[T]Blades MV[T]Blades -> T {
                 MV[0]
             }
             let a: MV[f64]3 = [1.0, 2.0, 3.0];
             print(first[f64]3(a));",
        );
        assert_eq!(output.trim(), "1");
    }

    #[test]
    fn const_generic_suffix_let_binding() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             let v: MV[f64]3 = [1.0, 2.0, 3.0];
             print(v[0], v[1], v[2]);",
        );
        assert_eq!(output.trim(), "1 2 3");
    }

    #[test]
    fn const_generic_suffix_return_type() {
        let output = parse_and_run(
            "type Blades = u64;
             type MV[T]Blades = [T]Blades;
             fn make_vec() -> MV[f64]3 {
                 let result: MV[f64]3 = [10.0, 20.0, 30.0];
                 result
             }
             let v: MV[f64]3 = make_vec();
             print(v[0], v[1], v[2]);",
        );
        assert_eq!(output.trim(), "10 20 30");
    }

    #[test]
    fn const_generic_suffix_as_cast() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             type Vec3 = MV[f64]3;
             let v: Vec3 = [1.0, 2.0, 3.0];
             let w = MV[f64]3: v;
             print(w[0]);",
        );
        assert_eq!(output.trim(), "1");
    }

    #[test]
    fn const_generic_suffix_sizeof() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             print(sizeof(MV[f64]3));",
        );
        assert_eq!(output.trim(), "24");
    }

    #[test]
    fn const_generic_suffix_popcount_in_body() {
        let output = parse_and_run(
            "const fn popcount(n: u64) -> u64 {
                 var count: u64 = 0;
                 var remaining: u64 = n;
                 while remaining > 0 {
                     count += (remaining & 1);
                     remaining := remaining >> 1;
                 }
                 count
             }
             type Blades = u64;
             type MV[T]Blades = [T](popcount(Blades));
             type Vec3 = MV[f64]0b1110;
             fn main() -> i32 {
                 let v: Vec3 = [1.0, 2.0, 3.0];
                 print(v[0], v[1], v[2]);
                 print(popcount(0b1110));
                 0
             }",
        );
        assert_eq!(output.trim(), "1 2 3\n3");
    }

    #[test]
    fn const_generic_suffix_turbofish_with_const_fn() {
        let output = parse_and_run(
            "const fn twice(n: u64) -> u64 { n * 2 }
             type N = u64;
             type Arr[T]N = [T]N;
             fn first[T]N Arr[T]N -> T { Arr[0] }
             fn main -> i32 {
                 let a: Arr[i32]3 = [10, 20, 30];
                 print(first[i32]3(a));
                 let b: Arr[i32](twice(3)) = [1, 2, 3, 4, 5, 6];
                 print(b[5]);
                 print(twice(4));
                 0
             }",
        );
        assert_eq!(output.trim(), "10\n6\n8");
    }

    #[test]
    fn const_generic_suffix_empty_brackets() {
        let output = parse_and_run(
            "type N = u64;
             type Buffer[]N = [i32]N;
             fn sum[]N Buffer[]N -> i32 {
                 var result: i32 = 0;
                 var i: u64 = 0;
                 while i < N {
                     result += Buffer[i];
                     i += 1;
                 }
                 result
             }
             let b: Buffer[]3 = [10, 20, 30];
             print(sum[]3(b));",
        );
        assert_eq!(output.trim(), "60");
    }

    #[test]
    fn const_generic_suffix_empty_brackets_vector() {
        let output = parse_and_run(
            "type N = u64;
             type FloatVec[]N = {f64}N;
             type Vec4 = FloatVec[]4;
             let v: Vec4 = {1.0, 2.0, 3.0, 4.0};
             print(v[0], v[3]);",
        );
        assert_eq!(output.trim(), "1 4");
    }

    #[test]
    fn const_generic_suffix_empty_brackets_array() {
        let output = parse_and_run(
            "type N = u64;
             type Buffer[]N = [i32]N;
             let b: Buffer[]3 = [10, 20, 30];
             print(b[0], b[2]);",
        );
        assert_eq!(output.trim(), "10 30");
    }

    #[test]
    fn const_generic_suffix_const_alias() {
        let output = parse_and_run(
            "type N = u64;
             type MV[T]N = [T]N;
             const SIZE: u64 = 3;
             type Vec3 = MV[f64]SIZE;
             let v: Vec3 = [1.0, 2.0, 3.0];
             print(v[0], v[2]);
             print(SIZE);",
        );
        assert_eq!(output.trim(), "1 3\n3");
    }

    #[test]
    fn const_generic_suffix_tuple_construction() {
        let output = parse_and_run(
            "type width = u64;
             type height = u64;
             type dim = width & height;
             type N = u64;
             type grid[T]dim = [T](dim.width * dim.height);
             type Grid2x3 = grid[f64]{ width: 2, height: 3 };
             let g: Grid2x3 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
             print(g[0], g[5]);",
        );
        assert_eq!(output.trim(), "1 6");
    }

    #[test]
    fn const_generic_suffix_field_access() {
        let output = parse_and_run(
            "type width = u64;
             type height = u64;
             type dim = width & height;
             type N = u64;
             type flat[T]dim = [T]dim.width;
             type Row3 = flat[i32](width: 3, height: 1);
             let r: Row3 = [10, 20, 30];
             print(r[0], r[2]);",
        );
        assert_eq!(output.trim(), "10 30");
    }

    #[test]
    fn const_generic_suffix_const_fn_in_turbofish() {
        let output = parse_and_run(
            "const fn square(n: u64) -> u64 { n * n }
             type N = u64;
             type Arr[T]N = [T]N;
             fn first[T]N Arr[T]N -> T { Arr[0] }
             fn main -> i32 {
                 let a: Arr[i32](square(3)) = [1, 2, 3, 4, 5, 6, 7, 8, 9];
                 print(first[i32](square(3))(a));
                 print(square(4));
                 0
             }",
        );
        assert_eq!(output.trim(), "1\n16");
    }

    #[test]
    fn turbofish_implicit_return() {
        let output = parse_and_run(
            "type N = u64;
             type Arr[T]N = [T]N;
             fn first[T]N Arr[T]N -> T { Arr[0] }
             fn get_first -> i32 {
                 let a: Arr[i32]3 = [42, 2, 3];
                 first[i32]3(a)
             }
             print(get_first());",
        );
        assert_eq!(output.trim(), "42");
    }

    #[test]
    fn turbofish_implicit_return_newtype() {
        let output = parse_and_run(
            "type Blades = u64;
             type MV[T]Blades = [T]Blades;
             fn mv_zeros[T]Blades() -> MV[T]Blades {
                 let result: MV[T]Blades = [0.0, 0.0, 0.0];
                 result
             }
             fn make_zeros() -> MV[f64]3 {
                 mv_zeros[f64]3()
             }
             let v: MV[f64]3 = make_zeros();
             print(v[0], v[1], v[2]);",
        );
        assert_eq!(output.trim(), "0 0 0");
    }

    #[test]
    fn array_repeat_literal() {
        let output = parse_and_run(
            "let a: [i32]3 = [0]3;
             print(a[0], a[1], a[2]);
             let b: [f64]4 = [1.5]4;
             print(b[0], b[3]);",
        );
        assert_eq!(output.trim(), "0 0 0\n1.5 1.5");
    }
}
