/**
 * Singleton entity caches for all shared entities.
 *
 * Import from here in page components:
 *   import { providersCache, targetsCache } from '$lib/stores/caches.svelte';
 */

import { createEntityCache } from './entity-cache.svelte';
import { api } from '$lib/api';
import type {
	ServiceProvider,
	NotificationTarget,
	Tracker,
	TrackingConfig,
	TemplateConfig,
	TelegramBot,
	EmailBot,
	MatrixBot,
	CommandConfig,
	CommandTemplateConfig,
	CommandTracker,
	Action,
	ReleaseStatus,
} from '$lib/types';

/** Service providers — used by Dashboard, Trackers, Command Trackers, Providers page. */
export const providersCache = createEntityCache<ServiceProvider>('/providers');

/** Notification targets — used by Trackers, Targets page. */
export const targetsCache = createEntityCache<NotificationTarget>('/targets');

/** Notification trackers — used by Dashboard, Trackers page. */
export const notificationTrackersCache = createEntityCache<Tracker>('/notification-trackers');

/** Tracking configs — used by Trackers, Tracking Configs page. */
export const trackingConfigsCache = createEntityCache<TrackingConfig>('/tracking-configs');

/** Template configs — used by Trackers, Template Configs page. */
export const templateConfigsCache = createEntityCache<TemplateConfig>('/template-configs');

/** Telegram bots — used by Targets, Command Trackers, Bots page. */
export const telegramBotsCache = createEntityCache<TelegramBot>('/telegram-bots');

/** Email bots — used by Targets, Bots page. */
export const emailBotsCache = createEntityCache<EmailBot>('/email-bots');

/** Matrix bots — used by Targets, Bots page. */
export const matrixBotsCache = createEntityCache<MatrixBot>('/matrix-bots');

/** Command configs — used by Command Trackers, Command Configs page. */
export const commandConfigsCache = createEntityCache<CommandConfig>('/command-configs');

/** Command template configs — used by Command Configs, Command Template Configs page. */
export const commandTemplateConfigsCache = createEntityCache<CommandTemplateConfig>('/command-template-configs');

/** Command trackers — used by Command Trackers page. */
export const commandTrackersCache = createEntityCache<CommandTracker>('/command-trackers');

/** Actions — used by Actions page. */
export const actionsCache = createEntityCache<Action>('/actions');

export interface SlotDef {
	name: string;
	description: string;
	required?: boolean;
}

export interface CommandDef {
	name: string;
	description?: string;
}

export interface ActionTypeDef {
	key: string;
	name: string;
	description?: string;
	[key: string]: unknown;
}

export interface ProviderCapabilities {
	notification_slots?: SlotDef[];
	command_slots?: SlotDef[];
	commands?: CommandDef[];
	action_types?: ActionTypeDef[];
	[key: string]: unknown;
}

export type CapabilitiesMap = Record<string, ProviderCapabilities>;

/** Provider capabilities — used by Template Configs, Command Configs.
 *  Dedups concurrent fetches so two fast navigations do not double-hit the API. */
export const capabilitiesCache = (() => {
	let data = $state<CapabilitiesMap>({});
	let fetchedAt = $state(0);
	let inflight: Promise<CapabilitiesMap> | null = null;
	const TTL = 60_000;
	return {
		get items() { return data; },
		async fetch(force = false): Promise<CapabilitiesMap> {
			if (!force && Object.keys(data).length > 0 && Date.now() - fetchedAt < TTL) return data;
			if (inflight) return inflight;
			inflight = (async () => {
				try {
					data = await api<CapabilitiesMap>('/providers/capabilities');
					fetchedAt = Date.now();
					return data;
				} finally {
					inflight = null;
				}
			})();
			return inflight;
		},
	};
})();

/** Configured external base URL — used to render absolute webhook URLs.
 *  Available to all authenticated users. Empty string when unset. */
export const externalUrlCache = (() => {
	let data = $state<string>('');
	let fetchedAt = $state(0);
	let inflight: Promise<string> | null = null;
	const TTL = 300_000;
	return {
		get value() { return data; },
		invalidate() { fetchedAt = 0; },
		async fetch(force = false): Promise<string> {
			if (!force && fetchedAt > 0 && Date.now() - fetchedAt < TTL) return data;
			if (inflight) return inflight;
			inflight = (async () => {
				try {
					const res = await api<{ external_url: string }>('/settings/external-url');
					data = (res?.external_url || '').replace(/\/+$/, '');
					fetchedAt = Date.now();
					return data;
				} finally {
					inflight = null;
				}
			})();
			return inflight;
		},
	};
})();

/** Upstream release status — drives the sidebar badge and Settings cassette. */
export const releaseStatusCache = (() => {
	let data = $state<ReleaseStatus | null>(null);
	let fetchedAt = $state(0);
	let inflight: Promise<ReleaseStatus | null> | null = null;
	// 5 min TTL — fresh enough that "Check now" feels instant on revisit,
	// long enough that route changes don't hammer the endpoint.
	const TTL = 300_000;
	return {
		get value() { return data; },
		invalidate() { fetchedAt = 0; },
		clear() {
			data = null;
			fetchedAt = 0;
			inflight = null;
		},
		set(next: ReleaseStatus | null) {
			data = next;
			fetchedAt = Date.now();
		},
		async fetch(force = false): Promise<ReleaseStatus | null> {
			if (!force && fetchedAt > 0 && Date.now() - fetchedAt < TTL) return data;
			if (inflight) return inflight;
			inflight = (async () => {
				try {
					data = await api<ReleaseStatus>('/settings/release');
					fetchedAt = Date.now();
					return data;
				} catch {
					// Swallow — the badge falls back to its default "no status" state.
					return data;
				} finally {
					inflight = null;
				}
			})();
			return inflight;
		},
	};
})();

/** Supported template locales — fetched from app settings. */
export const supportedLocalesCache = (() => {
	let data = $state<string[]>(['en', 'ru']);
	let fetchedAt = $state(0);
	let inflight: Promise<string[]> | null = null;
	const TTL = 300_000;
	return {
		get items() { return data; },
		async fetch(force = false): Promise<string[]> {
			if (!force && fetchedAt > 0 && Date.now() - fetchedAt < TTL) return data;
			if (inflight) return inflight;
			inflight = (async () => {
				try {
					data = await api<string[]>('/settings/locales');
					fetchedAt = Date.now();
					return data;
				} finally {
					inflight = null;
				}
			})();
			return inflight;
		},
	};
})();

/**
 * All caches keyed by entity type — for search palette and crosslink resolution.
 */
export const allCaches = {
	providers: providersCache,
	targets: targetsCache,
	notification_trackers: notificationTrackersCache,
	tracking_configs: trackingConfigsCache,
	template_configs: templateConfigsCache,
	telegram_bots: telegramBotsCache,
	email_bots: emailBotsCache,
	matrix_bots: matrixBotsCache,
	command_configs: commandConfigsCache,
	command_template_configs: commandTemplateConfigsCache,
	command_trackers: commandTrackersCache,
	actions: actionsCache,
} as const;

/**
 * Fetch all caches in parallel. Used by search palette.
 */
export async function fetchAllCaches(): Promise<void> {
	await Promise.all(Object.values(allCaches).map(c => c.fetch()));
}

/**
 * Invalidate all entity caches. Useful on logout.
 *
 * Singleton state caches (release status, external URL, supported locales)
 * live outside `allCaches` because their shape differs from entity caches —
 * we clear them explicitly so a returning user as a different role can't
 * briefly see the previous user's cached payload.
 */
export function clearAllCaches(): void {
	Object.values(allCaches).forEach(c => c.clear());
	releaseStatusCache.clear();
}
