"use strict";
/**
 * WorkflowDataWatcher — observe `<project>/.ccweb/workflow_data.json`
 * for `task_progress[taskIndex].finish === true` signals.
 *
 * Replaces (and inlines a more focused version of) the inline polling
 * loop in backend/src/flows/runner.ts:waitForTaskFinish. The track
 * subsystem needs the same primitive but decoupled from FlowRunner.
 *
 * Strategy:
 *   - fs.watch with 50ms debounce
 *   - on each event, read file + parse JSON
 *   - for any pending waiter whose taskIndex.finish is true → resolve
 *   - AbortSignal cancels the wait (returns 'cancelled')
 *   - timeoutMs caps the wait (returns 'timeout')
 *
 * The watcher does NOT write to workflow_data.json. Only the LLM (via
 * the PTY process the adapter injects into) is expected to write.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWorkflowDataWatcher = createWorkflowDataWatcher;
const fs = __importStar(require("fs"));
const fsp = __importStar(require("fs/promises"));
const DEBOUNCE_MS = 50;
function createWorkflowDataWatcher(workflowDataPath) {
    let watcher = null;
    let debounceTimer = null;
    const waiters = [];
    async function readSafe() {
        try {
            const text = await fsp.readFile(workflowDataPath, 'utf8');
            return JSON.parse(text);
        }
        catch {
            return null;
        }
    }
    async function checkAll() {
        const data = await readSafe();
        if (!data)
            return;
        // Snapshot waiters list so resolving doesn't mutate during iteration
        const pending = waiters.slice();
        for (const w of pending) {
            const entry = data.task_progress?.[w.taskIndex];
            if (entry && entry.finish === true) {
                finalize(w, {
                    kind: 'ok',
                    finishedAt: entry.finishedAt ?? Date.now(),
                    data,
                });
            }
        }
    }
    function finalize(w, outcome) {
        const idx = waiters.indexOf(w);
        if (idx >= 0)
            waiters.splice(idx, 1);
        clearTimeout(w.timer);
        if (w.signal && w.signalHandler) {
            w.signal.removeEventListener('abort', w.signalHandler);
        }
        w.resolve(outcome);
    }
    function scheduleCheck() {
        if (debounceTimer)
            return;
        debounceTimer = setTimeout(() => {
            debounceTimer = null;
            void checkAll();
        }, DEBOUNCE_MS);
    }
    return {
        start() {
            if (watcher)
                return;
            try {
                watcher = fs.watch(workflowDataPath, { persistent: false }, () => {
                    scheduleCheck();
                });
            }
            catch {
                // File may not exist yet; we'll still attempt to poll on-demand
                // when a waiter is added (see waitForFinish below).
            }
        },
        stop() {
            if (watcher) {
                watcher.close();
                watcher = null;
            }
            if (debounceTimer) {
                clearTimeout(debounceTimer);
                debounceTimer = null;
            }
            while (waiters.length > 0) {
                const w = waiters[0];
                finalize(w, { kind: 'cancelled' });
            }
        },
        waitForFinish(taskIndex, timeoutMs, signal) {
            return new Promise((resolve) => {
                const waiter = {
                    taskIndex,
                    resolve,
                    timer: setTimeout(() => finalize(waiter, { kind: 'timeout' }), timeoutMs),
                    signal,
                };
                if (signal) {
                    if (signal.aborted) {
                        // already aborted before we even started
                        resolve({ kind: 'cancelled' });
                        clearTimeout(waiter.timer);
                        return;
                    }
                    waiter.signalHandler = () => finalize(waiter, { kind: 'cancelled' });
                    signal.addEventListener('abort', waiter.signalHandler, { once: true });
                }
                waiters.push(waiter);
                // Eager check in case the signal landed before the watcher started
                void checkAll();
            });
        },
        async read() {
            const data = await readSafe();
            if (!data) {
                throw new Error(`workflow_data.json not readable: ${workflowDataPath}`);
            }
            return data;
        },
    };
}
//# sourceMappingURL=workflow-data-watcher.js.map