import { browser } from "$app/environment";
import { asset } from "$app/paths";
import { getClientDb } from "$lib/db/client-db";

/**
 * Version-keyed seeding for the client-side SQLocal database.
 *
 * Your shipped snapshot lives at:
 * - `static/data/amq.sqlite` -> `/data/amq.sqlite`
 *
 * SQLocal stores its own managed DB file (named in `client-db.ts`), and we
 * overwrite its contents from the shipped snapshot when needed.
 *
 * This is intended for READ-ONLY browsing. Bump the version when you ship a new
 * snapshot so clients refresh.
 */
export const AMQ_DB_SEED_VERSION = 3;

const SEED_ASSET_PATH = "/data/amq.sqlite";
const seededStorageKey = (version: number) => `amq.sqlocal.seeded.v${version}`;

let seedPromise: Promise<void> | null = null;

function ensureBrowser() {
	if (!browser) {
		throw new Error("Client DB seeding can only run in the browser.");
	}
}

/**
 * Ensure the client DB has been seeded for the given version.
 *
 * - Uses `localStorage` to skip reseeding once done.
 * - Uses a shared promise to avoid races across multiple callers.
 * - Uses streaming (ReadableStream) when available for large DB files.
 *
 * @param opts.version Bump to force users to refresh from the shipped snapshot
 * @param opts.force If true, reseed even if already seeded for this version
 * @param opts.fetch Optional fetch implementation (prefer the one from load)
 */
export async function ensureSeeded(
	opts: { version?: number; force?: boolean; fetch?: typeof fetch } = {},
): Promise<void> {
	ensureBrowser();

	const version = opts.version ?? AMQ_DB_SEED_VERSION;
	const key = seededStorageKey(version);
	const fetcher = opts.fetch ?? fetch;

	if (!opts.force && localStorage.getItem(key) === "1") return;

	// Serialize seeding work so multiple callers don't overwrite concurrently.
	if (seedPromise) return seedPromise;

	seedPromise = (async () => {
		// Re-check inside the serialized section in case another caller finished first.
		if (!opts.force && localStorage.getItem(key) === "1") return;

		const url = asset(SEED_ASSET_PATH);

		const res = await fetcher(url, { cache: "no-cache" });
		if (!res.ok) {
			throw new Error(
				`Failed to fetch seed DB from ${url}: ${res.status} ${res.statusText}`,
			);
		}

		// Prefer streaming when possible.
		const { overwriteDatabaseFile } = getClientDb();

		if (res.body) {
			await overwriteDatabaseFile(res.body);
		} else {
			const blob = await res.blob();
			await overwriteDatabaseFile(blob);
		}

		localStorage.setItem(key, "1");
	})();

	try {
		await seedPromise;
	} finally {
		// If seeding failed, allow future attempts (and let the error surface).
		if (localStorage.getItem(key) !== "1") {
			seedPromise = null;
		}
	}
}

/**
 * Utility for debugging / support: clears the seeded marker for a given version.
 * This does NOT delete SQLocal's stored DB file; it only forces reseed on next call.
 */
export function clearSeedMarker(version = AMQ_DB_SEED_VERSION) {
	ensureBrowser();
	localStorage.removeItem(seededStorageKey(version));
}
