import { Variable, bind, execAsync } from "astal";
import { App, Astal, Gtk } from "astal/gtk4";
import { hideWindow, limit, skip } from "@lib/utils";

import { SHELL } from "@/settings.json";

import Hyprland from "gi://AstalHyprland";

const hide = () => App.get_window("clipboard")?.hide();
type Entry = { id: string, content: string };

function ClipboardEntry({ id, content }: Entry) {
    return <button
        cssClasses={["entry"]}
        onClicked={() => {
            hide();
            execAsync([SHELL, "-c", `cliphist decode ${id} | wl-copy`]);
        }}
    >
        <label
            cssClasses={["content"]}
            xalign={0}
            label={content}
        />
    </button>
}

async function getClipboardHistory(history: Variable<Entry[]>) {
    try {
        const ids = await execAsync([SHELL, "-c", "cliphist list | awk '{print $1}'"])
            .then(it => it.split("\n"));
        const contents = await execAsync([SHELL, "-c", "cliphist list | awk '{$1=\"\"; print}'"])
            .then(it => it.split("\n"));

        history.set(ids
            .map((id, index) => ({ id, content: contents[index] }))
            .filter(({ content }) => content && content.length > 0));
    } catch (rer){
        console.error(rer)
        history.set([]);
    }
}

export default async function Clipboard(_monitor_id: number) {
    const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
    const hypr = Hyprland.get_default();

    const history: Variable<Entry[]> = Variable([]);
    const toSkip = Variable(0);

    const list = Variable.derive([
        bind(history),
        bind(toSkip)
    ], (history, count) => limit(
        skip(history, count),
        10
    ));

    const isEmpty = bind(list).as(list => list.length === 0);
    const onScroll = (dy: number) => {
        const value = toSkip.get();

        if (dy < 0) {
            if ((value - 10) < 0) return;
            toSkip.set(value - 10)
        } else {
            if ((value + 10) > history.get().length) return;
            toSkip.set(value + 10)
        }
    };

    const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
        if (self.is_visible()) await getClipboardHistory(history)
    })

    return <window
        visible={false}
        monitor={bind(hypr, "focusedMonitor").as(monitor => monitor.id)}
        name={"clipboard"}
        cssClasses={["clipboard"]}
        keymode={Astal.Keymode.EXCLUSIVE}
        anchor={TOP | BOTTOM | LEFT | RIGHT}
        onKeyPressed={hideWindow}
        application={App}
        setup={setup}>
        <box hexpand={false} cssClasses={["clipboard"]} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
            <box spacing={6} vertical onScroll={(_, __, dy) => onScroll(dy)}>
                {bind(list).as(list => list.map(({ id, content }) => (
                    <ClipboardEntry id={id} content={content} />
                )))}
                <button
                    cssClasses={["entry", "primary"]}
                    visible={isEmpty.as(empty => !empty)}
                    onClicked={() => {
                        execAsync([SHELL, "-c", "cliphist wipe"])
                        history.set([])
                    }}
                >
                    <label
                        cssClasses={["content"]}
                        xalign={0}
                        label="Wipe clipboard history"
                    />
                </button>
            </box>
            <box
                halign={Gtk.Align.CENTER}
                cssClasses={["not-found"]}
                vertical
                visible={isEmpty}>
                <image iconName="system-search-symbolic" />
                <label label="No match found" />
            </box>
        </box>
    </window>
}
