/**
 * TaskManager - Background task lifecycle service.
 *
 * Manages tasks with status tracking, dependency resolution,
 * and SharedStateBus event publishing.
 */
const VALID_TRANSITIONS = {
    pending: ["in_progress", "blocked", "failed"],
    in_progress: ["completed", "failed", "blocked"],
    blocked: ["pending", "in_progress", "failed"],
    completed: [],
    failed: ["pending"],
};
export class TaskManager {
    tasks = new Map();
    bus;
    maxTasks;
    nextId = 1;
    constructor(options) {
        this.maxTasks = options?.maxTasks ?? 100;
    }
    generateId() {
        return `task_${this.nextId++}`;
    }
    setBus(bus) {
        this.bus = bus;
    }
    create(opts) {
        if (this.tasks.size >= this.maxTasks) {
            throw new Error(`Maximum task limit reached (${this.maxTasks})`);
        }
        const now = Date.now();
        const task = {
            id: this.generateId(),
            subject: opts.subject,
            description: opts.description,
            status: "pending",
            owner: opts.owner,
            activeForm: opts.activeForm,
            metadata: opts.metadata,
            blocks: [],
            blockedBy: [],
            createdAt: now,
            updatedAt: now,
        };
        this.tasks.set(task.id, task);
        this.publishChange(task, "created");
        return task;
    }
    get(id) {
        return this.tasks.get(id);
    }
    list(filter) {
        let tasks = Array.from(this.tasks.values());
        if (filter?.status) {
            tasks = tasks.filter((t) => t.status === filter.status);
        }
        if (filter?.owner) {
            tasks = tasks.filter((t) => t.owner === filter.owner);
        }
        return tasks;
    }
    update(id, changes) {
        const task = this.tasks.get(id);
        if (!task)
            throw new Error(`Task ${id} not found`);
        if (changes.status && changes.status !== task.status) {
            const allowed = VALID_TRANSITIONS[task.status];
            if (!allowed.includes(changes.status)) {
                throw new Error(`Invalid transition: ${task.status} -> ${changes.status}`);
            }
            task.status = changes.status;
        }
        if (changes.subject !== undefined)
            task.subject = changes.subject;
        if (changes.description !== undefined)
            task.description = changes.description;
        if (changes.owner !== undefined)
            task.owner = changes.owner;
        if (changes.activeForm !== undefined)
            task.activeForm = changes.activeForm;
        if (changes.metadata !== undefined) {
            task.metadata = { ...task.metadata, ...changes.metadata };
        }
        if (changes.addBlocks) {
            for (const blockId of changes.addBlocks) {
                if (!task.blocks.includes(blockId))
                    task.blocks.push(blockId);
                const blocked = this.tasks.get(blockId);
                if (blocked && !blocked.blockedBy.includes(id)) {
                    blocked.blockedBy.push(id);
                    blocked.updatedAt = Date.now();
                }
            }
        }
        if (changes.addBlockedBy) {
            for (const blockerId of changes.addBlockedBy) {
                if (!task.blockedBy.includes(blockerId))
                    task.blockedBy.push(blockerId);
                const blocker = this.tasks.get(blockerId);
                if (blocker && !blocker.blocks.includes(id)) {
                    blocker.blocks.push(id);
                    blocker.updatedAt = Date.now();
                }
            }
        }
        task.updatedAt = Date.now();
        this.publishChange(task, "updated");
        if (task.status === "completed" || task.status === "failed") {
            this.resolveBlockedTasks(id);
        }
        return task;
    }
    delete(id) {
        const task = this.tasks.get(id);
        if (!task)
            return false;
        for (const blockId of task.blocks) {
            const blocked = this.tasks.get(blockId);
            if (blocked) {
                blocked.blockedBy = blocked.blockedBy.filter((b) => b !== id);
            }
        }
        for (const blockerId of task.blockedBy) {
            const blocker = this.tasks.get(blockerId);
            if (blocker) {
                blocker.blocks = blocker.blocks.filter((b) => b !== id);
            }
        }
        this.tasks.delete(id);
        this.publishChange(task, "deleted");
        return true;
    }
    resolveBlockedTasks(completedId) {
        for (const task of this.tasks.values()) {
            if (task.status === "blocked" && task.blockedBy.includes(completedId)) {
                const allResolved = task.blockedBy.every((bid) => {
                    const blocker = this.tasks.get(bid);
                    return (!blocker ||
                        blocker.status === "completed" ||
                        blocker.status === "failed");
                });
                if (allResolved) {
                    task.status = "pending";
                    task.updatedAt = Date.now();
                    this.publishChange(task, "unblocked");
                }
            }
        }
    }
    publishChange(task, action) {
        this.bus?.publish("state:tasks", "taskManager", {
            action,
            task: { ...task },
        });
    }
    toJSON() {
        return Array.from(this.tasks.values());
    }
    loadFromJSON(tasks) {
        this.tasks.clear();
        let maxNum = 0;
        for (const task of tasks) {
            this.tasks.set(task.id, task);
            const num = Number.parseInt(task.id.replace("task_", ""), 10);
            if (!Number.isNaN(num) && num > maxNum)
                maxNum = num;
        }
        this.nextId = maxNum + 1;
    }
}
