// The dial's surface and its entries.
//
// RadialDial.qml is the component; this is the panel it lives on and the list
// it renders. Split so the entries can move to the verb tables (TASK-30)
// without touching the ring, and so the ring can be reused by anything else
// that wants a thumb-picker.
//
// Entries are the ones Casey named: kill the current window, screenshot,
// system health, the agent — plus lock, which is the one every phone needs
// within a thumb's reach.
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import "." as DialLocal

Scope {
    id: scope

    property bool dialOpen: false

    Variants {
        model: Quickshell.screens

        PanelWindow {
            id: win
            required property var modelData
            screen: win.modelData

            anchors { top: true; left: true; right: true; bottom: true }
            color: "transparent"
            visible: scope.dialOpen
            WlrLayershell.namespace: "souveraine:dial"
            WlrLayershell.layer: WlrLayer.Overlay
            // OnDemand, not Exclusive: an exclusive grab was tried on the
            // polkit surface 2026-07-26 and stopped touch reaching the layer
            // below it entirely.
            WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
            exclusionMode: ExclusionMode.Ignore

            // `open` is assigned, never bound: openAt() writes it, and a
            // binding here would be broken by that write on the first call.
            // The host drives it in one direction and listens for the close.
            DialLocal.RadialDial {
                id: dial
                entries: scope.entries
                onOpenChanged: {
                    if (!dial.open)
                        scope.dialOpen = false;
                }
            }

            Connections {
                target: scope
                function onDialOpenChanged() {
                    // No touch point to hand over — and none can be computed
                    // here, because this fires on the same signal that maps
                    // the surface, when win.width/height are still 0.
                    if (scope.dialOpen)
                        dial.openAt();
                    else
                        dial.close();
                }
            }
        }
    }

    // The list. `enabled: false` carries a `reason` so a refusal explains
    // itself in the middle of the ring rather than greying out silently.
    readonly property var entries: [
        {
            icon: "lock",
            label: "Lock",
            verb: () => { Haptics.confirm(); Session.lock(); }
        },
        {
            icon: "photo_camera",
            label: "Screenshot",
            verb: () => {
                Haptics.confirm();
                Quickshell.execDetached(["sh", "-c",
                    "grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png"]);
            }
        },
        {
            icon: "cancel",
            label: "Kill window",
            enabled: !!Hyprland.activeToplevel,
            reason: "no focused window to close",
            verb: () => {
                const addr = Hyprland.activeToplevel?.address;
                if (!addr) { Haptics.refuse(); return; }
                Haptics.confirm();
                const a = addr.startsWith("0x") ? addr : "0x" + addr;
                Quickshell.execDetached(["hyprctl", "dispatch",
                    `hl.dsp.window.close({ window = "address:${a}" })`]);
            }
        },
        {
            icon: "monitor_heart",
            label: "Health",
            verb: () => { Haptics.confirm(); GlobalStates.sidebarRightOpen = true; }
        },
        {
            icon: "smart_toy",
            label: "Agent",
            verb: () => { Haptics.confirm(); GlobalStates.sidebarLeftOpen = true; }
        },
        {
            icon: "keyboard",
            label: "Keyboard",
            verb: () => { Haptics.confirm(); GlobalStates.oskOpen = !GlobalStates.oskOpen; }
        }
    ]

    // Anything that can reach this can raise the dial: the pill's long-hold,
    // an edge gesture, a squeeze once grip produces events, or an agent.
    IpcHandler {
        target: "dial"

        function open(): void {
            scope.dialOpen = true;
            console.log("[dial] open -> dialOpen=" + scope.dialOpen
                + " screens=" + Quickshell.screens.length);
        }

        function close(): void {
            scope.dialOpen = false;
        }

        function toggle(): void {
            scope.dialOpen = !scope.dialOpen;
        }

        // What the ring currently offers, so a caller can see the entries
        // without opening it. JSON-over-string: quickshell marshals a `var`
        // return as VOID.
        function entries(): string {
            return JSON.stringify(scope.entries.map(e => ({
                label: e.label,
                icon: e.icon,
                enabled: e.enabled !== false,
                reason: e.reason ?? ""
            })));
        }
    }

    GlobalShortcut {
        name: "dialToggle"
        description: "Toggle the radial dial"
        onPressed: scope.dialOpen = !scope.dialOpen
    }
}
