pragma Singleton

// Online accounts, via GNOME Online Accounts.
//
// The daemon already runs in this session -- gvfs activates it, and accounts
// work without gnome-shell anywhere. Only the panel was GNOME's; the accounts
// are D-Bus objects anything may read and modify. So listing, per-service
// toggles, and removal all happen here, natively.
//
// Signing in is the exception, and only for OAuth providers. The daemon's
// AddAccount takes credentials as an argument rather than obtaining them, and
// the code that runs Google's OAuth exchange lives in libgoa-backend, which
// Fedora ships without a GIR binding. So that one step is handed to GNOME's
// panel and the user comes straight back here.
//
// Read on demand and after every change: accounts are added and removed by
// people, not by the system, so there is nothing to poll for.

import Quickshell
import Quickshell.Io
import QtQuick

Singleton {
    id: root

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

    // [{ path, provider, providerName, identity, needsAttention, services: [{key,label,enabled}] }]
    property var accounts: []
    property bool scanned: false
    property bool busy: false
    property string lastError: ""

    // Accounts whose stored credentials have stopped working -- an expired
    // token, a changed password. GOA knows, and nothing outside its own panel
    // ever says so, which is how an account quietly stops syncing for weeks.
    readonly property int attentionCount: root.accounts.filter(a => a.needsAttention).length

    readonly property bool available: root.lastError === ""

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

    // Enabling a service clears GOA's "disabled" flag; the helper owns that
    // inversion so the UI can speak in terms of what is on.
    function setService(path: string, service: string, enabled: bool): void {
        if (root.busy)
            return;
        root.busy = true;
        write.command = [root.helperPath, "set", path, service, enabled ? "true" : "false"];
        write.running = true;
    }

    function remove(path: string): void {
        if (root.busy)
            return;
        root.busy = true;
        write.command = [root.helperPath, "remove", path];
        write.running = true;
    }

    Process {
        id: list
        command: [root.helperPath, "list"]
        stdout: StdioCollector {
            onStreamFinished: {
                try {
                    const parsed = JSON.parse(this.text);
                    root.accounts = Array.isArray(parsed.accounts) ? parsed.accounts : [];
                    root.lastError = String(parsed.error ?? "");
                } catch (error) {
                    root.accounts = [];
                    root.lastError = "Could not read the accounts helper's output.";
                    console.warn("OnlineAccounts: could not parse helper output:", error);
                }
                root.scanned = true;
            }
        }
    }

    Process {
        id: write
        stderr: StdioCollector {
            onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim()
        }
        // Re-read rather than assuming the write landed: GOA may refuse, and a
        // toggle that sprang back is the honest outcome.
        onExited: {
            root.busy = false;
            root.refresh();
        }
    }
}
