import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { resolveFridayNextConfig } from "../config.js";
import { getHostOpenClawConfigSnapshot } from "../host-config.js";
import { getFridayNextRuntime } from "../runtime.js";
/** Test-only override for queue base directory. */
let testQueueBaseDir = null;
export function setOfflineQueueBaseDirForTest(dir) {
    testQueueBaseDir = dir;
}
export function resolveFridayNextEventsQueueDir() {
    if (testQueueBaseDir)
        return testQueueBaseDir;
    try {
        const cfg = resolveFridayNextConfig(getHostOpenClawConfigSnapshot(getFridayNextRuntime().config));
        return path.join(path.dirname(cfg.historyDir), "events-queue");
    }
    catch {
        return path.join(os.homedir(), ".openclaw", "friday-next", "events-queue");
    }
}
/**
 * Per-device JSONL persistence for SSE replay.
 * `overrideBaseDir` is for tests; production uses `resolveFridayNextEventsQueueDir()`.
 */
export class FridaySseOfflineQueue {
    overrideBaseDir;
    constructor(overrideBaseDir = null) {
        this.overrideBaseDir = overrideBaseDir;
    }
    baseDir() {
        if (this.overrideBaseDir)
            return this.overrideBaseDir;
        return resolveFridayNextEventsQueueDir();
    }
    devicePath(deviceId) {
        const key = deviceId.trim().toUpperCase();
        return path.join(this.baseDir(), `${key}.jsonl`);
    }
    ensureDir() {
        fs.mkdirSync(this.baseDir(), { recursive: true });
    }
    /** Highest id in file (full scan; ok for bounded backlog). */
    scanMaxId(deviceId) {
        const file = this.devicePath(deviceId);
        if (!fs.existsSync(file))
            return 0;
        let max = 0;
        const content = fs.readFileSync(file, "utf8");
        for (const line of content.split("\n")) {
            if (!line.trim())
                continue;
            try {
                const o = JSON.parse(line);
                if (typeof o.id === "number" && o.id > max)
                    max = o.id;
            }
            catch {
                /* skip corrupt line */
            }
        }
        return max;
    }
    latestId(deviceId) {
        return this.scanMaxId(deviceId.trim().toUpperCase());
    }
    append(deviceId, id, event, data, backlogLimit) {
        if (event === "connected")
            return;
        this.ensureDir();
        const file = this.devicePath(deviceId);
        const line = JSON.stringify({ id, event, data }) + "\n";
        fs.appendFileSync(file, line, "utf8");
        if (backlogLimit > 0) {
            this.truncateKeepLastN(deviceId, backlogLimit);
        }
    }
    readAfter(deviceId, afterId) {
        const file = this.devicePath(deviceId);
        if (!fs.existsSync(file))
            return [];
        const out = [];
        const content = fs.readFileSync(file, "utf8");
        for (const line of content.split("\n")) {
            if (!line.trim())
                continue;
            try {
                const o = JSON.parse(line);
                if (typeof o.id === "number" &&
                    o.id > afterId &&
                    typeof o.event === "string" &&
                    o.data &&
                    typeof o.data === "object" &&
                    !Array.isArray(o.data)) {
                    out.push(o);
                }
            }
            catch {
                /* skip */
            }
        }
        out.sort((a, b) => a.id - b.id);
        return out;
    }
    truncateKeepLastN(deviceId, keep) {
        if (keep <= 0)
            return;
        const file = this.devicePath(deviceId);
        if (!fs.existsSync(file))
            return;
        const all = [];
        const content = fs.readFileSync(file, "utf8");
        for (const line of content.split("\n")) {
            if (!line.trim())
                continue;
            try {
                const o = JSON.parse(line);
                if (typeof o.id === "number" && typeof o.event === "string" && o.data && typeof o.data === "object") {
                    all.push(o);
                }
            }
            catch {
                /* skip */
            }
        }
        if (all.length <= keep)
            return;
        const slice = all.slice(-keep);
        fs.writeFileSync(file, slice.map((e) => JSON.stringify(e) + "\n").join(""), "utf8");
    }
}
/** Shared queue: base directory follows `setOfflineQueueBaseDirForTest` / config. */
export const fridaySseOfflineQueue = new FridaySseOfflineQueue(null);
