/// Represents a file or folder in the project tree
pub const Entry = struct {
    name: []const u8,
    description: ?[]const u8 = null,
    children: ?[]const Entry = null,

    pub fn isFolder(self: Entry) bool {
        return self.children != null;
    }
};

/// The default ZX project structure template
pub const default_project: Entry = .{
    .name = "my-app",
    .description = "Project root",
    .children = &.{
        .{ .name = "build.zig", .description = "Zig build entrypoint and ZX build steps" },
        .{ .name = "build.zig.zon", .description = "Zig package manifest and dependencies" },
        // .{ .name = "package.json", .description = "JS deps for React/WASM client integration" },
        // .{ .name = "tsconfig.json", .description = "TypeScript config for client components" },
        .{ .name = "README.md", .description = "Project overview and quick start" },
        .{
            .name = "app",
            .description = "Application source root (pages, assets, public, entrypoints)",
            .children = &.{
                .{ .name = "main.zig", .description = "Server entrypoint and app bootstrap" },
                // .{ .name = "main.ts", .description = "React/WASM client bootstrap (browser entry)" },
                .{
                    .name = "assets",
                    .description = "Static assets bundled and served at /assets/*",
                    .children = &.{
                        .{ .name = "style.css", .description = "Starter global styles" },
                    },
                },
                .{
                    .name = "pages",
                    .description = "File-based routes and layouts",
                    .children = &.{
                        .{ .name = "layout.zx", .description = "Root layout shared by all routes" },
                        .{ .name = "page.zx", .description = "Example page client component" },
                        .{ .name = "client.zx", .description = "ZX client component (counter logic)" },
                        // .{ .name = "client.tsx", .description = "React client component example" },
                        .{
                            .name = "about",
                            .children = &.{
                                .{ .name = "page.zx", .description = "About page (/about)" },
                            },
                        },
                    },
                },
                .{
                    .name = "public",
                    .description = "Static files served to the site root (/*)",
                    .children = &.{
                        .{ .name = "favicon.ico", .description = "Site favicon" },
                    },
                },
            },
        },
    },
};

pub const ProjectTreeProps = struct {
    root: Entry = default_project,
    show_descriptions: bool = true,
};

pub fn ProjectTree(allocator: zx.Allocator, props: ProjectTreeProps) zx.Component {
    return (
        <div class="project-tree" @allocator={allocator}>
            <div class="project-tree-container">
                <EntryRow entry={props.root} depth={0} is_last={true} prefix="" show_descriptions={props.show_descriptions} />
            </div>
        </div>
    );
}

const EntryRowProps = struct {
    entry: Entry,
    depth: usize,
    is_last: bool,
    prefix: []const u8,
    show_descriptions: bool = true,
};

fn EntryRow(allocator: zx.Allocator, props: EntryRowProps) zx.Component {
    const entry = props.entry;
    const is_folder = entry.isFolder();

    // Calculate indentation based on depth (in rem units)
    const indent = std.fmt.allocPrint(allocator, "padding-left: {d}rem", .{props.depth * 1}) catch "padding-left: 0";

    return (
        <>
            <div class="project-tree-row" style={indent} @allocator={allocator}>
                <span class={if (is_folder) "project-tree-folder" else "project-tree-file"} translate="no">
                    {if (is_folder) (<FolderIcon />) else (<FileIcon name={entry.name} />)}
                    <span class="project-tree-name">{entry.name}{if (is_folder) "/" else ""}</span>
                </span>
                {if (props.show_descriptions and entry.description != null) (
                    <span class="project-tree-desc">{entry.description.?}</span>
                )}
            </div>
            {if (entry.children) |children| (
                renderChildren(allocator, children, props.depth + 1, props.show_descriptions)
            )}
        </>
    );
}

fn renderChildren(allocator: zx.Allocator, children: []const Entry, depth: usize, show_descriptions: bool) zx.Component {
    var components = allocator.alloc(zx.Component, children.len) catch return zx.Component.none;
    for (children, 0..) |child, i| {
        const is_last = i == children.len - 1;
        components[i] = EntryRow(allocator, .{
            .entry = child,
            .depth = depth,
            .is_last = is_last,
            .prefix = "",
            .show_descriptions = show_descriptions,
        });
    }
    return (<fragment @allocator={allocator}>{components}</fragment>);
}

fn FolderIcon(allocator: zx.Allocator) zx.Component {
    return (
        <svg class="project-tree-icon project-tree-icon-folder" viewBox="0 0 24 24" fill="none" @allocator={allocator}>
            <path d="M3 7C3 5.89543 3.89543 5 5 5H9.58579C9.851 5 10.1054 5.10536 10.2929 5.29289L12 7H19C20.1046 7 21 7.89543 21 9V17C21 18.1046 20.1046 19 19 19H5C3.89543 19 3 18.1046 3 17V7Z" fill="currentColor" />
        </svg>
    );
}

const FileIconProps = struct {
    name: []const u8,
};

fn FileIcon(allocator: zx.Allocator, props: FileIconProps) zx.Component {
    const ext = getExtension(props.name);
    
    if (std.mem.eql(u8, ext, ".zig") or std.mem.eql(u8, ext, ".zon")) {
        return icons.ZigFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".zx")) {
        return icons.ZxFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".css")) {
        return icons.CssFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".md") or std.mem.eql(u8, ext, ".html")) {
        return icons.MdFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".ico")) {
        return icons.ImageFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".js")) {
        return icons.JsFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".ts")) {
        return icons.TsFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".jsx")) {
        return icons.JsxFileIcon(allocator);
    } else if (std.mem.eql(u8, ext, ".tsx")) {
        return icons.TsxFileIcon(allocator);
    } else {
        return icons.GenericFileIcon(allocator);
    }
}

fn getExtension(name: []const u8) []const u8 {
    var i = name.len;
    while (i > 0) : (i -= 1) {
        if (name[i - 1] == '.') {
            return name[i - 1 ..];
        }
    }
    return "";
}

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