import AstalTray from "gi://AstalTray";
import { bind, GObject } from "astal";
import AstalHyprland from "gi://AstalHyprland";
import { Gtk } from "astal/gtk4";

const SYNC = GObject.BindingFlags.SYNC_CREATE;

const SysTray = () => {
    const trayBox = new Gtk.Box({ cssClasses: ["bar-button"] });
    const tray = AstalTray.get_default();

    const trayItems = new Map<string, Gtk.MenuButton>();
    const trayAddedHandler = tray.connect("item-added", (_, id) => {
        const item = tray.get_item(id);
        const popover = Gtk.PopoverMenu.new_from_model(item.menu_model);
        const icon = new Gtk.Image();
        const button = new Gtk.MenuButton({
            popover,
            child: icon,
            cssClasses: ["tray-item"],
        });

        item.bind_property("gicon", icon, "gicon", SYNC);
        popover.insert_action_group("dbusmenu", item.action_group);
        item.connect("notify::action-group", () => {
            popover.insert_action_group("dbusmenu", item.action_group);
        });

        trayItems.set(id, button);
        trayBox.append(button);
    });

    const trayRemovedHandler = tray.connect("item-removed", (_, id) => {
        const button = trayItems.get(id);
        if (button) {
            trayBox.remove(button);
            button.run_dispose();
            trayItems.delete(id);
        }
    });

    trayBox.connect("destroy", () => {
        tray.disconnect(trayAddedHandler);
        tray.disconnect(trayRemovedHandler);
    });

    return trayBox;
};

const Workspace = () => {
    const hypr = AstalHyprland.get_default();

    return (
        <box>
            {bind(hypr, "workspaces").as(wss =>
                wss
                    .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
                    .sort((a, b) => a.id - b.id)
                    .map(ws => (
                        <button
                            cssClasses={bind(hypr, "focusedWorkspace").as(fw =>
                                ws === fw
                                    ? [
                                          "focused-workspace-button",
                                          "workspace-button",
                                      ]
                                    : ["workspace-button"],
                            )}
                            onButtonPressed={() => ws.focus()}
                            child={<label label={String(ws.id)}></label>}
                        ></button>
                    )),
            )}
        </box>
    );
};

/**
 * Displays the name of the currently active window and provides a popover for
 * displaying all available clients
 */
const ActiveWindow = () => {
    const hypr = AstalHyprland.get_default();
    const focused = bind(hypr, "focusedClient");

    const WindowPopover = (): Gtk.Popover => {
        // Set up boxes + Popover
        const clients = new Map<string, Gtk.Button>();
        const popover = new Gtk.Popover();
        const popoverBox = new Gtk.Box({
            orientation: Gtk.Orientation.VERTICAL,
        });

        const widgetTitle = new Gtk.Label({
            cssClasses: ["title-2"],
            label: "Available Windows",
        });

        popoverBox.append(widgetTitle);

        const seaparator = new Gtk.Separator({
            marginTop: 5,
            marginBottom: 10,
        });

        popoverBox.append(seaparator);

        const addClient = (client: AstalHyprland.Client) => {
            const clientBox = new Gtk.Box();

            // Workspace description
            const descWS = new Gtk.Label({ label: "(WS " });

            // Workpsace information
            const workspace = new Gtk.Label();
            client.workspace.bind_property("name", workspace, "label", SYNC);

            const windowClassDesc = new Gtk.Label({ label: ") [" });

            const windowClass = new Gtk.Label();
            windowClass.label = client.get_initial_class();

            const titleDesc = new Gtk.Label({ label: "] " });
            titleDesc.set_margin_end(2);

            const title = new Gtk.Label();
            client.bind_property("title", title, "label", SYNC);

            clientBox.append(descWS);
            clientBox.append(workspace);
            clientBox.append(windowClassDesc);
            clientBox.append(windowClass);
            clientBox.append(titleDesc);
            clientBox.append(title);

            const button = new Gtk.Button();
            button.connect( 'clicked', () => {
                client.workspace.focus();
            } );
            button.set_child(clientBox);

            popoverBox.append(button);

            clients.set(client.get_address(), button);
        };

        // Populate with already added clients
        const c = hypr.get_clients();
        for (let index = 0; index < c.length; index++) {
            addClient(c[index]);
        }

        hypr.connect("client-added", (_, client) => {
            addClient(client);
        });

        hypr.connect("client-removed", (_, client) => {
            const c = clients.get(client);
            if (c) {
                popoverBox.remove(c);
                c.run_dispose();
                clients.delete(client);
            }
        });

        popover.set_child(popoverBox);
        return popover;
    };

    const windowPopover = WindowPopover();

    // ───────────────────────────────────────────────────────────────────
    // Return fully assembled HyprlandFocusedClient box
    // ───────────────────────────────────────────────────────────────────
    return (
        <box visible={focused.as(Boolean)}>
            <button
                onClicked={() => windowPopover.popup()}
                cssClasses={["bar-button"]}
            >
                {focused.as(
                    client =>
                        client && (
                            <label label={bind(client, "title").as(String)} />
                        ),
                )}
            </button>
            {windowPopover}
        </box>
    );
};

export default {
    Workspace,
    ActiveWindow,
    SysTray,
};
