pub fn Page(ctx: zx.PageContext) zx.Component {
    return (
        <div @allocator={ctx.arena} class="todo-container">
            <TodoApp @rendering={.client} />
        </div>
    );
}

const Todo = struct { id: u32, text: []const u8, completed: bool };
pub fn TodoApp(allocator: zx.Allocator) zx.Component {
    initTodos(todos_allocator);
    if (builtin.cpu.arch != .wasm32) resetTodosForSSR(todos_allocator);

    return (
        <div @{allocator} class="todo-app">
            <div class="todo-header">
                <h1 class="todo-title">ZX + WASM</h1>
                <Counter />
                <p class="todo-subtitle">A demo todo app built with ZX Client Side Rendering</p>
            </div>

            <form class="todo-input-section" onsubmit={addTodo}>
                <input type="text" class="todo-input" id="todo-input" placeholder="Add a new todo..." required />
                <button class="todo-add-btn" type="submit">Add</button>
            </form>

            <div class="todo-stats">
                <span class="todo-stat"><strong>{todos.items.len}</strong> total</span>
                <span class="todo-stat-divider">•</span>
                <span class="todo-stat"><strong>{countCompleted()}</strong> done</span>
                <span class="todo-stat-divider">•</span>
                <span class="todo-stat"><strong>{todos.items.len - countCompleted()}</strong> left</span>
                <button class="todo-clear-btn" onclick={clearTodos}>Clear All</button>
            </div>

            <ul class="todo-list">
                {for (todos.items) |todo| (
                    <TodoItem id={todo.id} text={todo.text} completed={todo.completed} />
                )}
            </ul>
        </div>
    );
}

fn TodoItem(ctx: *zx.ComponentCtx(struct { id: u32, text: []const u8, completed: bool })) zx.Component {
    const class = if (ctx.props.completed) "todo-item todo-item-completed" else "todo-item";
    const key = ctx.props.id;
    const value = ctx.props.id;

    return (
        <li @allocator={ctx.allocator} {class} {key}>
            <input type="button" class="todo-checkbox" {value} onclick={toggleTodo} />
            <span> {ctx.props.text} </span>
            <button class="todo-delete-btn" {value} onclick={deleteTodo}>×</button>
        </li>
    );
}

const initial_todos = [_]Todo{
    .{ .id = 1, .text = "Build a fast web app with ZX", .completed = true },
    .{ .id = 2, .text = "Implement server-side rendering", .completed = true },
    .{ .id = 3, .text = "Implement client-side rendering", .completed = false },
};
var next_todo_id: u32 = 4; // Start after initial todos (1, 2, 3)
var initialized: bool = false;
var todos = std.ArrayList(Todo).empty;
var todos_allocator: std.mem.Allocator = if (builtin.cpu.arch == .wasm32) std.heap.wasm_allocator else std.heap.page_allocator;

fn initTodos(allocator: zx.Allocator) void {
    if (initialized) return;
    initialized = true;
    todos_allocator = allocator;
    todos.appendSlice(allocator, &initial_todos) catch @panic("OOM");
}

fn resetTodosForSSR(allocator: zx.Allocator) void {
    todos = std.ArrayList(Todo).empty;
    todos_allocator = allocator;
    todos.appendSlice(allocator, &initial_todos) catch @panic("OOM");
}

fn addTodo(ctx: zx.EventContext) void {
    ctx.preventDefault();
    if (comptime builtin.cpu.arch != .wasm32) return;

    const document = zx.client.Document.init(todos_allocator);
    const input = document.getElementById("todo-input") catch return;

    const text = input.ref.getAlloc(js.String, todos_allocator, "value") catch return;
    if (text.len == 0) return;

    const id = next_todo_id;
    next_todo_id += 1;
    todos.append(todos_allocator, .{ .id = id, .text = text, .completed = false }) catch @panic("OOM");
    input.ref.set("value", js.string("")) catch return;
    zx.rerender();
}

fn handleInputChange(ctx: zx.EventContext) void {
    const event = ctx.getEvent();
    defer event.deinit();
    const target = event.getTarget() orelse return;
    defer target.deinit();
    const value = target.getAlloc(js.String, todos_allocator, "value") catch return;

    const id = next_todo_id;
    next_todo_id += 1;
    todos.append(todos_allocator, .{ .id = id, .text = value, .completed = false }) catch @panic("OOM");
}
fn toggleTodo(ctx: zx.EventContext) void {
    if (comptime builtin.cpu.arch != .wasm32) return;

    const event = ctx.getEvent();
    defer event.deinit();
    var id: ?u32 = null;
    if (event.getTarget()) |target| {
        defer target.deinit();
        if (target.getAlloc(js.String, todos_allocator, "value") catch null) |v| {
            id = std.fmt.parseInt(u32, v, 10) catch null;
        }
    }

    for (todos.items) |*todo| {
        if (todo.id == id) {
            todo.completed = !todo.completed;
            break;
        }
    }
    zx.rerender();
}

fn clearTodos(_: zx.EventContext) void {
    todos.clearRetainingCapacity();
    zx.rerender();
}

fn deleteTodo(ctx: zx.EventContext) void {
    if (comptime builtin.cpu.arch != .wasm32) return;

    const event = ctx.getEvent();
    defer event.deinit();
    var id: ?u32 = null;
    if (event.getTarget()) |target| {
        defer target.deinit();
        if (target.getAlloc(js.String, todos_allocator, "value") catch null) |v| {
            id = std.fmt.parseInt(u32, v, 10) catch null;
        }
    }

    for (todos.items, 0..) |todo, i| {
        if (todo.id == id) {
            _ = todos.orderedRemove(i);
            break;
        }
    }
    zx.rerender();
}

fn countCompleted() u32 {
    var count: u32 = 0;
    for (todos.items) |todo| {
        if (todo.completed) count += 1;
    }
    return count;
}

// This is temporarily here to validated pagecontext is buildable for wasm target
fn Counter(allocator: zx.Allocator) zx.Component {
    const ctx = zx.PageContext{
        .request = .{
            .url = "",
            .method = .GET,
            .pathname = "",
            .headers = .{},
            .arena = allocator,
        },
        .response = .{ .arena = allocator },
        .allocator = allocator,
        .arena = allocator,
    };
    const counter_page = CounterPage(ctx);
    return (
        <div @allocator={allocator} style="display: none;">
            {counter_page}
        </div>
    );
}

pub const CounterPage = @import("./simple/page.zx").Page;

const zx = @import("zx");
const std = @import("std");
const builtin = @import("builtin");
const client = zx.client;
const js = zx.Client.js;
