var routes: []const AppRoute = &[_]AppRoute{};

pub fn Page(ctx: zx.PageContext) zx.Component {
    return (
        <div @allocator={ctx.arena} class="app-container">
            <div class="content">
                <div style="margin-bottom: 24px;">
                    <div class="current-route-label">Current route</div>
                    <div class="route-input-row">
                        <span class="route-arrow">↳</span>
                        // <input type="text" value="/" class="route-input" />
                        <ComponentSearchInput @rendering={.client} />
                    </div>
                    <div class="route-hint">
                        Press <strong>Enter</strong> to navigate <span class="no-match">(no match)</span>
                    </div>
                </div>

                <ComponentRotes @rendering={.client} />
                
            </div>
        </div>
    );
}

var fetched = false;
fn fetch() void {
    if (fetched) return;
    fetched = true;
    _ = zx.fetch(.wasm(&onFetchText), zx.client_allocator, "http://localhost:5588/.well-known/_zx/devtool?meta=true", .{ .method = .GET }) catch {};
}

    pub const AppRoute = struct {
        path: []const u8,
        method: []const u8 = "NA",
        has_notfound: bool = false,
        is_dynamic: bool = false,
    };


fn onFetchText(res: ?*zx.Fetch.Response, _: ?zx.Fetch.FetchError) void {
    if (res) |r| {
        defer r.deinit();
        if (r.text()) |p| {
            routes = zx.prop.parse([]const AppRoute, zx.client_allocator, p);
        } else |_| {}
    }
    zx.rerender();
}


fn getMethodClass(method: []const u8) []const u8 {
    if (std.mem.eql(u8, method, "GET")) return "route-method method-get";
    if (std.mem.eql(u8, method, "POST")) return "route-method method-post";
    if (std.mem.eql(u8, method, "PUT")) return "route-method method-put";
    if (std.mem.eql(u8, method, "DELETE")) return "route-method method-delete";
    return "route-method";
}

const ComponentRoutesProps = struct {  };
pub fn ComponentRotes(
    ctx: *zx.ComponentCtx(ComponentRoutesProps),
) zx.Component {
    fetch();
    return (
        <div @allocator={ctx.allocator} class="routes-section" >
            <input type="checkbox" id="routes-toggle" class="routes-toggle" checked />
            <label for="routes-toggle" class="routes-section-row">
                {icons.Workflow(ctx.allocator)}
                <span class="routes-section-title">All Routes</span>
                <span class="routes-section-arrow"></span>
            </label>
            <div class="routes-section-desc">
                <span class="routes-count">{getFilteredRouteCount(ctx.allocator)} routes registered in your application</span>
                <div class="routes-children">
                    {for (routes) |route| (
                        <div @allocator={ctx.allocator} class={getRouteItemClass(route.path, route.method)}>
                            <span class={getMethodClass(route.method)}>{route.method}</span>
                            <span class="route-path">{route.path}</span>
                        </div>
                    )}
                </div>
            </div>
        </div>
    );
}


var inputvalue: []const u8 = "";
const ComponentSearchInputProps = struct {  };
pub fn ComponentSearchInput(
    ctx: *zx.ComponentCtx(ComponentSearchInputProps),
) zx.Component {
    return (
        <input @allocator={ctx.allocator} oninput={handleComponentSearchInput} type="text" value="/" class="route-input" />
    );
}

pub fn handleComponentSearchInput(ctx: zx.EventContext) void {
    inputvalue = ctx.value() orelse "";
    zx.rerender();
}

fn routeMatchesSearch(path: []const u8, method: []const u8) bool {
    if (inputvalue.len == 0) return true;
    if (containsIgnoreCase(path, inputvalue)) return true;
    if (containsIgnoreCase(method, inputvalue)) return true;
    return false;
}

fn getRouteItemClass(path: []const u8, method: []const u8) []const u8 {
    if (routeMatchesSearch(path, method)) return "route-item";
    return "route-item route-item-hidden";
}

fn getFilteredRouteCount(allocator: std.mem.Allocator) []const u8 {
    var count: usize = 0;
    for (routes) |route| {
        if (routeMatchesSearch(route.path, route.method)) count += 1;
    }
    return std.fmt.allocPrint(allocator, "{d}", .{count}) catch "0";
}

fn containsIgnoreCase(haystack: []const u8, needle: []const u8) bool {
    if (needle.len > haystack.len) return false;
    var i: usize = 0;
    while (i <= haystack.len - needle.len) : (i += 1) {
        var match = true;
        for (0..needle.len) |j| {
            if (std.ascii.toLower(haystack[i + j]) != std.ascii.toLower(needle[j])) {
                match = false;
                break;
            }
        }
        if (match) return true;
    }
    return false;
}

const zx = @import("zx");
const std = @import("std");
const icons = @import("../components/icons.zx");
const data = @import("../data.zig");
