<!--
  Settings › Overview (General)

  Display preferences (timezone), core infrastructure (domain/IPs/network/
  subdomain/polling/volume path), and the proxy-provider choice. Everything
  else — DNS, integrations, maintenance — lives on its own page now.
-->
<script lang="ts">
	import { onMount } from 'svelte';
	import { get } from 'svelte/store';
	import { updateSettings } from '$lib/api';
	import type { Settings } from '$lib/types';
	import { settingsCache } from '$lib/stores/caches';
	import FormField from '$lib/components/FormField.svelte';
	import TimezoneSelector from '$lib/components/TimezoneSelector.svelte';
	import Skeleton from '$lib/components/Skeleton.svelte';
	import { toasts } from '$lib/stores/toast';
	import { t } from '$lib/i18n';
	import { IconLoader, IconInfo } from '$lib/components/icons';

	let loading = $state(true);
	let saving = $state(false);

	let domain = $state('');
	let serverIp = $state('');
	let publicIp = $state('');
	let network = $state('');
	let subdomainPattern = $state('');
	let pollingInterval = $state('');
	let baseVolumePath = $state('');
	let proxyProvider = $state('npm');
	let errors = $state<Record<string, string>>({});

	// Go duration ("5m", "300s", "1h") ↔ seconds string, for the number input.
	function parseDurationToSeconds(dur: string): string {
		if (!dur) return '60';
		const m = dur.match(/^(\d+)(s|m|h)?$/);
		if (!m) return dur;
		const n = parseInt(m[1], 10);
		switch (m[2]) {
			case 'h': return String(n * 3600);
			case 'm': return String(n * 60);
			default: return String(n);
		}
	}

	function secondsToDuration(sec: string | number): string {
		const n = parseInt(String(sec), 10);
		if (isNaN(n) || n <= 0) return '60s';
		if (n >= 3600 && n % 3600 === 0) return `${n / 3600}h`;
		if (n >= 60 && n % 60 === 0) return `${n / 60}m`;
		return `${n}s`;
	}

	function validateDomain(value: string): string {
		if (!value.trim()) return '';
		if (!/^[a-zA-Z0-9][a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/.test(value.trim())) return $t('validation.invalidDomain');
		return '';
	}

	function validateIp(value: string): string {
		if (!value.trim()) return '';
		if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(value.trim())) return $t('validation.invalidIp');
		return '';
	}

	function validatePollingInterval(value: string | number): string {
		const s = String(value ?? '');
		if (!s.trim()) return '';
		const num = parseInt(s, 10);
		if (isNaN(num) || num < 60 || num > 86400) return $t('validation.invalidPollingInterval');
		return '';
	}

	function validateAll(): boolean {
		const next: Record<string, string> = {};
		const dErr = validateDomain(domain); if (dErr) next.domain = dErr;
		const ipErr = validateIp(serverIp); if (ipErr) next.serverIp = ipErr;
		const piErr = validatePollingInterval(pollingInterval); if (piErr) next.pollingInterval = piErr;
		errors = next;
		return Object.keys(next).length === 0;
	}

	// Seed the form fields from a Settings object. Applied once on mount (from
	// the warm cache if available, else from the network) — never re-applied
	// during a background refresh, so it can't clobber unsaved edits.
	function seedForm(s: Settings) {
		domain = s.domain ?? '';
		serverIp = s.server_ip ?? '';
		publicIp = s.public_ip ?? '';
		network = s.network ?? '';
		subdomainPattern = s.subdomain_pattern ?? '';
		pollingInterval = parseDurationToSeconds(s.polling_interval ?? '60');
		baseVolumePath = s.base_volume_path ?? '';
		proxyProvider = s.proxy_provider ?? 'npm';
	}

	async function loadSettings() {
		// Warm cache → seed immediately and skip the skeleton (kills the
		// entry flicker). Cold → keep the skeleton until the fetch resolves.
		const cached = get(settingsCache).value;
		if (cached) {
			seedForm(cached);
			loading = false;
		}
		await settingsCache.refresh();
		const fresh = get(settingsCache).value;
		// Only seed from the fetch when nothing was shown yet (cold load), so a
		// background refresh never overwrites edits the user may have started.
		if (fresh && loading) seedForm(fresh);
		loading = false;
		const { error } = get(settingsCache);
		if (error && !cached) {
			toasts.error(error || $t('settingsGeneral.loadFailed'));
		}
	}

	async function handleSave() {
		if (!validateAll()) return;
		saving = true;
		try {
			await updateSettings({
				domain: domain.trim(),
				server_ip: serverIp.trim(),
				public_ip: publicIp.trim(),
				network: network.trim(),
				subdomain_pattern: subdomainPattern.trim(),
				polling_interval: secondsToDuration(pollingInterval),
				base_volume_path: baseVolumePath.trim(),
				proxy_provider: proxyProvider
			});
			// Refetch the canonical Settings into the shared cache so the
			// sub-layout's proxy-nav and the next visit reflect the save.
			// (PUT /api/settings returns {status:"updated"}, not the object —
			// so we must refresh, not set() the response.)
			await settingsCache.refresh();
			toasts.success($t('settingsGeneral.saved'));
		} catch (err) {
			toasts.error(err instanceof Error ? err.message : $t('settingsGeneral.saveFailed'));
		} finally {
			saving = false;
		}
	}

	onMount(loadSettings);
</script>

<svelte:head>
	<title>{$t('settingsGeneral.title')} - {$t('app.name')}</title>
</svelte:head>

<div class="space-y-6">
	<!-- Timezone selector (display preference) -->
	<TimezoneSelector />

	{#if loading}
		<div class="space-y-4">
			<Skeleton height="2rem" width="12rem" />
			<div class="grid grid-cols-2 gap-4">
				{#each Array(6) as _}
					<Skeleton height="4rem" />
				{/each}
			</div>
		</div>
	{:else}
		<!-- Core infrastructure -->
		<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]">
			<h2 class="mb-1 text-lg font-semibold text-[var(--text-primary)]">{$t('settingsGeneral.globalConfig')}</h2>
			<p class="mb-4 text-sm text-[var(--text-secondary)]">{$t('settingsGeneral.globalConfigDesc')}</p>

			<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
				<FormField label={$t('settingsGeneral.domain')} name="domain" bind:value={domain} placeholder="example.com" error={errors.domain ?? ''} helpText={$t('settingsGeneral.domainHelp')} />
				<FormField label={$t('settingsGeneral.serverIp')} name="serverIp" bind:value={serverIp} placeholder="192.168.1.100" error={errors.serverIp ?? ''} helpText={$t('settingsGeneral.serverIpHelp')} />
				<FormField label={$t('settingsGeneral.publicIp')} name="publicIp" bind:value={publicIp} placeholder="93.84.96.191" helpText={$t('settingsGeneral.publicIpHelp')} />
				<FormField label={$t('settingsGeneral.dockerNetwork')} name="network" bind:value={network} placeholder="staging-net" helpText={$t('settingsGeneral.dockerNetworkHelp')} />

				<div>
					<div class="flex items-center gap-1.5">
						<label for="subdomainPattern" class="block text-sm font-medium text-[var(--text-primary)]">{$t('settingsGeneral.subdomainPattern')}</label>
						<div class="group relative">
							<button type="button" class="inline-flex items-center justify-center rounded-full text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] transition-colors" aria-label={$t('settingsGeneral.subdomainVarsTitle')}>
								<IconInfo size={14} />
							</button>
							<div class="pointer-events-none absolute bottom-full left-1/2 z-50 mb-2 w-64 -translate-x-1/2 rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] p-3 shadow-lg opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
								<p class="mb-2 text-xs font-semibold text-[var(--text-primary)]">{$t('settingsGeneral.subdomainVarsTitle')}</p>
								<ul class="space-y-1 text-xs text-[var(--text-secondary)]">
									<li><code class="rounded bg-[var(--surface-card-hover)] px-1 py-0.5 font-mono text-[var(--text-link)]">{'{project}'}</code> — {$t('settingsGeneral.varProject')}</li>
									<li><code class="rounded bg-[var(--surface-card-hover)] px-1 py-0.5 font-mono text-[var(--text-link)]">{'{stage}'}</code> — {$t('settingsGeneral.varStage')}</li>
									<li><code class="rounded bg-[var(--surface-card-hover)] px-1 py-0.5 font-mono text-[var(--text-link)]">{'{tag}'}</code> — {$t('settingsGeneral.varTag')}</li>
									<li><code class="rounded bg-[var(--surface-card-hover)] px-1 py-0.5 font-mono text-[var(--text-link)]">{'{port}'}</code> — {$t('settingsGeneral.varPort')}</li>
								</ul>
								<div class="absolute left-1/2 top-full -translate-x-1/2 border-4 border-transparent border-t-[var(--border-primary)]"></div>
							</div>
						</div>
					</div>
					<input
						id="subdomainPattern"
						type="text"
						bind:value={subdomainPattern}
						placeholder="stage-{'{stage}'}-{'{project}'}"
						class="mt-1.5 block w-full rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:border-[var(--color-brand-500)] focus:outline-none focus:ring-1 focus:ring-[var(--color-brand-500)]"
					/>
					<p class="mt-1 text-xs text-[var(--text-tertiary)]">{$t('settingsGeneral.subdomainPatternHelp')}</p>
				</div>

				<FormField label={$t('settingsGeneral.pollingInterval')} name="pollingInterval" type="number" bind:value={pollingInterval} placeholder="60" error={errors.pollingInterval ?? ''} helpText={$t('settingsGeneral.pollingIntervalHelp')} />
				<FormField label={$t('settingsGeneral.baseVolumePath')} name="baseVolumePath" bind:value={baseVolumePath} placeholder="/data" helpText={$t('settingsGeneral.baseVolumePathHelp')} />
			</div>
		</div>

		<!-- Proxy provider -->
		<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]">
			<h2 class="mb-1 text-lg font-semibold text-[var(--text-primary)]">{$t('settings.proxyProvider')}</h2>
			<p class="mb-4 text-sm text-[var(--text-secondary)]">{$t('settings.proxyProviderHelp')}</p>

			<div class="flex flex-col gap-3 sm:flex-row">
				<label class="flex flex-1 cursor-pointer items-start gap-3 rounded-lg border p-3 transition-colors {proxyProvider === 'none' ? 'border-[var(--color-brand-500)] bg-[var(--surface-card-hover)]' : 'border-[var(--border-primary)] hover:bg-[var(--surface-card-hover)]'}">
					<input type="radio" bind:group={proxyProvider} value="none" class="mt-0.5 h-4 w-4 text-[var(--color-brand-600)] focus:ring-[var(--color-brand-500)]" />
					<div>
						<span class="text-sm font-medium text-[var(--text-primary)]">{$t('settings.proxyNone')}</span>
						<p class="text-xs text-[var(--text-tertiary)]">{$t('settings.proxyNoneDesc')}</p>
					</div>
				</label>
				<label class="flex flex-1 cursor-pointer items-start gap-3 rounded-lg border p-3 transition-colors {proxyProvider === 'npm' ? 'border-[var(--color-brand-500)] bg-[var(--surface-card-hover)]' : 'border-[var(--border-primary)] hover:bg-[var(--surface-card-hover)]'}">
					<input type="radio" bind:group={proxyProvider} value="npm" class="mt-0.5 h-4 w-4 text-[var(--color-brand-600)] focus:ring-[var(--color-brand-500)]" />
					<div>
						<span class="text-sm font-medium text-[var(--text-primary)]">{$t('settings.proxyNpm')}</span>
						<p class="text-xs text-[var(--text-tertiary)]">{$t('settings.proxyNpmDesc')}</p>
					</div>
				</label>
				<label class="flex flex-1 cursor-pointer items-start gap-3 rounded-lg border p-3 transition-colors {proxyProvider === 'traefik' ? 'border-[var(--color-brand-500)] bg-[var(--surface-card-hover)]' : 'border-[var(--border-primary)] hover:bg-[var(--surface-card-hover)]'}">
					<input type="radio" bind:group={proxyProvider} value="traefik" class="mt-0.5 h-4 w-4 text-[var(--color-brand-600)] focus:ring-[var(--color-brand-500)]" />
					<div>
						<span class="text-sm font-medium text-[var(--text-primary)]">{$t('settings.proxyTraefik')}</span>
						<p class="text-xs text-[var(--text-tertiary)]">{$t('settings.proxyTraefikDesc')}</p>
					</div>
				</label>
			</div>

			{#if proxyProvider === 'none'}
				<div class="mt-3 rounded-lg border border-amber-300 bg-amber-50 dark:border-amber-700 dark:bg-amber-950/30 p-3">
					<p class="text-sm text-amber-800 dark:text-amber-300">{$t('settings.proxyNoneWarning')}</p>
				</div>
			{:else}
				<p class="mt-3 text-xs text-[var(--text-tertiary)]">
					{proxyProvider === 'npm' ? $t('settingsGeneral.configureNpm') : $t('settingsGeneral.configureTraefik')}
					<a href="/settings/{proxyProvider}" class="text-[var(--text-link)] hover:text-[var(--text-link-hover)] underline">→ {$t('settingsGeneral.configureLink')}</a>
				</p>
			{/if}
		</div>

		<!-- Single save for everything on this page -->
		<div class="sticky bottom-4 z-10 flex justify-end">
			<button onclick={handleSave} disabled={saving} class="inline-flex items-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-5 py-2.5 text-sm font-medium text-white shadow-lg transition-all duration-150 hover:bg-[var(--color-brand-700)] disabled:opacity-50 active:animate-press">
				{#if saving}<IconLoader size={16} />{/if}
				{saving ? $t('settingsGeneral.saving') : $t('settingsGeneral.saveSettings')}
			</button>
		</div>
	{/if}
</div>
