/**
 * Module-scoped resource caches for sidebar list pages.
 *
 * Each cache is a stale-while-revalidate store (see `resourceCache.ts`). Pages
 * read the value via `$store` and call `.refresh()` in `onMount` (and after
 * mutations). Because these instances live at module scope, their data
 * survives navigation, so revisiting a tab renders instantly instead of
 * flashing a skeleton frame.
 *
 * Reset them all on logout so a different user never sees the previous
 * session's cached data.
 */

import * as api from '$lib/api';
import type {
	RedeployTrigger,
	EventTrigger,
	MetricAlertRule,
	LogScanRule,
	LogScanStats,
	SharedSecret,
	AuthSettings,
	AuthUser
} from '$lib/api';
import type {
	Workload,
	Container,
	ContainerView,
	ProxyRoute,
	App,
	StaleContainer,
	Settings,
	Registry,
	BackupInfo,
	DnsRecordView
} from '$lib/types';
import {
	createResourceCache,
	createKeyedResourceCache,
	type ResourceCache,
	type KeyedResourceCache
} from './resourceCache';
import { clearEventsSnapshot } from './eventsSnapshot';

// ── Single-resource caches ──────────────────────────────────────────────
export const workloadsCache = createResourceCache<Workload[]>(
	(signal) => api.listWorkloads(undefined, signal),
	[]
);

export const containersCache = createResourceCache<ContainerView[]>(
	(signal) => api.listContainers({}, signal),
	[]
);

export const proxyRoutesCache = createResourceCache<ProxyRoute[]>(
	(signal) => api.listProxyRoutes(signal),
	[]
);

export const triggersCache = createResourceCache<RedeployTrigger[]>(
	(signal) => api.listTriggers(undefined, signal),
	[]
);

export const eventTriggersCache = createResourceCache<EventTrigger[]>(
	(signal) => api.listEventTriggers(signal),
	[]
);

export const metricAlertRulesCache = createResourceCache<MetricAlertRule[]>(
	() => api.listMetricAlertRules(),
	[]
);

// ── Composite caches (pages that load several resources together) ─────────
export interface LogScanData {
	rules: LogScanRule[];
	stats: LogScanStats | null;
}
export const logScanCache = createResourceCache<LogScanData>(
	async (signal) => {
		const [rules, stats] = await Promise.all([
			api.listLogScanRules(),
			api.getLogScanStats(signal).catch(() => null)
		]);
		return { rules, stats };
	},
	{ rules: [], stats: null }
);

export interface SharedSecretsData {
	secrets: SharedSecret[];
	apps: App[];
}
export const sharedSecretsCache = createResourceCache<SharedSecretsData>(
	async (signal) => {
		// App load failure is non-fatal — app-scoped rows fall back to a
		// truncated id when the name lookup misses.
		const [secrets, apps] = await Promise.all([
			api.listSharedSecrets(),
			api.listApps(signal).catch(() => [] as App[])
		]);
		return { secrets, apps };
	},
	{ secrets: [], apps: [] }
);

export interface DashboardData {
	workloads: Workload[];
	containers: ContainerView[];
	stale: StaleContainer[];
	unusedImagesMB: number;
	unusedImagesCount: number;
	unusedImagesExceeded: boolean;
}
export const dashboardCache = createResourceCache<DashboardData>(
	async (signal) => {
		// Each list falls back to empty so a single slow daemon (e.g. Docker
		// stats) does not blank the whole dashboard.
		const [workloads, containers, stale] = await Promise.all([
			api.listWorkloads(undefined, signal),
			api.listContainers({}, signal).catch(() => [] as ContainerView[]),
			api.fetchStaleContainers(signal).catch(() => [] as StaleContainer[])
		]);
		let unusedImagesMB = 0;
		let unusedImagesCount = 0;
		let unusedImagesExceeded = false;
		try {
			const s = await api.getUnusedImageStats(signal);
			unusedImagesMB = s.total_size_mb;
			unusedImagesCount = s.count;
			unusedImagesExceeded = s.exceeded;
		} catch {
			/* non-critical */
		}
		return { workloads, containers, stale, unusedImagesMB, unusedImagesCount, unusedImagesExceeded };
	},
	{ workloads: [], containers: [], stale: [], unusedImagesMB: 0, unusedImagesCount: 0, unusedImagesExceeded: false }
);

// Shared global Settings object. Backs both the settings sub-layout (which
// derives the proxy provider for its sub-nav) and the settings landing form.
// `value` is null until first load — consumers guard with `?.`.
export const settingsCache = createResourceCache<Settings | null>(
	(signal) => api.getSettings(signal),
	null
);

// Settings subpages with their own (non-/settings) data sources.
export const registriesCache = createResourceCache<Registry[]>(() => api.listRegistries(), []);
export const backupsCache = createResourceCache<BackupInfo[]>(() => api.listBackups(), []);
export const authSettingsCache = createResourceCache<AuthSettings | null>(
	() => api.getAuthSettings(),
	null
);
export const usersCache = createResourceCache<AuthUser[]>(
	async () => (await api.listUsers()) ?? [],
	[]
);

// ── Drill-in LIST pages (single-value SWR) ────────────────────────────────
export const staleContainersCache = createResourceCache<StaleContainer[]>(
	(signal) => api.fetchStaleContainers(signal),
	[]
);

export interface DnsData {
	records: DnsRecordView[];
	wildcardDns: boolean;
}
export const dnsCache = createResourceCache<DnsData>(
	async (signal) => {
		const [s, records] = await Promise.all([api.getSettings(signal), api.getDnsRecords()]);
		return { records, wildcardDns: s.wildcard_dns ?? true };
	},
	{ records: [], wildcardDns: true }
);

// ── DETAIL pages (keyed by route param) ───────────────────────────────────
// Per-id caches so revisiting a detail (or navigating A→B→A) renders the
// cached entity instantly while revalidating. Detail-FORM pages seed their
// editable fields once per id from these (see the pages for the guard).
// apps/[id] warm-seeds BOTH the workload and its containers so the live-status
// badge (which reads containers.length) shows last-known state on revisit
// rather than flashing a wrong "not deployed" before the fetch resolves.
export interface WorkloadDetailSeed {
	workload: Workload;
	containers: Container[];
}
export const workloadDetailCache = createKeyedResourceCache<WorkloadDetailSeed>(
	async (key, signal) => ({
		workload: await api.getWorkload(key, signal),
		containers: await api.listWorkloadContainers(key, signal)
	})
);
export const triggerDetailCache = createKeyedResourceCache<RedeployTrigger>(
	(key, signal) => api.getTrigger(key, signal)
);
export const eventTriggerDetailCache = createKeyedResourceCache<EventTrigger>(
	(key, signal) => api.getEventTrigger(Number(key), signal)
);
export const logScanRuleDetailCache = createKeyedResourceCache<LogScanRule>(
	(key, signal) => api.getLogScanRule(Number(key), signal)
);
export const metricAlertRuleDetailCache = createKeyedResourceCache<MetricAlertRule>(
	(key, signal) => api.getMetricAlertRule(Number(key), signal)
);
export const sharedSecretDetailCache = createKeyedResourceCache<SharedSecret>(
	(key, signal) => api.getSharedSecret(key, signal)
);

const keyedCaches: KeyedResourceCache<unknown>[] = [
	workloadDetailCache,
	triggerDetailCache,
	eventTriggerDetailCache,
	logScanRuleDetailCache,
	metricAlertRuleDetailCache,
	sharedSecretDetailCache
] as KeyedResourceCache<unknown>[];

/** Every cache, for bulk reset on logout. */
const allCaches: ResourceCache<unknown>[] = [
	workloadsCache,
	containersCache,
	proxyRoutesCache,
	triggersCache,
	eventTriggersCache,
	metricAlertRulesCache,
	logScanCache,
	sharedSecretsCache,
	dashboardCache,
	settingsCache,
	registriesCache,
	backupsCache,
	authSettingsCache,
	usersCache,
	staleContainersCache,
	dnsCache
] as ResourceCache<unknown>[];

export function resetAllCaches(): void {
	for (const c of allCaches) c.reset();
	for (const c of keyedCaches) c.reset();
	clearEventsSnapshot();
}
