import app from "ags/gtk4/app";
import { Astal, Gtk } from "ags/gtk4";
import { readFile } from "ags/file";
import { execAsync } from "ags/process";
import css from "./switcher.css";
import GLib from "gi://GLib";

type SwitcherItem = {
    type: "theme" | "wallpaper";
    path: string;
    name: string;
    icon?: string;
    wallpaper: string;
    active?: boolean;
    accent?: string;
    accent2?: string;
    muted?: string;
};

const HYPR_DIR = GLib.getenv("HYPR_DIR") || "/home/pascal/.config/hypr";
const SCRIPT_DIR = `${HYPR_DIR}/Scripts`;
const THEME_DIR = GLib.getenv("HYPR_SWITCHER_THEME_DIR") || `${HYPR_DIR}/Themes`;
const WALLPAPER_DIR = GLib.getenv("HYPR_SWITCHER_WALLPAPER_DIR")
    || GLib.getenv("WALLPAPER_DIR")
    || `${GLib.get_home_dir()}/Bilder/Wallpaper`;
const CURRENT_WALLPAPER = `${HYPR_DIR}/current-wallpaper`;
const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif"];

type UiTheme = {
    accent: string;
    accent2: string;
    background: string;
    backgroundSoft: string;
    foreground: string;
    muted: string;
    panelHex: string;
};

function notify(message: string) {
    execAsync(["notify-send", "AGS Switcher", message]).catch(console.error);
}

function fileExists(path: string) {
    return GLib.file_test(path, GLib.FileTest.EXISTS);
}

function readText(path: string) {
    try {
        return readFile(path);
    } catch {
        return "";
    }
}

function listFiles(dir: string, predicate: (path: string, name: string) => boolean) {
    try {
        const directory = GLib.Dir.open(dir, 0);
        const files: string[] = [];
        let name = directory.read_name();

        while (name !== null) {
            const path = `${dir}/${name}`;
            if (predicate(path, name)) {
                files.push(path);
            }
            name = directory.read_name();
        }

        return files.sort((a, b) => a.localeCompare(b));
    } catch {
        return [];
    }
}

function shellValue(contents: string, key: string) {
    const regex = new RegExp(`^${key}=(["']?)(.*?)\\1$`, "m");
    return contents.match(regex)?.[2] || "";
}

function activeTheme(): UiTheme {
    const activeWallpaper = currentWallpaper();
    const fallback = {
        accent: "#00ff9c",
        accent2: "#00cc88",
        background: "rgba(20, 20, 30, 0.95)",
        backgroundSoft: "rgba(40, 40, 55, 0.8)",
        foreground: "#cdd6f4",
        muted: "#cccccc",
        panelHex: "#282837",
    };

    const themeFile = listFiles(THEME_DIR, (_path, name) => name.endsWith(".theme"))
        .find(path => shellValue(readText(path), "WALLPAPER") === activeWallpaper);

    if (!themeFile) {
        return fallback;
    }

    const contents = readText(themeFile);
    return {
        accent: shellValue(contents, "ACCENT") || fallback.accent,
        accent2: shellValue(contents, "ACCENT_2") || fallback.accent2,
        background: shellValue(contents, "BACKGROUND") || fallback.background,
        backgroundSoft: shellValue(contents, "BACKGROUND_SOFT") || fallback.backgroundSoft,
        foreground: shellValue(contents, "FOREGROUND") || fallback.foreground,
        muted: shellValue(contents, "MUTED") || fallback.muted,
        panelHex: shellValue(contents, "PANEL_HEX") || fallback.panelHex,
    };
}

function themeCss(theme: UiTheme) {
    return [
        `@define-color ags_accent ${theme.accent};`,
        `@define-color ags_accent_2 ${theme.accent2};`,
        `@define-color ags_bg ${theme.background};`,
        `@define-color ags_bg_soft ${theme.backgroundSoft};`,
        `@define-color ags_fg ${theme.foreground};`,
        `@define-color ags_muted ${theme.muted};`,
        `@define-color ags_panel ${theme.panelHex};`,
        css,
    ].join("\n");
}

function basename(path: string) {
    return GLib.path_get_basename(path);
}

function currentWallpaper() {
    return readText(CURRENT_WALLPAPER).trim();
}

function loadThemes(): SwitcherItem[] {
    return listFiles(THEME_DIR, (_path, name) => name.endsWith(".theme"))
        .map(path => {
            const contents = readText(path);
            return {
                type: "theme",
                path,
                name: shellValue(contents, "NAME") || basename(path).replace(/\.theme$/, ""),
                icon: shellValue(contents, "ICON") || "󰌪",
                wallpaper: shellValue(contents, "WALLPAPER"),
                accent: shellValue(contents, "ACCENT") || "#f38ba8",
                accent2: shellValue(contents, "ACCENT_2") || "#cba6f7",
                muted: shellValue(contents, "MUTED") || "#cdd6f4",
            };
        });
}

function loadWallpapers(): SwitcherItem[] {
    const active = currentWallpaper();

    return listFiles(WALLPAPER_DIR, (_path, name) =>
        IMAGE_EXTENSIONS.some(ext => name.toLowerCase().endsWith(ext)),
    ).map(path => ({
        type: "wallpaper",
        path,
        name: basename(path),
        wallpaper: path,
        active: path === active,
    }));
}

function applyItem(item: SwitcherItem) {
    const command = item.type === "theme"
        ? [`${SCRIPT_DIR}/theme-menu.sh`, "--apply", item.path]
        : [`${SCRIPT_DIR}/wallpaper-menu.sh`, "--apply", item.path];

    execAsync(command)
        .then(() => app.quit())
        .catch(error => {
            console.error(error);
            notify(`${item.name} konnte nicht angewendet werden.`);
        });
}

function previewWallpaper(item: SwitcherItem) {
    execAsync([`${SCRIPT_DIR}/wallpaper-menu.sh`, "--preview", item.path]).catch(error => {
        console.error(error);
        notify(`${item.name} konnte nicht geoeffnet werden.`);
    });
}

function Preview({ item }: { item: SwitcherItem }) {
    const hasImage = item.wallpaper && fileExists(item.wallpaper);

    return (
        <box class={hasImage ? "preview" : "preview preview-empty"}>
            {hasImage
                ? <image class="preview-image" file={item.wallpaper} pixelSize={92} />
                : <label label="󰌪" />}
        </box>
    );
}

function Swatches({ item }: { item: SwitcherItem }) {
    return (
        <box class="swatches" spacing={6}>
            {[item.accent, item.accent2, item.muted].map(color => (
                <box class="swatch" css={`background: ${color};`} />
            ))}
        </box>
    );
}

function ItemButton({ item }: { item: SwitcherItem }) {
    const content = (
        <box spacing={14}>
            <Preview item={item} />
            <box
                orientation={Gtk.Orientation.VERTICAL}
                hexpand
                valign={Gtk.Align.CENTER}
            >
                <label
                    class="item-title"
                    xalign={0}
                    ellipsize={3}
                    label={`${item.icon || "󰸉"}  ${item.name}`}
                />
                <label
                    class="item-subtitle"
                    xalign={0}
                    ellipsize={3}
                    label={item.type === "theme"
                        ? basename(item.path)
                        : item.active ? "Aktuelles Wallpaper" : item.path}
                />
            </box>
            {item.type === "theme" ? <Swatches item={item} /> : <box />}
        </box>
    );

    const applyButton = (
        <button
            class={`item ${item.active ? "active" : ""}`}
            hexpand
            onClicked={() => applyItem(item)}
        >
            {content}
        </button>
    );

    if (item.type === "theme") {
        return applyButton;
    }

    return (
        <box class="item-row" spacing={8}>
            {applyButton}
            <button
                class="icon-button preview-button"
                tooltipText="Vorschau"
                onClicked={() => previewWallpaper(item)}
            >
                <label label="󰋩" />
            </button>
        </box>
    );
}

function SwitcherWindow(mode: string) {
    const isTheme = mode === "theme" || mode === "themes";
    const items = isTheme ? loadThemes() : loadWallpapers();
    const title = isTheme ? "Theme wechseln" : "Wallpaper wechseln";
    const empty = isTheme ? `Keine Themes in ${THEME_DIR}` : `Keine Bilder in ${WALLPAPER_DIR}`;

    return (
        <window
            name="ags-hypr-switcher"
            namespace="ags-hypr-switcher"
            class="switcher-window"
            visible
            keymode={Astal.Keymode.EXCLUSIVE}
            anchor={Astal.WindowAnchor.TOP}
            application={app}
        >
            <box
                class="switcher"
                orientation={Gtk.Orientation.VERTICAL}
                spacing={16}
                marginTop={70}
            >
                <box class="header">
                    <label class="title" hexpand xalign={0} label={title} />
                    <button
                        class="icon-button close"
                        tooltipText="Schliessen"
                        onClicked={() => app.quit()}
                    >
                        <label label="" />
                    </button>
                </box>
                {items.length > 0
                    ? (
                        <scrolledwindow
                            class="list-scroll"
                            hscrollbarPolicy={Gtk.PolicyType.NEVER}
                            maxContentHeight={600}
                        >
                            <box orientation={Gtk.Orientation.VERTICAL} spacing={10}>
                                {items.map(item => <ItemButton item={item} />)}
                            </box>
                        </scrolledwindow>
                    )
                    : (
                        <box class="empty">
                            <label label={empty} />
                        </box>
                    )}
            </box>
        </window>
    );
}

app.start({
    css: themeCss(activeTheme()),
    instanceName: "hypr-switcher",
    main(mode = "wallpaper") {
        SwitcherWindow(mode);
    },
});
