<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import { slide } from 'svelte/transition';
	import { api } from '$lib/api';
	import { t } from '$lib/i18n';
	import MdiIcon from '$lib/components/MdiIcon.svelte';
	import IconButton from '$lib/components/IconButton.svelte';
	import IconGridSelect from '$lib/components/IconGridSelect.svelte';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';

	interface ActiveOverride {
		module: string;
		baseline_level: string;
		current_level: string;
		activated_at: string;
		expires_at: string;
		remaining_seconds: number;
	}

	// Modules ship with shortcuts; users can also type a freeform name
	// matching the backend allowlist (notify_bridge_*, sqlalchemy.*, etc.).
	// Icons let the IconGridSelect render each entry as a visual chip
	// instead of a bare text list — same pattern as the surrounding
	// log-level / log-format selectors.
	const QUICK_MODULES: { value: string; icon: string; label: string; desc?: string }[] = [
		{ value: 'notify_bridge_core.notifications.telegram.client', icon: 'mdiSend', label: 'Telegram client' },
		{ value: 'notify_bridge_core.notifications.dispatcher', icon: 'mdiCallSplit', label: 'Dispatcher' },
		{ value: 'notify_bridge_core.providers.immich', icon: 'mdiImageMultiple', label: 'Immich provider' },
		{ value: 'notify_bridge_server.services.watcher', icon: 'mdiEyeOutline', label: 'Watcher' },
		{ value: 'notify_bridge_server.services.deferred_dispatch', icon: 'mdiClockOutline', label: 'Deferred dispatch' },
		{ value: 'notify_bridge_server.services.scheduled_dispatch', icon: 'mdiCalendarClock', label: 'Scheduled dispatch' },
		{ value: 'sqlalchemy.engine', icon: 'mdiDatabase', label: 'SQLAlchemy engine (SQL)' },
		{ value: 'aiohttp.client', icon: 'mdiWeb', label: 'aiohttp client' },
	];

	const DURATION_PRESETS: { minutes: number; label: string }[] = [
		{ minutes: 5, label: '5m' },
		{ minutes: 15, label: '15m' },
		{ minutes: 30, label: '30m' },
		{ minutes: 60, label: '1h' },
		{ minutes: 120, label: '2h' },
	];

	let active = $state<ActiveOverride[]>([]);
	let pickedModule = $state(QUICK_MODULES[0].value);
	let customModule = $state('');
	let pickedMinutes = $state(30);
	let submitting = $state(false);
	let tickHandle: ReturnType<typeof setInterval> | null = null;
	// Resync from the backend every N seconds so a server-side auto-revert
	// is reflected even if we missed a tick. Tracked as elapsed-time so the
	// 1s ticker can drift without breaking the cadence.
	const RESYNC_EVERY_SECONDS = 30;
	let lastResyncAt = Date.now();

	async function refresh(): Promise<void> {
		try {
			const data = await api<{ active: ActiveOverride[] }>(
				'/settings/diagnostic-mode',
				{ method: 'GET' },
			);
			active = data.active || [];
		} catch (err: unknown) {
			// Surface non-401 errors only; settings page already shows a banner
			// when the API is unreachable.
		}
	}

	function tick(): void {
		// Cheap local countdown so the UI doesn't poll the server every second
		// to render a clock. The full refresh happens every 30s OR on action.
		if (active.length === 0) return;
		const now = Date.now();
		active = active
			.map(a => ({
				...a,
				remaining_seconds: Math.max(
					0,
					Math.floor((new Date(a.expires_at).getTime() - now) / 1000),
				),
			}))
			.filter(a => a.remaining_seconds > 0);
	}

	function startTicker(): void {
		if (tickHandle != null) return;
		tickHandle = setInterval(() => {
			tick();
			const now = Date.now();
			if (now - lastResyncAt >= RESYNC_EVERY_SECONDS * 1000) {
				lastResyncAt = now;
				void refresh();
			}
		}, 1000);
	}

	function stopTicker(): void {
		if (tickHandle != null) {
			clearInterval(tickHandle);
			tickHandle = null;
		}
	}

	onMount(() => {
		lastResyncAt = Date.now();
		void refresh();
		startTicker();
	});

	onDestroy(() => {
		stopTicker();
	});

	function effectiveModule(): string {
		return (customModule.trim() || pickedModule).trim();
	}

	async function activate(): Promise<void> {
		const mod = effectiveModule();
		if (!mod) {
			snackError(t('settings.diagModuleRequired'));
			return;
		}
		submitting = true;
		try {
			const entry = await api<ActiveOverride>('/settings/diagnostic-mode', {
				method: 'POST',
				body: JSON.stringify({ module: mod, duration_minutes: pickedMinutes }),
			});
			// Replace any existing row for this module with the new schedule.
			active = [
				...active.filter(a => a.module !== entry.module),
				entry,
			];
			customModule = '';
			snackSuccess(t('settings.diagActivated'));
		} catch (err: unknown) {
			const msg = err instanceof Error ? err.message : String(err);
			snackError(msg || t('settings.diagActivateFailed'));
		} finally {
			submitting = false;
		}
	}

	async function revert(module: string): Promise<void> {
		try {
			await api(`/settings/diagnostic-mode/${encodeURIComponent(module)}`, {
				method: 'DELETE',
			});
			active = active.filter(a => a.module !== module);
			snackSuccess(t('settings.diagReverted'));
		} catch (err: unknown) {
			const msg = err instanceof Error ? err.message : String(err);
			snackError(msg || t('settings.diagRevertFailed'));
		}
	}

	function formatRemaining(seconds: number): string {
		if (seconds <= 0) return '0s';
		const mins = Math.floor(seconds / 60);
		const secs = seconds % 60;
		if (mins >= 60) {
			const hours = Math.floor(mins / 60);
			const remMins = mins % 60;
			return `${hours}h ${remMins}m`;
		}
		if (mins > 0) return `${mins}m ${secs}s`;
		return `${secs}s`;
	}
</script>

<section class="diag glass">
	<header class="diag-head">
		<div class="diag-eyebrow">
			<MdiIcon name="mdiBugOutline" size={12} />
			<span>{t('settings.diagnostics')}</span>
		</div>
		<h3 class="diag-title">{t('settings.diagnosticsHeadline')}</h3>
		<p class="diag-sub">{t('settings.diagnosticsHint')}</p>
	</header>

	<!-- Compose new override -->
	<div class="diag-compose">
		<div class="diag-label">
			<span>{t('settings.diagModuleQuick')}</span>
			<IconGridSelect items={QUICK_MODULES} bind:value={pickedModule} columns={2} compact />
		</div>

		<label class="diag-label">
			<span>{t('settings.diagModuleCustom')}</span>
			<input
				bind:value={customModule}
				type="text"
				autocomplete="off"
				spellcheck="false"
				placeholder={t('settings.diagModuleCustomPlaceholder')}
				class="diag-input"
			/>
		</label>

		<div class="diag-label">
			<span>{t('settings.diagDuration')}</span>
			<div class="diag-duration-chips">
				{#each DURATION_PRESETS as preset (preset.minutes)}
					<button
						type="button"
						class="diag-chip"
						class:diag-chip-active={pickedMinutes === preset.minutes}
						onclick={() => (pickedMinutes = preset.minutes)}
					>
						{preset.label}
					</button>
				{/each}
			</div>
		</div>

		<button
			type="button"
			onclick={activate}
			disabled={submitting}
			class="diag-activate"
		>
			<MdiIcon name="mdiPlay" size={14} />
			<span>{submitting ? t('common.loading') : t('settings.diagActivate')}</span>
		</button>
	</div>

	<!-- Active overrides list -->
	{#if active.length > 0}
		<div class="diag-active" in:slide={{ duration: 180 }}>
			<div class="diag-active-head">
				<MdiIcon name="mdiTimerSandComplete" size={12} />
				<span>{t('settings.diagActive')}</span>
			</div>
			{#each active as ov (ov.module)}
				<div class="diag-row">
					<div class="diag-row-info">
						<code class="diag-row-module">{ov.module}</code>
						<span class="diag-row-meta">
							{t('settings.diagRevertsIn')} <strong>{formatRemaining(ov.remaining_seconds)}</strong>
							<span class="diag-row-baseline">→ {ov.baseline_level}</span>
						</span>
					</div>
					<IconButton
						icon="mdiUndoVariant"
						title={t('settings.diagRevertNow')}
						onclick={() => revert(ov.module)}
						size={16}
					/>
				</div>
			{/each}
		</div>
	{/if}
</section>

<style>
	.diag {
		padding: 1.5rem 1.6rem 1.4rem;
		display: flex;
		flex-direction: column;
		gap: 1.15rem;
	}
	.diag-head {
		position: relative;
		z-index: 1;
	}
	.diag-eyebrow {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
		font-family: var(--font-mono);
		font-size: 0.62rem;
		text-transform: uppercase;
		letter-spacing: 0.18em;
		color: var(--color-muted-foreground);
		margin-bottom: 0.45rem;
	}
	.diag-title {
		margin: 0;
		font-family: var(--font-display);
		font-style: italic;
		font-weight: 400;
		font-size: 1.15rem;
		line-height: 1.35;
		letter-spacing: -0.015em;
		color: var(--color-foreground);
		max-width: 38ch;
	}
	.diag-sub {
		margin: 0.45rem 0 0 0;
		font-size: 0.78rem;
		color: var(--color-muted-foreground);
		max-width: 56ch;
	}
	.diag-compose {
		position: relative;
		z-index: 1;
		display: flex;
		flex-direction: column;
		gap: 0.7rem;
		padding-top: 0.4rem;
		border-top: 1px solid var(--color-border);
	}
	.diag-label {
		display: flex;
		flex-direction: column;
		gap: 0.32rem;
	}
	.diag-label > span {
		font-size: 0.74rem;
		font-weight: 500;
		color: var(--color-foreground);
	}
	.diag-input {
		width: 100%;
		font-family: var(--font-mono);
		font-size: 0.78rem;
		padding: 0.45rem 0.7rem;
		border: 1px solid var(--color-border);
		border-radius: 8px;
		background: var(--color-glass);
		color: var(--color-foreground);
	}
	.diag-duration-chips {
		display: flex;
		flex-wrap: wrap;
		gap: 0.35rem;
	}
	.diag-chip {
		padding: 0.32rem 0.75rem;
		border-radius: 999px;
		border: 1px solid var(--color-border);
		background: transparent;
		color: var(--color-muted-foreground);
		font-family: var(--font-mono);
		font-size: 0.72rem;
		cursor: pointer;
		transition: background 0.15s, color 0.15s, border-color 0.15s;
	}
	.diag-chip:hover {
		background: var(--color-glass-strong);
		color: var(--color-foreground);
	}
	.diag-chip-active {
		background: color-mix(in srgb, var(--color-primary) 12%, transparent);
		color: var(--color-primary);
		border-color: color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
	}
	.diag-activate {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.4rem;
		align-self: flex-start;
		padding: 0.55rem 1.1rem;
		border-radius: 10px;
		border: 1px solid color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
		background: color-mix(in srgb, var(--color-primary) 12%, transparent);
		color: var(--color-primary);
		font-family: var(--font-display);
		font-style: italic;
		font-size: 0.85rem;
		cursor: pointer;
		transition: background 0.15s, color 0.15s, border-color 0.15s;
	}
	.diag-activate:hover {
		background: color-mix(in srgb, var(--color-primary) 18%, transparent);
		border-color: color-mix(in srgb, var(--color-primary) 65%, var(--color-border));
	}
	.diag-activate:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.diag-active {
		display: flex;
		flex-direction: column;
		gap: 0.4rem;
		padding-top: 0.55rem;
		border-top: 1px solid var(--color-border);
	}
	.diag-active-head {
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		font-family: var(--font-mono);
		font-size: 0.58rem;
		text-transform: uppercase;
		letter-spacing: 0.18em;
		color: var(--color-muted-foreground);
	}
	.diag-row {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.6rem;
		padding: 0.5rem 0.65rem;
		border-radius: 10px;
		border: 1px solid var(--color-border);
		background: var(--color-glass-strong);
	}
	.diag-row-info {
		display: flex;
		flex-direction: column;
		gap: 0.2rem;
		min-width: 0;
	}
	.diag-row-module {
		font-family: var(--font-mono);
		font-size: 0.78rem;
		color: var(--color-foreground);
		word-break: break-all;
	}
	.diag-row-meta {
		font-size: 0.72rem;
		color: var(--color-muted-foreground);
	}
	.diag-row-baseline {
		font-family: var(--font-mono);
		font-size: 0.7rem;
		margin-left: 0.4rem;
		opacity: 0.7;
	}
</style>
