import { Cid } from '../utils/cid.js';
import { createLevelDatabase, LevelWrapper } from './level-wrapper.js';
/**
 * A simple single-instance implementation of {@link ResumableTaskStore} that works in both browsers and node.js.
 * Leverages LevelDB under the hood.
 */
export class ResumableTaskStoreLevel {
    static taskTimeoutInSeconds = 60;
    db;
    config;
    constructor(config) {
        this.config = {
            // defaults:
            location: 'RESUMABLE-TASK-STORE',
            createLevelDatabase,
            // user-provided overrides:
            ...config,
        };
        this.db = new LevelWrapper({
            location: this.config.location,
            createLevelDatabase: this.config.createLevelDatabase,
            keyEncoding: 'utf8'
        });
    }
    async open() {
        await this.db.open();
    }
    async close() {
        await this.db.close();
    }
    async register(task, timeoutInSeconds) {
        const taskId = await Cid.computeCid(task);
        const managedResumableTask = {
            id: taskId,
            timeout: Date.now() + (timeoutInSeconds * 1000),
            retryCount: 0,
            task,
        };
        await this.db.put(taskId, JSON.stringify(managedResumableTask));
        return managedResumableTask;
    }
    async grab(count) {
        const tasks = [];
        // iterate over the tasks to find unhandled (timed-out) tasks to return to the caller,
        // tasks that are not timed-out are considered in-flight/under processing
        // NOTE: there is an opportunity here to introduce an additional index where we can query by timed-out tasks,
        // but it requires an additional index thus more complexity
        for await (const [_, value] of this.db.iterator()) {
            const task = JSON.parse(value);
            // if the task is timed-out, we can give it to the caller to handle
            if (Date.now() >= task.timeout) {
                // update the task metadata first before adding to list of tasks to return
                task.timeout = Date.now() + (ResumableTaskStoreLevel.taskTimeoutInSeconds * 1000);
                task.retryCount++;
                await this.db.put(task.id, JSON.stringify(task));
                tasks.push(task);
            }
            if (tasks.length >= count) {
                break;
            }
        }
        return tasks;
    }
    async read(taskId) {
        const value = await this.db.get(taskId);
        if (value) {
            const task = JSON.parse(value);
            return task;
        }
        else {
            return undefined;
        }
    }
    async extend(taskId, timeoutInSeconds) {
        const value = await this.db.get(taskId);
        if (value) {
            const task = JSON.parse(value);
            task.timeout = Date.now() + (timeoutInSeconds * 1000);
            await this.db.put(task.id, JSON.stringify(task));
        }
    }
    async delete(taskId) {
        return this.db.delete(taskId);
    }
    /**
     * Deletes everything in the store. Mainly used in tests.
     */
    async clear() {
        await this.db.clear();
    }
}
//# sourceMappingURL=resumable-task-store-level.js.map