/**
 * Supporter (subscription) state.
 *
 * The user pastes an access key once; it is stored locally and re-verified
 * against the self-hosted verify API on load. The last good result is cached so
 * gated content shows instantly on reload (and keeps working briefly offline)
 * without waiting for the network round-trip.
 *
 * This gate is a convenience/honor-system gate: the frontend is open source and
 * static, so it can be bypassed. Keeping the subscriber list server-side (in the
 * verify API's repo) is what makes it meaningful in practice.
 */
import { derived, get, writable } from 'svelte/store';

import { persisted } from 'svelte-persisted-store';

import * as m from '$lib/paraglide/messages';

import { SUPPORTER_API_BASE, SUPPORTER_ENABLED } from './config';

/** The supporter's access key, e.g. "DRZ-7Q2K-9F4M-XW3P". Empty when signed out. */
export const storedLicenseKey = persisted<string>('license_key', '');

export interface SupporterCache {
	valid: boolean;
	tier?: string;
	/** ISO date the subscription lapses, or null for a lifetime key. */
	expires?: string | null;
	/** epoch ms of the last verify call */
	checkedAt: number;
}

const CACHE_KEY = 'supporter_cache_v1';
const LEGACY_CACHE_KEY = 'premium_cache_v1';

/**
 * Carries the pre-rename cache over on first load, so supporters who already
 * verified aren't shown a locked page while the key re-verifies.
 */
function readLegacyCache(): SupporterCache | null {
	if (typeof localStorage === 'undefined') return null;
	try {
		if (localStorage.getItem(CACHE_KEY)) return null;
		const legacy = localStorage.getItem(LEGACY_CACHE_KEY);
		if (!legacy) return null;
		localStorage.removeItem(LEGACY_CACHE_KEY);
		return JSON.parse(legacy) as SupporterCache;
	} catch {
		return null;
	}
}

/** Last verify result, persisted so the UI doesn't flash "locked" on reload. */
export const storedSupporterCache = persisted<SupporterCache | null>(CACHE_KEY, readLegacyCache());

export type SupporterStatus = 'idle' | 'checking' | 'valid' | 'invalid' | 'error';

export interface SupporterState {
	status: SupporterStatus;
	tier?: string;
	expires?: string | null;
	error?: string;
}

/** Live verification state for the current session. */
export const supporterState = writable<SupporterState>({ status: 'idle' });

function notExpired(expires: string | null | undefined): boolean {
	if (!expires) return true; // lifetime key
	const t = Date.parse(expires);
	return Number.isFinite(t) && t > Date.now();
}

/**
 * Whether supporter content should be shown. A live "valid"/"invalid" result
 * wins; otherwise we fall back to the cached result (so a reload or a brief
 * network blip doesn't lock a paying user out).
 *
 * While supporter features are parked (`SUPPORTER_ENABLED === false`) this is always
 * true, so every gated page renders unlocked for everyone.
 */
export const isSupporter = derived(
	[supporterState, storedSupporterCache],
	([$state, $cache]): boolean => {
		if (!SUPPORTER_ENABLED) return true;
		if ($state.status === 'valid') return true;
		if ($state.status === 'invalid') return false;
		return !!($cache && $cache.valid && notExpired($cache.expires));
	}
);

export interface VerifyResult {
	valid: boolean;
	tier?: string;
	expires?: string | null;
	error?: string;
}

/**
 * Verify a key against the API. On success the key is persisted and the cache
 * updated. On an invalid key the stored key is left untouched (so an expired
 * subscription can still show a "renew" state) but the cache is marked invalid.
 */
export async function verifyKey(key: string): Promise<VerifyResult> {
	// Parked: never touch the verify API (and report success, since everything
	// is unlocked anyway).
	if (!SUPPORTER_ENABLED) return { valid: true, expires: null };

	const trimmed = key.trim();
	if (!trimmed) {
		supporterState.set({ status: 'invalid' });
		return { valid: false, error: m.supporter_enter_key() };
	}

	supporterState.set({ status: 'checking' });
	try {
		const res = await fetch(`${SUPPORTER_API_BASE}/verify?key=${encodeURIComponent(trimmed)}`, {
			headers: { accept: 'application/json' }
		});
		const data = (await res.json()) as VerifyResult;

		if (res.ok && data.valid) {
			storedLicenseKey.set(trimmed);
			storedSupporterCache.set({
				valid: true,
				tier: data.tier,
				expires: data.expires ?? null,
				checkedAt: Date.now()
			});
			supporterState.set({ status: 'valid', tier: data.tier, expires: data.expires ?? null });
			return { valid: true, tier: data.tier, expires: data.expires ?? null };
		}

		storedSupporterCache.set({ valid: false, checkedAt: Date.now() });
		supporterState.set({ status: 'invalid' });
		return { valid: false };
	} catch (err) {
		const message = err instanceof Error ? err.message : String(err);
		supporterState.set({ status: 'error', error: message });
		return { valid: false, error: message };
	}
}

/** Re-verify the stored key (call on app / gated-page load). No-op if signed out. */
export async function refreshSupporter(): Promise<void> {
	if (!SUPPORTER_ENABLED) return;

	const key = get(storedLicenseKey);
	if (!key) {
		supporterState.set({ status: 'idle' });
		return;
	}
	await verifyKey(key);
}

/** Forget the key and supporter state ("sign out"). */
export function clearLicense(): void {
	storedLicenseKey.set('');
	storedSupporterCache.set(null);
	supporterState.set({ status: 'idle' });
}
