pragma Singleton

// The system locale.
//
// Named SystemLocale, not Locale: QML has a built-in Locale value type, and a
// singleton of that name is silently shadowed by it. Every binding then reads
// properties off the wrong thing and the page renders empty with only
// "Cannot read property of undefined" to show for it.
//
// Changing it is privileged: localectl goes through polkit, which prompts
// (hyprpolkitagent serves that in this session). It also only applies to
// programs started afterwards, so `pendingRestart` goes true once a change is
// accepted and the page says a sign-out is needed. Reporting the new locale as
// simply "in effect" would be wrong -- almost nothing on screen would be using
// it yet.

import Quickshell
import Quickshell.Io
import QtQuick

Singleton {
    id: root

    readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-locale"

    // [{ value, label, detail }]
    property var locales: []
    property string current: ""
    property bool scanning: false
    property string lastError: ""

    // True once a change has been accepted but the session has not restarted,
    // so the UI can stop claiming the new locale is already in use.
    property bool pendingRestart: false

    readonly property string currentLabel: {
        const match = root.locales.find(locale => locale.value === root.current);
        return match ? match.label : root.current;
    }

    function refresh(): void {
        if (root.scanning)
            return;
        root.scanning = true;
        readCurrent.running = true;
        list.running = true;
    }

    function set(value: string): void {
        if (value === root.current)
            return;
        apply.command = [root.helperPath, "set", value];
        apply.pendingValue = value;
        apply.running = true;
    }

    Process {
        id: list
        command: [root.helperPath, "list"]
        stdout: StdioCollector {
            onStreamFinished: {
                try {
                    const parsed = JSON.parse(this.text);
                    root.locales = Array.isArray(parsed) ? parsed : [];
                } catch (error) {
                    root.locales = [];
                    console.warn("SystemLocale: could not parse the locale list:", error);
                }
                root.scanning = false;
            }
        }
    }

    Process {
        id: readCurrent
        command: [root.helperPath, "get"]
        stdout: StdioCollector {
            onStreamFinished: root.current = this.text.trim()
        }
    }

    Process {
        id: apply

        property string pendingValue: ""

        // A refused change -- polkit dismissed, or an unknown locale -- must not
        // move the UI. The value is only adopted on a zero exit.
        onExited: code => {
            if (code === 0) {
                root.current = apply.pendingValue;
                root.pendingRestart = true;
                root.lastError = "";
            } else {
                root.lastError = "The system did not accept that language. It may have needed a password.";
            }
        }
    }
}
