/**
 * App-wide sync queue singleton.
 *
 * One SyncQueue per browser tab, initialised lazily on first access. Pass
 * mutations through `enqueue*` helpers below so they're persisted to IDB
 * BEFORE the network call — that way a lost connection doesn't lose work.
 *
 * On window 'online' we flush the queue. Before returning from a mutation
 * helper we also update the pendingOpsCount store so the banner can react.
 */
import { browser } from '$app/environment';
import { getSupabase } from '$lib/supabase';
import { pendingOpsCount } from '$lib/stores/syncStatus';
import { SyncQueue, attachOnlineFlush, type NewOp } from './queue';

let singleton: SyncQueue | null = null;
let detachOnline: (() => void) | null = null;

export function getQueue(): SyncQueue {
	if (!singleton) {
		// The SyncQueue's constructor parameter is structurally compatible with
		// supabase-js's query builder surface. Cast through unknown rather than
		// introducing a shared interface for a single integration point.
		singleton = new SyncQueue(getSupabase() as unknown as ConstructorParameters<typeof SyncQueue>[0]);
		if (browser) {
			detachOnline = attachOnlineFlush(singleton);
			// Refresh the pendingOpsCount whenever we flush.
			const originalFlush = singleton.flush.bind(singleton);
			singleton.flush = async (...args: Parameters<typeof originalFlush>) => {
				const r = await originalFlush(...args);
				await refreshPending();
				return r;
			};
		}
	}
	return singleton;
}

export async function refreshPending(): Promise<void> {
	if (!singleton) return;
	const ops = await singleton.pending();
	pendingOpsCount.set(ops.length);
}

/**
 * Enqueue-then-send convenience wrapper. Returns immediately on offline (op
 * stays queued) and propagates the online-success/failure otherwise.
 */
export async function enqueueOp(op: NewOp): Promise<void> {
	const q = getQueue();
	await q.enqueue(op);
	await refreshPending();
}

/**
 * Called at app startup (from a top-level layout) to hydrate the
 * pendingOpsCount store from IDB so the banner reflects leftover ops.
 */
export async function hydrateSyncState(): Promise<void> {
	if (!browser) return;
	const q = getQueue();
	const ops = await q.pending();
	pendingOpsCount.set(ops.length);
	// If we booted up with pending ops AND we're online, flush immediately.
	if (ops.length > 0 && navigator.onLine) {
		await q.flush();
	}
}

// For tests that want to tear down the singleton.
export function __resetSyncForTests(): void {
	detachOnline?.();
	singleton = null;
	detachOnline = null;
}
