pragma Singleton

// The login keyring's lock state.
//
// The keyring is unlocked at sign-in by pam_gnome_keyring, exactly as it is
// under GNOME. What a bare Hyprland session lacks is anywhere to see when that
// has stopped being true.
//
// It stops being true rarely but expensively: gnome-keyring-daemon can crash,
// D-Bus activates a replacement, and the replacement never received the login
// password -- so the keyring is locked in the middle of a session that unlocked
// it correctly. Nothing announces this. What the user sees instead is a mail
// account that will not authenticate, a git push that cannot find its key, or
// an integration reporting "not configured", none of which mention keyrings.
//
// Checked on demand and after an unlock, not polled: the state changes only
// when a daemon dies or a password is entered.

import Quickshell
import Quickshell.Io
import QtQuick

Singleton {
    id: root

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

    property bool available: false
    property bool locked: false
    property bool scanned: false
    property bool unlocking: false
    property string lastError: ""

    // "pam" when the daemon that holds the keyring is the one PAM started at
    // login, "dbus" when it is a D-Bus-activated replacement -- which is the
    // signature of the crash case, and worth showing, because a dbus daemon
    // that is currently unlocked was unlocked by hand and will not survive.
    property string daemon: ""

    readonly property bool replacementDaemon: root.daemon === "dbus"

    function refresh(): void {
        if (!query.running)
            query.running = true;
    }

    // Raises the standard password dialog. The password never passes through
    // Panama -- the Secret Service prompts, the same way it does under GNOME.
    function unlock(): void {
        if (root.unlocking)
            return;
        root.unlocking = true;
        unlockProcess.running = true;
    }

    function absorb(text: string): void {
        try {
            const parsed = JSON.parse(text);
            root.available = parsed.available === true;
            root.locked = parsed.locked === true;
            root.daemon = String(parsed.daemon ?? "");
            root.lastError = String(parsed.error ?? "");
        } catch (error) {
            root.available = false;
            root.lastError = "Could not read the keyring helper's output.";
            console.warn("Keyring: could not parse helper output:", error);
        }
        root.scanned = true;
    }

    Process {
        id: query
        command: [root.helperPath, "status"]
        stdout: StdioCollector { onStreamFinished: root.absorb(this.text) }
    }

    Process {
        id: unlockProcess
        command: [root.helperPath, "unlock"]
        stdout: StdioCollector { onStreamFinished: root.absorb(this.text) }
        onExited: root.unlocking = false
    }
}
