/**
 * Timezone preference store.
 *
 * The user's IANA timezone is a purely client-side preference — it controls how
 * server-supplied ISO timestamps are rendered. A reserved sentinel ('auto')
 * means "use whatever the browser reports right now". Stored in localStorage
 * so it survives page reloads and roams per-browser.
 */

import { writable, derived, type Readable } from 'svelte/store';

const TIMEZONE_KEY = 'dw_timezone';

/** Sentinel meaning "follow the browser's Intl-reported zone on every read". */
export const AUTO_TIMEZONE = 'auto' as const;

export type TimezonePreference = typeof AUTO_TIMEZONE | string;

/** Resolve the browser's current IANA timezone, with a safe UTC fallback. */
export function detectBrowserTimezone(): string {
	try {
		const resolved = Intl.DateTimeFormat().resolvedOptions().timeZone;
		if (resolved) return resolved;
	} catch {
		// ignore and fall through
	}
	return 'UTC';
}

/**
 * Verify an IANA timezone string by asking Intl to format with it. Intl throws
 * `RangeError` for unknown zones — exactly what we want for user-facing hints.
 */
export function isValidTimezone(tz: string): boolean {
	if (!tz) return false;
	try {
		new Intl.DateTimeFormat('en-US', { timeZone: tz }).format(new Date());
		return true;
	} catch {
		return false;
	}
}

function getInitialPreference(): TimezonePreference {
	if (typeof localStorage === 'undefined') return AUTO_TIMEZONE;
	const stored = localStorage.getItem(TIMEZONE_KEY);
	if (!stored) return AUTO_TIMEZONE;
	if (stored === AUTO_TIMEZONE) return AUTO_TIMEZONE;
	return isValidTimezone(stored) ? stored : AUTO_TIMEZONE;
}

/** Raw preference — may be 'auto' or a specific IANA zone. */
export const timezonePreference = writable<TimezonePreference>(getInitialPreference());

timezonePreference.subscribe((value) => {
	if (typeof localStorage !== 'undefined') {
		localStorage.setItem(TIMEZONE_KEY, value);
	}
});

/**
 * Effective timezone — always a concrete IANA string. When preference is
 * 'auto', re-resolves from the browser. Components should consume this store
 * for formatting, never the raw preference.
 */
export const effectiveTimezone: Readable<string> = derived(
	timezonePreference,
	($pref) => ($pref === AUTO_TIMEZONE ? detectBrowserTimezone() : $pref)
);

export function setTimezonePreference(value: TimezonePreference): void {
	if (value !== AUTO_TIMEZONE && !isValidTimezone(value)) return;
	timezonePreference.set(value);
}

/**
 * Curated shortlist shown at the top of the picker. The full catalogue comes
 * from `Intl.supportedValuesOf('timeZone')` when available.
 */
export const COMMON_TIMEZONES: readonly string[] = [
	'UTC',
	'Europe/London',
	'Europe/Berlin',
	'Europe/Moscow',
	'Europe/Minsk',
	'America/New_York',
	'America/Chicago',
	'America/Denver',
	'America/Los_Angeles',
	'America/Sao_Paulo',
	'Asia/Tokyo',
	'Asia/Shanghai',
	'Asia/Singapore',
	'Asia/Dubai',
	'Asia/Kolkata',
	'Australia/Sydney',
	'Pacific/Auckland'
] as const;

/**
 * Full list of IANA zones supported by the browser. Falls back to the curated
 * shortlist on older runtimes that lack `Intl.supportedValuesOf`.
 */
export function listAllTimezones(): string[] {
	const intlAny = Intl as unknown as { supportedValuesOf?: (kind: string) => string[] };
	if (typeof intlAny.supportedValuesOf === 'function') {
		try {
			return intlAny.supportedValuesOf('timeZone');
		} catch {
			// fall through
		}
	}
	return [...COMMON_TIMEZONES];
}

/**
 * Human-readable offset label like "UTC+03:00" for a given zone at a given
 * instant (defaults to now). Used inline in the picker list.
 */
export function formatOffsetLabel(tz: string, at: Date = new Date()): string {
	try {
		const parts = new Intl.DateTimeFormat('en-US', {
			timeZone: tz,
			timeZoneName: 'shortOffset'
		}).formatToParts(at);
		const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '';
		// Intl reports "GMT", "GMT+3", "GMT-05:30" — normalise to "UTC±HH:MM".
		if (!offset || offset === 'GMT') return 'UTC+00:00';
		const m = offset.match(/GMT([+-])(\d{1,2})(?::?(\d{2}))?/);
		if (!m) return offset.replace('GMT', 'UTC');
		const sign = m[1];
		const hours = m[2].padStart(2, '0');
		const minutes = (m[3] ?? '00').padStart(2, '0');
		return `UTC${sign}${hours}:${minutes}`;
	} catch {
		return '';
	}
}
