const { spawn } = require("child_process");
const { EventEmitter } = require("events");

/**
 * Watches a fixed set of GNOME gsettings keys via `gsettings monitor`
 * and emits a "change" event whenever one of them changes.
 *
 * Event payload: { schema: string, key: string, value: string }
 *   - `value` is the raw gsettings string (still quoted for strings,
 *     e.g. "'Inter Bold 11'"). Consumers may strip the surrounding quotes.
 *
 * The class is intentionally tolerant: if `gsettings` is not available
 * (non-GNOME systems, missing binary), watchers fail silently and the
 * rest of the app keeps working with whatever defaults it had.
 */
class GsettingsMonitor extends EventEmitter {
    /**
     * @param {Array<{schema: string, key: string}>} watches
     */
    constructor(watches = []) {
        super();
        this.watches = watches;
        this._procs = [];
        this._started = false;
    }

    start() {
        if (this._started) return;
        this._started = true;

        for (const { schema, key } of this.watches) {
            this._spawnWatcher(schema, key);
        }
    }

    _spawnWatcher(schema, key) {
        let proc;
        try {
            proc = spawn("gsettings", ["monitor", schema, key], {
                stdio: ["ignore", "pipe", "pipe"],
            });
        } catch (err) {
            console.warn(
                `GsettingsMonitor: failed to spawn monitor for ${schema} ${key}:`,
                err.message,
            );
            return;
        }

        proc.on("error", (err) => {
            console.warn(
                `GsettingsMonitor: monitor for ${schema} ${key} errored:`,
                err.message,
            );
        });

        let buffer = "";
        proc.stdout.on("data", (chunk) => {
            buffer += chunk.toString("utf8");

            let newlineIndex;
            while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
                const line = buffer.slice(0, newlineIndex).trim();
                buffer = buffer.slice(newlineIndex + 1);
                if (!line) continue;

                // gsettings monitor prints lines like:
                //   font-name: 'Cantarell 12'
                //   button-layout: 'appmenu:minimize,maximize,close'
                const colonIdx = line.indexOf(":");
                if (colonIdx === -1) continue;

                const lineKey = line.slice(0, colonIdx).trim();
                const value = line.slice(colonIdx + 1).trim();

                if (lineKey !== key) continue;

                this.emit("change", { schema, key, value });
            }
        });

        this._procs.push(proc);
    }

    stop() {
        for (const proc of this._procs) {
            try {
                proc.kill();
            } catch (_) {
                // best effort
            }
        }
        this._procs = [];
        this._started = false;
    }
}

/**
 * Strip a single layer of surrounding single quotes from a gsettings
 * string value (e.g. "'Inter Bold 11'" -> "Inter Bold 11").
 */
function unquoteGsettingsString(value) {
    if (
        typeof value === "string" &&
        value.length >= 2 &&
        value.startsWith("'") &&
        value.endsWith("'")
    ) {
        return value.slice(1, -1);
    }
    return value;
}

module.exports = { GsettingsMonitor, unquoteGsettingsString };
