// LobbyDataStore is the session-wide cache for the four lobby panels
// (active-past / recruitment / invitations / private-games). It owns the
// GalaxyClient instance used by lobby HTTP commands, the result of the
// `lobby.*.list` fan-out, and the loading / error flags every panel
// reads. Sub-screens that need to mutate (submit application, redeem
// invite) go through the store so the optimistic state stays consistent
// across navigations.
//
// The store is built around F8-04b's split of the old single
// `lobby-screen.svelte` into per-panel screens — the prior design fetched
// everything on every panel mount, and refetching on each navigation
// flash-cleared the UI. A singleton with $state runes keeps the four
// lists alive while the user moves between subpages.
//
// `clear()` resets the store on signOut; the matching plumbing lives in
// `session-store.svelte.ts::signOut`.

import { createGatewayClient } from "../api/connect";
import { GalaxyClient } from "../api/galaxy-client";
import {
	LobbyError,
	listMyApplications,
	listMyGames,
	listMyInvites,
	listPublicGames,
	type ApplicationSummary,
	type GameSummary,
	type InviteSummary,
} from "../api/lobby";
import { gatewayRpcBaseUrl, GATEWAY_RESPONSE_PUBLIC_KEY } from "./env";
import { i18n, type TranslationKey } from "./i18n/index.svelte";
import { loadCore } from "../platform/core/index";
import { session } from "./session-store.svelte";

async function sha256(payload: Uint8Array): Promise<Uint8Array> {
	const digest = await crypto.subtle.digest("SHA-256", payload as BufferSource);
	return new Uint8Array(digest);
}

export function describeLobbyError(err: unknown): string {
	if (err instanceof LobbyError) {
		const key = `lobby.error.${err.code}` as TranslationKey;
		const translated = i18n.t(key);
		if (translated !== key) {
			return translated;
		}
		return i18n.t("lobby.error.unknown", { message: err.message });
	}
	return err instanceof Error ? err.message : "request failed";
}

class LobbyDataStore {
	myGames = $state<GameSummary[]>([]);
	invitations = $state<InviteSummary[]>([]);
	applications = $state<ApplicationSummary[]>([]);
	publicGames = $state<GameSummary[]>([]);
	loading = $state(true);
	error: string | null = $state(null);
	configError: string | null = $state(null);

	#client: GalaxyClient | null = null;
	#bootstrap: Promise<GalaxyClient | null> | null = null;
	#refresh: Promise<void> | null = null;

	get client(): GalaxyClient | null {
		return this.#client;
	}

	// ensure resolves to the cached GalaxyClient, building one on first
	// call and triggering the initial `lobby.*.list` fan-out. Concurrent
	// callers from sibling screens share the same in-flight bootstrap.
	ensure(): Promise<GalaxyClient | null> {
		if (this.#client !== null) {
			return Promise.resolve(this.#client);
		}
		if (this.#bootstrap !== null) {
			return this.#bootstrap;
		}
		this.#bootstrap = this.#bootstrapClient();
		return this.#bootstrap;
	}

	async #bootstrapClient(): Promise<GalaxyClient | null> {
		try {
			if (
				session.keypair === null ||
				session.deviceSessionId === null ||
				GATEWAY_RESPONSE_PUBLIC_KEY.length === 0
			) {
				this.loading = false;
				if (GATEWAY_RESPONSE_PUBLIC_KEY.length === 0) {
					this.configError = "VITE_GATEWAY_RESPONSE_PUBLIC_KEY is not configured";
				}
				return null;
			}
			const keypair = session.keypair;
			const core = await loadCore();
			this.#client = new GalaxyClient({
				core,
				edge: createGatewayClient(gatewayRpcBaseUrl()),
				signer: (canonical) => keypair.sign(canonical),
				sha256,
				deviceSessionId: session.deviceSessionId,
				gatewayResponsePublicKey: GATEWAY_RESPONSE_PUBLIC_KEY,
			});
			await this.refresh();
			return this.#client;
		} catch (err) {
			this.error = describeLobbyError(err);
			this.loading = false;
			return null;
		} finally {
			this.#bootstrap = null;
		}
	}

	// refresh re-runs the four `lobby.*.list` fan-out. Concurrent callers
	// share the same in-flight promise.
	refresh(): Promise<void> {
		if (this.#client === null) {
			return Promise.resolve();
		}
		if (this.#refresh !== null) {
			return this.#refresh;
		}
		const client = this.#client;
		this.loading = true;
		this.error = null;
		this.#refresh = (async () => {
			try {
				const [games, invites, apps, publicPage] = await Promise.all([
					listMyGames(client),
					listMyInvites(client),
					listMyApplications(client),
					listPublicGames(client),
				]);
				this.myGames = games;
				this.invitations = invites.filter((invite) => invite.status === "pending");
				this.applications = apps;
				this.publicGames = publicPage.items;
			} catch (err) {
				this.error = describeLobbyError(err);
			} finally {
				this.loading = false;
				this.#refresh = null;
			}
		})();
		return this.#refresh;
	}

	prependApplication(app: ApplicationSummary): void {
		this.applications = [app, ...this.applications];
	}

	removeInvitation(inviteId: string): void {
		this.invitations = this.invitations.filter((i) => i.inviteId !== inviteId);
	}

	setMyGames(games: GameSummary[]): void {
		this.myGames = games;
	}

	clear(): void {
		this.#client = null;
		this.#bootstrap = null;
		this.#refresh = null;
		this.myGames = [];
		this.invitations = [];
		this.applications = [];
		this.publicGames = [];
		this.loading = true;
		this.error = null;
		this.configError = null;
	}
}

export const lobbyData = new LobbyDataStore();
