import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import lockfile from "proper-lockfile";
import { getAgentDir } from "../../config.js";
export class WebSearchQueue {
    rateLimitMs;
    lockFilePath;
    timeFilePath;
    constructor(options = {}) {
        this.rateLimitMs = options.rateLimitMs ?? 10_000;
        this.lockFilePath = options.lockFilePath ?? join(getAgentDir(), "web-search-queue.lock");
        this.timeFilePath = options.timeFilePath ?? join(getAgentDir(), "web-search-queue.time");
    }
    async enqueue(fn) {
        // Ensure parent directory and lock file exist (proper-lockfile requires the file to exist)
        try {
            const dir = dirname(this.lockFilePath);
            if (!existsSync(dir)) {
                mkdirSync(dir, { recursive: true });
            }
            if (!existsSync(this.lockFilePath)) {
                writeFileSync(this.lockFilePath, "");
            }
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            throw new Error(`Failed to initialize web search queue lock file at ${this.lockFilePath}: ${msg}`);
        }
        let release;
        try {
            release = await lockfile.lock(this.lockFilePath, {
                stale: 60_000,
                retries: { retries: 10, factor: 2, minTimeout: 100, maxTimeout: 10_000, randomize: true },
            });
            // Read last search timestamp
            let lastSearchTime = 0;
            try {
                const raw = readFileSync(this.timeFilePath, "utf-8");
                const data = JSON.parse(raw);
                if (typeof data.lastSearchTime === "number") {
                    lastSearchTime = data.lastSearchTime;
                }
            }
            catch {
                // Missing or malformed — treat as 0
            }
            // Enforce rate limit
            const delayNeeded = Math.max(0, this.rateLimitMs - (Date.now() - lastSearchTime));
            if (delayNeeded > 0) {
                await new Promise((resolve) => setTimeout(resolve, delayNeeded));
            }
            // Execute the search operation
            try {
                return await fn();
            }
            finally {
                // Ensure time file directory exists before writing
                const timeDir = dirname(this.timeFilePath);
                if (!existsSync(timeDir)) {
                    mkdirSync(timeDir, { recursive: true });
                }
                // Update timestamp even on error to prevent retry storms
                try {
                    const timestampData = { lastSearchTime: Date.now() };
                    writeFileSync(this.timeFilePath, JSON.stringify(timestampData));
                }
                catch (tsErr) {
                    // Don't let timestamp write failure mask the original error
                    console.error(`Failed to write search timestamp: ${tsErr}`);
                }
            }
        }
        finally {
            try {
                await release?.();
            }
            catch {
                // Swallow unlock errors
            }
        }
    }
}
//# sourceMappingURL=web-search-queue.js.map