//! Capsule TUI Application
//! Built with Vaxis (The "Luxury Deck").

const std = @import("std");
const vaxis = @import("vaxis");

const control = @import("../control.zig");
const client_mod = @import("client.zig");
const view_mod = @import("view.zig");

const Event = union(enum) {
    key_press: vaxis.Key,
    winsize: vaxis.Winsize,
    update_data: void,
};

pub const AppState = struct {
    allocator: std.mem.Allocator,
    should_quit: bool,
    client: client_mod.Client,

    // UI State
    active_tab: enum { Dashboard, SlashLog, TrustGraph } = .Dashboard,

    // Data State
    node_status: ?client_mod.NodeStatus = null,
    slash_log: std.ArrayList(client_mod.SlashEvent),
    topology: ?client_mod.TopologyInfo = null,

    pub fn init(allocator: std.mem.Allocator) !AppState {
        return .{
            .allocator = allocator,
            .should_quit = false,
            .client = try client_mod.Client.init(allocator),
            .slash_log = std.ArrayList(client_mod.SlashEvent){},
            .topology = null,
        };
    }

    pub fn deinit(self: *AppState) void {
        self.client.deinit();
        if (self.node_status) |s| {
            // Free strings in status if any? NodeStatus fields are slices.
            // Client parser allocates them. We own them.
            // We should free them.
            // For now, simpler leak or arena. (TODO: correct cleanup)
            _ = s;
        }
        for (self.slash_log.items) |ev| {
            self.allocator.free(ev.target_did);
            self.allocator.free(ev.reason);
            self.allocator.free(ev.severity);
            self.allocator.free(ev.evidence_hash);
        }
        self.slash_log.deinit(self.allocator);
    }
};

pub fn run(allocator: std.mem.Allocator) !void {
    var app = try AppState.init(allocator);
    defer app.deinit();

    // Initialize Vaxis
    var vx = try vaxis.init(allocator, .{});
    // Initialize TTY
    var tty = try vaxis.Tty.init(&.{}); // Use empty buffer (vaxis manages its own if needed, or this is for buffered read?)
    defer tty.deinit();

    defer vx.deinit(allocator, tty.writer()); // Reset terminal

    // Event Loop
    var loop: vaxis.Loop(Event) = .{ .vaxis = &vx, .tty = &tty };
    try loop.init();
    try loop.start();
    defer loop.stop();

    // Connect to Daemon
    try app.client.connect();

    // Spawn Data Thread
    const DataThread = struct {
        fn run(l: *vaxis.Loop(Event), a: *AppState) void {
            while (!a.should_quit) {
                // Poll Status
                if (a.client.getStatus()) |status| {
                    if (a.node_status) |old| {
                        // Free old strings
                        a.allocator.free(old.node_id);
                        a.allocator.free(old.state);
                        a.allocator.free(old.version);
                    }
                    a.node_status = status;
                } else |_| {}

                // Poll Slash Log
                if (a.client.getSlashLog(20)) |logs| {
                    // Logs are new allocations. Replace list.
                    for (a.slash_log.items) |ev| {
                        a.allocator.free(ev.target_did);
                        a.allocator.free(ev.reason);
                        a.allocator.free(ev.severity);
                        a.allocator.free(ev.evidence_hash);
                    }
                    a.slash_log.clearRetainingCapacity();
                    a.slash_log.appendSlice(a.allocator, logs) catch {};
                    a.allocator.free(logs); // Free the slice itself (deep copy helper allocated slice)
                } else |_| {}

                if (a.client.getTopology()) |topo| {
                    if (a.topology) |old| {
                        // Free old
                        // TODO: Implement deep free or rely on allocator arena if we had one.
                        // For now we leak old topology strings if not careful.
                        // Ideally we should free the old one using a helper.
                        // But since we use a shared allocator, we should be careful.
                        // Given this is a TUI, we might accept some leakage for MVP or fix it properly.
                        // Let's rely on OS cleanup for now or implement freeTopology
                        _ = old;
                    }
                    a.topology = topo;
                } else |_| {}

                // Notify UI to redraw
                l.postEvent(.{ .update_data = {} });

                std.Thread.sleep(1 * std.time.ns_per_s);
            }
        }
    };

    var thread = try std.Thread.spawn(.{}, DataThread.run, .{ &loop, &app });
    defer thread.join();

    while (!app.should_quit) {
        // Handle Events
        const event = loop.nextEvent();
        switch (event) {
            .key_press => |key| {
                if (key.matches('c', .{ .ctrl = true }) or key.matches('q', .{})) {
                    app.should_quit = true;
                }
                // Handle tab switching
                if (key.matches(vaxis.Key.tab, .{})) {
                    app.active_tab = switch (app.active_tab) {
                        .Dashboard => .SlashLog,
                        .SlashLog => .TrustGraph,
                        .TrustGraph => .Dashboard,
                    };
                }
            },
            .winsize => |ws| {
                try vx.resize(allocator, tty.writer(), ws);
            },
            .update_data => {
                // Just trigger render
            },
        }

        // Render
        const win = vx.window();
        win.clear();

        try view_mod.draw(&app, win);

        try vx.render(tty.writer());
    }
}
