// Tests for type_bridge.zig
const std = @import("std");
const types = @import("types.zig");
const type_bridge = @import("type_bridge.zig");
const ast = @import("../ast.zig");
const Node = ast.Node;

const TypeId = types.TypeId;
const TypeInfo = types.TypeInfo;
const TypeTable = types.TypeTable;

test "bridgeType: primitives" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    try std.testing.expectEqual(TypeId.s32, type_bridge.bridgeType(.{ .signed = 32 }, &table));
    try std.testing.expectEqual(TypeId.u8, type_bridge.bridgeType(.{ .unsigned = 8 }, &table));
    try std.testing.expectEqual(TypeId.f64, type_bridge.bridgeType(.f64, &table));
    try std.testing.expectEqual(TypeId.void, type_bridge.bridgeType(.void_type, &table));
    try std.testing.expectEqual(TypeId.bool, type_bridge.bridgeType(.boolean, &table));
    try std.testing.expectEqual(TypeId.string, type_bridge.bridgeType(.string_type, &table));
    try std.testing.expectEqual(TypeId.any, type_bridge.bridgeType(.any_type, &table));
}

test "bridgeType: composite types" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    // Pointer
    const ptr_id = type_bridge.bridgeType(.{ .pointer_type = .{ .pointee_name = "s32" } }, &table);
    try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .s32 } }, table.get(ptr_id));

    // Slice
    const slice_id = type_bridge.bridgeType(.{ .slice_type = .{ .element_name = "u8" } }, &table);
    try std.testing.expectEqual(TypeInfo{ .slice = .{ .element = .u8 } }, table.get(slice_id));

    // Array
    const arr_id = type_bridge.bridgeType(.{ .array_type = .{ .element_name = "f32", .length = 4 } }, &table);
    try std.testing.expectEqual(TypeInfo{ .array = .{ .element = .f32, .length = 4 } }, table.get(arr_id));

    // Optional
    const opt_id = type_bridge.bridgeType(.{ .optional_type = .{ .child_name = "s64" } }, &table);
    try std.testing.expectEqual(TypeInfo{ .optional = .{ .child = .s64 } }, table.get(opt_id));
}

test "resolveAstType: primitive type_expr" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    const node = try alloc.create(Node);
    defer alloc.destroy(node);
    node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "f64" } } };

    try std.testing.expectEqual(TypeId.f64, type_bridge.resolveAstType(node, &table));
}

test "resolveAstType: pointer type" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    const inner = try alloc.create(Node);
    defer alloc.destroy(inner);
    inner.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "s32" } } };

    const node = try alloc.create(Node);
    defer alloc.destroy(node);
    node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .pointer_type_expr = .{ .pointee_type = inner } } };

    const id = type_bridge.resolveAstType(node, &table);
    try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .s32 } }, table.get(id));
}

test "resolveAstType: optional slice" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    const elem = try alloc.create(Node);
    defer alloc.destroy(elem);
    elem.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "u8" } } };

    const slice = try alloc.create(Node);
    defer alloc.destroy(slice);
    slice.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .slice_type_expr = .{ .element_type = elem } } };

    const opt = try alloc.create(Node);
    defer alloc.destroy(opt);
    opt.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .optional_type_expr = .{ .inner_type = slice } } };

    const id = type_bridge.resolveAstType(opt, &table);
    const info = table.get(id);
    switch (info) {
        .optional => |o| {
            const child_info = table.get(o.child);
            try std.testing.expectEqual(TypeInfo{ .slice = .{ .element = .u8 } }, child_info);
        },
        else => return error.TestUnexpectedResult,
    }
}

test "resolveAstType: null returns default" {
    const alloc = std.testing.allocator;
    var table = TypeTable.init(alloc);
    defer table.deinit();

    try std.testing.expectEqual(TypeId.s64, type_bridge.resolveAstType(null, &table));
}
