import { writable } from 'svelte/store';
import { api } from './api';
import { setRole } from './role-store';
import { privacyNote } from './privacy-note-store';
import { isBanned } from './ban-store';
import type { MeContextDto } from './types';

/**
 * Live event lock/release state, so the composer can reflect a close/reopen the *instant*
 * it happens instead of a guest discovering the lock via a rejected upload. Seeded from
 * `/me/context` on boot and kept current by the `event-closed`/`event-opened` SSE events
 * (see the root layout, which subscribes once).
 */
export interface EventState {
	uploadsLocked: boolean;
	galleryReleased: boolean;
}

export const eventState = writable<EventState>({ uploadsLocked: false, galleryReleased: false });

// Monotonic sequence bumped by every synchronous state transition (markClosed/markOpened).
// `refreshEventState` captures it before its async fetch and only applies the result if no
// newer transition happened meanwhile — so a slow `/me/context` reflecting a pre-reopen
// snapshot can't clobber a subsequent `event-opened` with stale `locked=true`.
let stateSeq = 0;

/** True when uploads are closed for any reason (event locked or gallery released). */
export function uploadsClosed(s: EventState): boolean {
	return s.uploadsLocked || s.galleryReleased;
}

/** Refresh from the server. Non-fatal on failure — the next SSE event reconciles. */
export async function refreshEventState(): Promise<void> {
	const seq = stateSeq;
	try {
		const ctx = await api.get<MeContextDto>('/me/context');
		// The role is unaffected by the close/reopen race guarded below, so apply it
		// unconditionally — this is one of the refreshes that used to drop it.
		setRole(ctx.role);
		// Same reasoning, same bug shape: these two fields come back in the SAME payload and
		// were simply dropped on the floor here. `privacy_note` mattered most on the join
		// → feed path, where the layout's token-gated boot fetch never runs, so a first-time
		// guest's onboarding never learned the note existed. `is_banned` drives the read-only
		// banner, which has to appear the moment a ban lands, not on the next cold start.
		privacyNote.set(ctx.privacy_note);
		isBanned.set(ctx.is_banned);
		// A close/reopen landed while this was in flight — its result is now authoritative;
		// don't overwrite it with our possibly-stale snapshot.
		if (seq !== stateSeq) return;
		eventState.set({
			uploadsLocked: ctx.uploads_locked,
			galleryReleased: ctx.gallery_released
		});
	} catch {
		// non-fatal
	}
}

/** Apply an `event-closed` SSE event (uploads just locked). */
export function markClosed(): void {
	stateSeq++;
	eventState.update((s) => ({ ...s, uploadsLocked: true }));
}

/** Apply an `event-opened` SSE event (uploads reopened → release also cleared). */
export function markOpened(): void {
	stateSeq++;
	eventState.set({ uploadsLocked: false, galleryReleased: false });
}
