pragma Singleton

// Redacted Home Assistant configuration state. The helper is the only object
// that touches the private env file; QML never receives the stored token.

import Quickshell
import Quickshell.Io
import QtQuick

Singleton {
    id: root

    readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-home-assistant-config"

    property string url: ""
    property var entities: []
    property bool tokenConfigured: false
    property bool configured: false
    property string lastError: ""
    property string pendingPayload: ""
    property var refreshHomeAssistant: function() { HomeAssistant.refresh(); }
    readonly property bool busy: statusProc.running || writeProc.running

    signal configurationSaved

    function errorMessage(code: string): string {
        switch (code) {
        case "invalid-url": return "Enter an HTTP or HTTPS Home Assistant URL.";
        case "invalid-token": return "The access token contains unsupported characters.";
        case "invalid-entities": return "Entity IDs must look like light.living_room.";
        case "read-failed": return "The private configuration file could not be read.";
        case "write-failed": return "The private configuration file could not be saved.";
        case "invalid-payload": return "The configuration could not be validated.";
        default: return code ? "Home Assistant configuration is unavailable." : "";
        }
    }

    function applyResult(text: string, saved: bool): void {
        let result = null;
        try {
            result = JSON.parse(text);
        } catch (error) {
            result = { ok: false, error: "invalid-response" };
        }
        if (result.ok !== true) {
            root.lastError = root.errorMessage(String(result.error || "invalid-response"));
            return;
        }
        root.url = String(result.url || "");
        root.entities = Array.isArray(result.entities) ? result.entities : [];
        root.tokenConfigured = result.tokenConfigured === true;
        root.configured = result.configured === true;
        root.lastError = "";
        if (saved) {
            root.configurationSaved();
            root.refreshHomeAssistant();
        }
    }

    function refresh(): void {
        if (!statusProc.running && !writeProc.running)
            statusProc.running = true;
    }

    // An empty token means "keep the stored token". Clearing is an explicit
    // separate action so editing the URL can never erase a secret by accident.
    function save(url: string, entitiesText: string, token: string): bool {
        if (root.busy)
            return false;
        const payload = { url: url, entities: entitiesText };
        if (token.trim() !== "")
            payload.token = token;
        return root.startWrite(payload);
    }

    function clearToken(): bool {
        if (root.busy || !root.tokenConfigured)
            return false;
        return root.startWrite({ token: "" });
    }

    function startWrite(payload: var): bool {
        root.lastError = "";
        root.pendingPayload = JSON.stringify(payload);
        writeProc.running = true;
        return true;
    }

    Process {
        id: statusProc
        command: [root.helperPath, "status"]
        stdout: StdioCollector {
            onStreamFinished: root.applyResult(this.text, false)
        }
        onExited: (code, status) => {
            if (code !== 0 && root.lastError === "")
                root.lastError = "Home Assistant configuration could not be loaded.";
        }
    }

    Process {
        id: writeProc
        command: [root.helperPath, "write"]
        stdinEnabled: true
        stdout: StdioCollector {
            onStreamFinished: root.applyResult(this.text, true)
        }
        onStarted: {
            writeProc.write(root.pendingPayload + "\n");
            root.pendingPayload = "";
        }
        onExited: (code, status) => {
            root.pendingPayload = "";
            if (code !== 0 && root.lastError === "")
                root.lastError = "Home Assistant configuration could not be saved.";
        }
    }

    Component.onCompleted: root.refresh()
}
