import Adw from "gi://Adw";
import GLib from "gi://GLib";
import Gtk from "gi://Gtk";
import Soup from "gi://Soup";

import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";

export default class SnapshellPreferences extends ExtensionPreferences {
    fillPreferencesWindow(window) {
        const settings = this.getSettings();

        const session = new Soup.Session({
            timeout: 5,
            idle_timeout: 5,
            user_agent: "Snapshell/1",
        });

        const groupNames = new Gtk.StringList();
        let groupIds = [];

        function jsonRpc(method, params) {
            return JSON.stringify({
                jsonrpc: "2.0",
                id: 1,
                method: method,
                params: params || {},
            });
        }

        function postJson(url, bodyStr) {
            return new Promise((resolve, reject) => {
                const msg = Soup.Message.new("POST", url);
                msg.request_headers.append("Content-Type", "application/json");
                msg.set_request_body_from_bytes(
                    "application/json",
                    new GLib.Bytes(bodyStr),
                );

                session.send_and_read_async(
                    msg,
                    GLib.PRIORITY_DEFAULT,
                    null,
                    (sess, res) => {
                        try {
                            const bytes = sess.send_and_read_finish(res);
                            const text = new TextDecoder().decode(
                                bytes.get_data(),
                            );
                            resolve(JSON.parse(text));
                        } catch (e) {
                            reject(e);
                        }
                    },
                );
            });
        }

        function labelForGroup(g) {
            const clients = g && g.clients ? g.clients : [];
            let connected = 0;
            for (const c of clients) {
                if (c && c.connected) connected++;
            }
            const total = clients.length;

            let base = "Group";
            if (g && g.name && String(g.name).trim().length > 0)
                base = String(g.name).trim();
            else if (g && g.stream_id) base = String(g.stream_id);

            return `${base} (${connected}/${total})`;
        }

        function clearGroups() {
            while (groupNames.get_n_items() > 0) groupNames.remove(0);
            groupIds = [];
        }

        function syncSelectionFromSettings(groupRow) {
            const selectedId = settings.get_string("group-id");
            const idx = groupIds.indexOf(selectedId);
            groupRow.selected = idx >= 0 ? idx : Gtk.INVALID_LIST_POSITION;
        }

        async function fetchGroups(statusRow, groupRow) {
            const url = settings.get_string("server-url").trim();
            if (!url) {
                clearGroups();
                statusRow.subtitle = "Set Server URL first.";
                return;
            }

            statusRow.subtitle = "Fetching…";
            try {
                const resp = await postJson(
                    url,
                    jsonRpc("Server.GetStatus", {}),
                );
                const groups =
                    resp &&
                    resp.result &&
                    resp.result.server &&
                    resp.result.server.groups
                        ? resp.result.server.groups
                        : [];

                clearGroups();
                for (const g of groups) {
                    groupNames.append(labelForGroup(g));
                    groupIds.push(g.id);
                }

                statusRow.subtitle = `Loaded ${groups.length} group(s).`;
                syncSelectionFromSettings(groupRow);
            } catch (e) {
                clearGroups();
                statusRow.subtitle = `Failed: ${e && e.message ? e.message : e}`;
            }
        }

        // ----- UI -----
        const page = new Adw.PreferencesPage({
            title: "Snapshell",
            icon_name: "audio-volume-high-symbolic",
        });
        window.add(page);

        const connectionGroup = new Adw.PreferencesGroup({
            title: "Connection",
            description: "Configure Snapserver JSON-RPC endpoint.",
        });
        page.add(connectionGroup);

        const urlRow = new Adw.EntryRow({
            title: "Server URL",
            text: settings.get_string("server-url"),
        });
        connectionGroup.add(urlRow);

        urlRow.connect("notify::text", () => {
            settings.set_string("server-url", urlRow.text.trim());
        });

        const targetGroup = new Adw.PreferencesGroup({
            title: "Target group",
            description:
                "Choose which Snapcast group the Quick Settings slider controls.",
        });
        page.add(targetGroup);

        const groupRow = new Adw.ComboRow({
            title: "Snapcast group",
            model: groupNames,
        });
        targetGroup.add(groupRow);

        const statusRow = new Adw.ActionRow({
            title: "Refresh groups",
            subtitle: "Fetch groups from Server.GetStatus",
        });
        const refreshBtn = new Gtk.Button({ label: "Refresh" });
        statusRow.add_suffix(refreshBtn);
        statusRow.activatable_widget = refreshBtn;
        targetGroup.add(statusRow);

        const behaviorGroup = new Adw.PreferencesGroup({ title: "Behavior" });
        page.add(behaviorGroup);

        const pollRow = new Adw.SpinRow({
            title: "Poll interval (seconds)",
            adjustment: new Gtk.Adjustment({
                lower: 2,
                upper: 60,
                step_increment: 1,
                page_increment: 5,
                value: settings.get_uint("poll-seconds") || 5,
            }),
        });
        behaviorGroup.add(pollRow);

        pollRow.connect("notify::value", () => {
            settings.set_uint("poll-seconds", pollRow.value);
        });

        groupRow.connect("notify::selected", () => {
            const idx = groupRow.selected;
            if (idx === Gtk.INVALID_LIST_POSITION) return;
            const id = groupIds[idx];
            if (id) settings.set_string("group-id", id);
        });

        refreshBtn.connect("clicked", () => {
            fetchGroups(statusRow, groupRow);
        });

        urlRow.connect("notify::text", () => {
            statusRow.subtitle = "URL changed. Click Refresh.";
            clearGroups();
            syncSelectionFromSettings(groupRow);
        });

        // Initial load
        fetchGroups(statusRow, groupRow);
    }
}
