import { writable, get } from 'svelte/store';
import { generateId } from '$lib/utils/id';

export interface UndoableAction {
	id: string;
	label: string;
	restore: () => void | Promise<void>;
	commit: () => void | Promise<void>;
	/** ms remaining before commit fires automatically */
	timeoutId: ReturnType<typeof setTimeout>;
}

/**
 * Each pending action lives 4 s. Either:
 *  - `undo(id)` is called → `restore` runs, timeout cleared
 *  - the 4-s timer expires → `commit` runs (server-side effect goes through)
 *
 * The store only exposes the minimum the UI needs — id + label — so consumers
 * can't accidentally hold onto stale closures.
 */
export const undoQueue = writable<Array<Pick<UndoableAction, 'id' | 'label'>>>([]);

const actions = new Map<string, UndoableAction>();
export const UNDO_TTL_MS = 4_000;

export function scheduleUndoable(params: {
	label: string;
	restore: UndoableAction['restore'];
	commit: UndoableAction['commit'];
	ttlMs?: number;
}): string {
	const id = generateId();

	const ttl = params.ttlMs ?? UNDO_TTL_MS;

	const timeoutId = setTimeout(() => {
		void commit(id);
	}, ttl);

	actions.set(id, {
		id,
		label: params.label,
		restore: params.restore,
		commit: params.commit,
		timeoutId
	});
	undoQueue.update((q) => [...q, { id, label: params.label }]);
	return id;
}

export async function undo(id: string): Promise<void> {
	const a = actions.get(id);
	if (!a) return;
	clearTimeout(a.timeoutId);
	actions.delete(id);
	undoQueue.update((q) => q.filter((x) => x.id !== id));
	await a.restore();
}

async function commit(id: string): Promise<void> {
	const a = actions.get(id);
	if (!a) return;
	actions.delete(id);
	undoQueue.update((q) => q.filter((x) => x.id !== id));
	await a.commit();
}

/** Test helper — flushes all pending undo actions by firing their commits. */
export async function __flushUndoQueueForTests(): Promise<void> {
	const ids = [...actions.keys()];
	for (const id of ids) {
		const a = actions.get(id);
		if (a) clearTimeout(a.timeoutId);
		// eslint-disable-next-line no-await-in-loop
		await commit(id);
	}
}

/** Test helper — number of pending actions (for assertions). */
export function __pendingUndoCount(): number {
	return actions.size;
}
