pub const TestFileCache = struct {
    files: std.StringHashMap([]const u8),
    allocator: std.mem.Allocator,
    pub const test_files = [_][]const u8{
        // Control Flow
        "control_flow/if",
        "control_flow/if_error",
        "control_flow/if_block",

        "control_flow/if_only",
        "control_flow/if_only_block",

        "control_flow/for",
        "control_flow/for_block",

        "control_flow/switch",
        "control_flow/switch_capture",
        "control_flow/switch_multicaseval",
        "control_flow/switch_block",

        "control_flow/while",
        "control_flow/while_block",

        // Nested Control Flow (2-level nesting)
        "control_flow/if_if",
        "control_flow/if_for",
        "control_flow/if_switch",
        "control_flow/if_while",
        "control_flow/if_if_only",
        "control_flow/if_if_only_block",
        "control_flow/if_else_if",
        "control_flow/if_capture",

        "control_flow/for_if",
        "control_flow/for_for",
        "control_flow/for_switch",
        "control_flow/for_while",

        "control_flow/switch_if",
        "control_flow/switch_for",
        "control_flow/switch_switch",
        "control_flow/switch_while",

        "control_flow/while_if",
        "control_flow/while_for",
        "control_flow/while_switch",
        "control_flow/while_while",
        "control_flow/while_capture",
        "control_flow/while_else",
        "control_flow/while_error",

        // Deeply Nested Control Flow (3-level nesting)
        "control_flow/if_for_if",
        "control_flow/if_while_if",

        // Expression
        "expression/text",
        "expression/format",
        "expression/component",
        "expression/mixed",
        "expression/struct_access",
        "expression/function_call",
        "expression/multiline_string",

        // Component
        "component/basic",
        "component/multiple",
        "component/nested",
        "component/children_only",
        "component/contexted",
        "component/contexted_props",
        "component/react",
        "component/csr_react_multiple",
        "component/csr_zig",
        "component/import",
        "component/root_cmp",
        "component/caching",

        // Attribute
        "attribute/builtin",
        "attribute/component",
        "attribute/builtin_escaping",
        "attribute/dynamic",
        "attribute/types",
        "attribute/shorthand",
        "attribute/spread",

        // Element
        "element/void",
        "element/empty",
        "element/nested",
        "element/fragment",
        "element/fragment_root",

        // Expression
        "expression/optional",
        "expression/template",

        // Raw
        "escaping/pre",
        "escaping/quotes",
    };
    pub fn init(allocator: std.mem.Allocator) !TestFileCache {
        var cache = TestFileCache{
            .files = std.StringHashMap([]const u8).init(allocator),
            .allocator = allocator,
        };

        const base_path = "test/data/";

        // Load .zx and .zig files for each test file
        for (test_files) |file_path| {
            for ([_]struct { ext: []const u8 }{ .{ .ext = ".zx" }, .{ .ext = ".zig" } }) |ext_info| {
                const full_path = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ base_path, file_path, ext_info.ext });
                defer allocator.free(full_path);

                const content = std.Io.Dir.cwd().readFileAlloc(
                    std.testing.io,
                    full_path,
                    allocator,
                    .limited(std.math.maxInt(usize)),
                ) catch |err| switch (err) {
                    error.FileNotFound => continue,
                    else => return err,
                };
                const cache_key = try std.fmt.allocPrint(allocator, "{s}{s}", .{ file_path, ext_info.ext });
                try cache.files.put(cache_key, content);
            }
        }

        return cache;
    }

    pub fn deinit(self: *TestFileCache) void {
        var it = self.files.iterator();
        while (it.next()) |entry| {
            self.allocator.free(entry.key_ptr.*);
            self.allocator.free(entry.value_ptr.*);
        }
        self.files.deinit();
    }

    pub fn get(self: *TestFileCache, path: []const u8) !?[]const u8 {
        // Check if file is already in cache
        if (self.files.get(path)) |content| {
            return content;
        }

        // File not in cache, try to fetch it from disk
        const base_path = "test/data/";
        const full_path = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{ base_path, path });
        defer self.allocator.free(full_path);

        const content = std.Io.Dir.cwd().readFileAlloc(
            std.testing.io,
            full_path,
            self.allocator,
            .limited(std.math.maxInt(usize)),
        ) catch |err| switch (err) {
            error.FileNotFound => return null,
            else => return err,
        };

        // Cache the file for future calls
        const cache_key = try std.fmt.allocPrint(self.allocator, "{s}", .{path});
        try self.files.put(cache_key, content);

        return content;
    }
};

pub fn shouldRunSlowTest() bool {
    return testing.environ.contains(testing.allocator, "E2E") catch false;
}

pub fn shouldRunNetworkTest() bool {
    return testing.environ.contains(testing.allocator, "E2E_NET") catch false;
}

const std = @import("std");
const testing = std.testing;
const zx = @import("zx");
