"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TodoWatcher = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const paths_1 = require("../server/paths");
const DEBOUNCE_MS = 300;
const POLL_INTERVAL_MS = 1000;
/**
 * Watches both ~/.claude/todos/<sessionId>-agent-*.json (TodoWrite) and
 * ~/.claude/tasks/<sessionId>/*.json (TaskCreate/TaskUpdate) for changes,
 * normalizes them to TodoItem[], and calls onUpdate when the list changes.
 */
class TodoWatcher {
    cliSessionId;
    onUpdate;
    watchers = [];
    polling = false;
    timer = null;
    lastJson = "";
    stopped = false;
    todosDir;
    tasksDir;
    constructor(cliSessionId, onUpdate) {
        this.cliSessionId = cliSessionId;
        this.onUpdate = onUpdate;
        const base = (0, paths_1.getClaudeDir)();
        this.todosDir = (0, node_path_1.join)(base, "todos");
        this.tasksDir = (0, node_path_1.join)(base, "tasks", cliSessionId);
    }
    start() {
        this.watchDir(this.todosDir);
        this.watchDir(this.tasksDir);
        this.reload();
    }
    stop() {
        this.stopped = true;
        if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
        for (const w of this.watchers) {
            w.close();
        }
        this.watchers = [];
        if (this.polling) {
            try {
                (0, node_fs_1.unwatchFile)(this.todosDir);
            }
            catch {
                /* noop */
            }
            try {
                (0, node_fs_1.unwatchFile)(this.tasksDir);
            }
            catch {
                /* noop */
            }
            this.polling = false;
        }
    }
    watchDir(dir) {
        if (!(0, node_fs_1.existsSync)(dir)) {
            this.pollDir(dir);
            return;
        }
        try {
            const w = (0, node_fs_1.watch)(dir, () => this.scheduleReload());
            this.watchers.push(w);
        }
        catch {
            this.pollDir(dir);
        }
    }
    pollDir(dir) {
        this.polling = true;
        (0, node_fs_1.watchFile)(dir, { interval: POLL_INTERVAL_MS }, () => {
            if (this.stopped)
                return;
            if ((0, node_fs_1.existsSync)(dir)) {
                (0, node_fs_1.unwatchFile)(dir);
                try {
                    const w = (0, node_fs_1.watch)(dir, () => this.scheduleReload());
                    this.watchers.push(w);
                }
                catch {
                    /* stay on poll */
                }
            }
            this.scheduleReload();
        });
    }
    scheduleReload() {
        if (this.stopped)
            return;
        if (this.timer)
            clearTimeout(this.timer);
        this.timer = setTimeout(() => this.reload(), DEBOUNCE_MS);
    }
    readOnce() {
        const todos = this.readTodos();
        const tasks = this.readTasks();
        return todos.length > 0 ? todos : tasks;
    }
    reload() {
        const todos = this.readTodos();
        const tasks = this.readTasks();
        const merged = todos.length > 0 ? todos : tasks;
        if (merged.length === 0 && this.lastJson === "")
            return;
        const json = JSON.stringify(merged);
        if (json !== this.lastJson) {
            this.lastJson = json;
            this.onUpdate(merged);
        }
    }
    readTodos() {
        try {
            if (!(0, node_fs_1.existsSync)(this.todosDir))
                return [];
            const prefix = `${this.cliSessionId}-agent-`;
            const files = (0, node_fs_1.readdirSync)(this.todosDir).filter((f) => f.startsWith(prefix) && f.endsWith(".json"));
            if (files.length === 0)
                return [];
            const latest = files.sort().pop();
            const raw = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(this.todosDir, latest), "utf-8"));
            if (!Array.isArray(raw))
                return [];
            return raw
                .filter((t) => t.content && t.status)
                .map((t) => ({
                content: t.content,
                status: t.status,
                activeForm: t.activeForm || undefined,
            }));
        }
        catch {
            return [];
        }
    }
    readTasks() {
        try {
            if (!(0, node_fs_1.existsSync)(this.tasksDir))
                return [];
            const files = (0, node_fs_1.readdirSync)(this.tasksDir).filter((f) => f.endsWith(".json"));
            if (files.length === 0)
                return [];
            const tasks = [];
            for (const file of files) {
                try {
                    const raw = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(this.tasksDir, file), "utf-8"));
                    if (raw.status === "deleted")
                        continue;
                    tasks.push({
                        content: raw.subject || raw.description || "",
                        status: mapTaskStatus(raw.status),
                        activeForm: raw.activeForm || undefined,
                    });
                }
                catch {
                    // skip malformed file
                }
            }
            return tasks;
        }
        catch {
            return [];
        }
    }
}
exports.TodoWatcher = TodoWatcher;
function mapTaskStatus(status) {
    if (status === "in_progress")
        return "in_progress";
    if (status === "completed")
        return "completed";
    return "pending";
}
