<script lang="ts">
	import { t } from '$lib/i18n';
	import MdiIcon from '$lib/components/MdiIcon.svelte';
	import Hint from '$lib/components/Hint.svelte';
	import { api } from '$lib/api';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
	import { releaseStatusCache } from '$lib/stores/caches.svelte';
	import type { ReleaseProviderKind, ReleaseStatus, ReleaseTestResult } from '$lib/types';

	interface Props {
		// All five fields are persisted as strings via the /settings PUT —
		// the parent owns the boundary type. Bool flags use "0" / "1".
		providerKind: string;
		providerUrl: string;
		providerRepo: string;
		includePrereleases: string;
		checkIntervalHours: string;
	}

	let {
		providerKind = $bindable(),
		providerUrl = $bindable(),
		providerRepo = $bindable(),
		includePrereleases = $bindable(),
		checkIntervalHours = $bindable(),
	}: Props = $props();

	let checking = $state(false);
	let testing = $state(false);
	let testResult = $state<ReleaseTestResult | null>(null);

	const status = $derived(releaseStatusCache.value);
	const prereleaseChecked = $derived(includePrereleases === '1');
	const isDisabled = $derived(providerKind === 'disabled');

	// Stale Test-result on input change is misleading — wipe whenever any of
	// the probed parameters change so the strip reflects "current" state.
	$effect(() => {
		// Touch each parameter to register dependency.
		void providerKind; void providerUrl; void providerRepo; void prereleaseChecked;
		testResult = null;
	});

	type Tone = 'mint' | 'citrus' | 'coral' | 'sky';

	const stateTone: Tone = $derived.by(() => {
		if (!status) return 'sky';
		if (status.error && status.error !== 'disabled' && status.error !== 'provider_changed') return 'coral';
		if (status.update_available) return 'citrus';
		if (status.provider === 'disabled') return 'sky';
		return 'mint';
	});

	const stateLabel = $derived.by(() => {
		if (!status) return t('settings.release.statusUnknown');
		if (status.provider === 'disabled') return t('settings.release.statusDisabled');
		if (status.error && status.error !== 'provider_changed') return t('settings.release.statusError');
		if (status.update_available) return t('settings.release.statusUpdate');
		if (status.latest) return t('settings.release.statusUpToDate');
		return t('settings.release.statusUnknown');
	});

	// Map backend error taxonomy → localized text. Falls back to the raw code
	// only when the key is missing (so a new server code surfaces something).
	function localizedError(code: string | null): string {
		if (!code) return '';
		const key = `settings.release.error.${code}`;
		const localized = t(key);
		// `t` falls back to the key itself when missing — detect by exact match.
		return localized === key ? code : localized;
	}

	function relTime(iso: string | null): string {
		if (!iso) return t('settings.release.never');
		const then = Date.parse(iso);
		if (!Number.isFinite(then)) return t('settings.release.never');
		const diff = Date.now() - then;
		const min = Math.round(diff / 60_000);
		if (min < 1) return t('settings.release.justNow');
		if (min < 60) return t('settings.release.minutesAgo').replace('{n}', String(min));
		const h = Math.round(min / 60);
		if (h < 24) return t('settings.release.hoursAgo').replace('{n}', String(h));
		const d = Math.round(h / 24);
		return t('settings.release.daysAgo').replace('{n}', String(d));
	}

	function setProvider(kind: ReleaseProviderKind): void {
		providerKind = kind;
	}

	function onIntervalInput(e: Event): void {
		// The native input emits string values; we keep the contract by
		// re-coercing to string before assigning to the bindable prop.
		const raw = (e.currentTarget as HTMLInputElement).value;
		checkIntervalHours = raw === '' ? '' : String(Math.max(1, Math.min(168, Number(raw))));
	}

	async function checkNow(): Promise<void> {
		checking = true;
		try {
			const next = await api<ReleaseStatus>('/settings/release/check', { method: 'POST' });
			releaseStatusCache.set(next);
			snackSuccess(t('settings.release.checkDone'));
		} catch (err: unknown) {
			snackError(err instanceof Error ? err.message : t('settings.release.checkFailed'));
		} finally {
			checking = false;
		}
	}

	async function testProvider(): Promise<void> {
		testing = true;
		testResult = null;
		try {
			testResult = await api<ReleaseTestResult>('/settings/release/test', {
				method: 'POST',
				body: JSON.stringify({
					provider_kind: providerKind,
					provider_url: providerUrl,
					provider_repo: providerRepo,
					include_prereleases: prereleaseChecked,
				}),
			});
			if (testResult.ok) snackSuccess(t('settings.release.testOk'));
			else snackError(t('settings.release.testFailed'));
		} catch (err: unknown) {
			snackError(err instanceof Error ? err.message : t('settings.release.testFailed'));
		} finally {
			testing = false;
		}
	}
</script>

<section class="rel glass" id="release">
	<header class="rel-head">
		<div class="rel-eyebrow">
			<MdiIcon name="mdiUpdate" size={12} />
			<span>{t('settings.release.eyebrow')}</span>
		</div>
		<h3 class="rel-title">{t('settings.release.headline')}</h3>
	</header>

	<div class="rel-body">
		<!-- 01 Provider — native radios for free keyboard a11y. -->
		<div class="row">
			<div class="row-label">
				<span class="row-num">01</span>
				<span class="row-name">
					{t('settings.release.provider')}
					<Hint text={t('settings.release.providerHint')} />
				</span>
			</div>
			<div class="row-control">
				<div class="seg" role="radiogroup" aria-label={t('settings.release.provider')}>
					<label class="seg-item" class:seg-active={providerKind === 'gitea'}>
						<input
							type="radio"
							name="release-provider"
							value="gitea"
							checked={providerKind === 'gitea'}
							onchange={() => setProvider('gitea')}
							class="seg-radio"
						/>
						<span class="seg-content"><MdiIcon name="mdiGit" size={13} /> Gitea</span>
					</label>
					<label class="seg-item seg-soon" title={t('settings.release.comingSoon')}>
						<input
							type="radio"
							name="release-provider"
							value="github"
							disabled
							class="seg-radio"
						/>
						<span class="seg-content"><MdiIcon name="mdiGithub" size={13} /> GitHub</span>
					</label>
					<label class="seg-item" class:seg-active={providerKind === 'disabled'}>
						<input
							type="radio"
							name="release-provider"
							value="disabled"
							checked={providerKind === 'disabled'}
							onchange={() => setProvider('disabled')}
							class="seg-radio"
						/>
						<span class="seg-content"><MdiIcon name="mdiPowerSettings" size={13} /> {t('settings.release.disabled')}</span>
					</label>
				</div>
			</div>
		</div>

		<!-- 02 Repository -->
		<div class="row" class:row-dim={isDisabled}>
			<div class="row-label">
				<span class="row-num">02</span>
				<span class="row-name">
					{t('settings.release.repository')}
					<Hint text={t('settings.release.repositoryHint')} />
				</span>
			</div>
			<div class="row-control repo-grid">
				<input
					bind:value={providerUrl}
					placeholder="https://git.example.com"
					class="text-input"
					type="url"
					spellcheck="false"
					disabled={isDisabled}
				/>
				<input
					bind:value={providerRepo}
					placeholder="owner/repo"
					class="text-input mono"
					spellcheck="false"
					disabled={isDisabled}
				/>
			</div>
		</div>

		<!-- 03 Options — slider toggle for include-prereleases. -->
		<div class="row" class:row-dim={isDisabled}>
			<div class="row-label">
				<span class="row-num">03</span>
				<span class="row-name">
					{t('settings.release.options')}
					<Hint text={t('settings.release.prereleasesHint')} />
				</span>
			</div>
			<div class="row-control">
				<button
					type="button"
					class="toggle"
					class:toggle-disabled={isDisabled}
					onclick={() => { if (!isDisabled) includePrereleases = prereleaseChecked ? '0' : '1'; }}
					aria-pressed={prereleaseChecked}
					disabled={isDisabled}
				>
					<span class="toggle-track" class:toggle-on={prereleaseChecked} aria-hidden="true">
						<span class="toggle-thumb"></span>
					</span>
					<span class="toggle-label-text">{t('settings.release.includePrereleases')}</span>
				</button>
			</div>
		</div>

		<!-- 04 Check interval -->
		<div class="row" class:row-dim={isDisabled}>
			<div class="row-label">
				<span class="row-num">04</span>
				<span class="row-name">
					{t('settings.release.interval')}
					<Hint text={t('settings.release.intervalHint')} />
				</span>
			</div>
			<div class="row-control interval">
				<input
					type="number"
					min={1}
					max={168}
					value={checkIntervalHours}
					oninput={onIntervalInput}
					class="text-input num"
					disabled={isDisabled}
				/>
				<span class="unit">{t('settings.release.hoursUnit')}</span>
				<span class="footnote">{t('settings.release.intervalRange')}</span>
			</div>
		</div>
	</div>

	<!-- State strip -->
	<footer class="strip" data-tone={stateTone}>
		<div class="strip-left">
			<span class="dot" data-tone={stateTone} aria-hidden="true"></span>
			<div class="strip-text">
				<div class="strip-state">{stateLabel}</div>
				<div class="strip-meta">
					<span class="versions">
						<span class="v-current">v{status?.current ?? '—'}</span>
						{#if status?.latest && status.latest !== status.current}
							<span class="arrow" aria-hidden="true">→</span>
							<span
								class="v-latest"
								class:v-latest-update={status.update_available}
							>v{status.latest}{#if status.latest_prerelease} · pre{/if}</span>
						{/if}
					</span>
					<span class="sep" aria-hidden="true">·</span>
					<span class="checked">
						{t('settings.release.lastChecked')}: <span class="rel-time">{relTime(status?.checked_at ?? null)}</span>
					</span>
				</div>
				{#if status?.error && status.error !== 'disabled' && status.error !== 'provider_changed'}
					<div class="strip-error">
						<MdiIcon name="mdiAlertCircleOutline" size={12} /> {localizedError(status.error)}
					</div>
				{/if}
				{#if testResult && !testResult.ok}
					<div class="strip-error">
						<MdiIcon name="mdiAlertCircleOutline" size={12} /> {t('settings.release.testFailed')}:
						{localizedError(testResult.error)}
					</div>
				{/if}
				{#if testResult && testResult.ok && testResult.info}
					<div class="strip-test-ok">
						<MdiIcon name="mdiCheckCircleOutline" size={12} /> {t('settings.release.testFound')}:
						<span class="mono">v{testResult.info.version}</span>
					</div>
				{/if}
			</div>
		</div>
		<div class="strip-actions">
			{#if status?.update_available && status.latest_url}
				<a
					class="strip-btn strip-btn-cta"
					href={status.latest_url}
					target="_blank"
					rel="noopener noreferrer"
				>
					<MdiIcon name="mdiOpenInNew" size={13} />
					<span>{t('settings.release.viewRelease').replace('{v}', status.latest ?? '')}</span>
				</a>
			{/if}
			<button
				type="button"
				class="strip-btn"
				onclick={testProvider}
				disabled={testing || isDisabled || !providerRepo}
			>
				<MdiIcon name={testing ? 'mdiLoading' : 'mdiCheckNetworkOutline'} size={13} />
				<span>{t('settings.release.testConnection')}</span>
			</button>
			<button
				type="button"
				class="strip-btn strip-btn-primary"
				onclick={checkNow}
				disabled={checking || isDisabled}
			>
				<MdiIcon name={checking ? 'mdiLoading' : 'mdiRefresh'} size={13} />
				<span>{t('settings.release.checkNow')}</span>
			</button>
		</div>
	</footer>
</section>

<style>
	.rel {
		padding: 1.5rem 1.6rem 0;
		display: flex;
		flex-direction: column;
		gap: 1.2rem;
		overflow: hidden;
	}
	.rel-head { position: relative; z-index: 1; }
	.rel-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;
	}
	.rel-title {
		margin: 0;
		font-family: var(--font-display);
		font-weight: 400;
		font-style: italic;
		font-size: 1.25rem;
		line-height: 1.3;
		letter-spacing: -0.015em;
		color: var(--color-foreground);
		max-width: 42ch;
	}

	.rel-body {
		position: relative;
		z-index: 1;
		display: flex;
		flex-direction: column;
	}
	.row {
		display: grid;
		grid-template-columns: 11rem 1fr;
		gap: 1.4rem;
		padding: 1rem 0;
		border-top: 1px solid var(--color-border);
	}
	.row:first-child { border-top: 0; padding-top: 0.4rem; }
	.row-dim { opacity: 0.55; }
	.row-label {
		display: flex;
		flex-direction: column;
		gap: 0.3rem;
		padding-top: 0.15rem;
	}
	.row-num {
		font-family: var(--font-mono);
		font-size: 0.62rem;
		letter-spacing: 0.18em;
		color: var(--color-muted-foreground);
	}
	.row-name {
		font-size: 0.78rem;
		font-weight: 500;
		color: var(--color-foreground);
		letter-spacing: -0.005em;
		display: inline-flex;
		align-items: center;
	}
	.row-control { min-width: 0; }

	/* Segmented provider control — uses real radios so arrow-key + tab
	   navigation just work via the browser. */
	.seg {
		display: inline-flex;
		gap: 0.25rem;
		padding: 0.25rem;
		background: var(--color-glass-strong);
		border: 1px solid var(--color-rule-strong);
		border-radius: 0.6rem;
	}
	.seg-item {
		display: inline-flex;
		align-items: center;
		border-radius: 0.45rem;
		cursor: pointer;
		position: relative;
	}
	.seg-radio {
		position: absolute;
		opacity: 0;
		pointer-events: none;
		inset: 0;
	}
	.seg-radio:focus-visible + .seg-content {
		outline: 2px solid var(--color-primary);
		outline-offset: 2px;
	}
	.seg-content {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.4rem 0.7rem;
		border-radius: 0.45rem;
		font-size: 0.78rem;
		color: var(--color-muted-foreground);
		transition: background 0.18s, color 0.18s;
	}
	.seg-item:hover:not(.seg-soon) .seg-content {
		color: var(--color-foreground);
		background: var(--color-glass);
	}
	.seg-active .seg-content {
		color: var(--color-foreground);
		background: var(--color-input-bg);
		box-shadow: 0 0 0 1px var(--color-primary);
	}
	.seg-soon { opacity: 0.45; cursor: not-allowed; }

	/* Text fields */
	.repo-grid {
		display: grid;
		grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
		gap: 0.6rem;
		max-width: 100%;
	}
	.text-input {
		width: 100%;
		padding: 0.55rem 0.75rem;
		border: 1px solid var(--color-rule-strong);
		border-radius: 0.6rem;
		background: var(--color-input-bg);
		font-family: var(--font-sans);
		font-size: 0.82rem;
		color: var(--color-foreground);
		transition: border-color 0.18s, box-shadow 0.18s;
	}
	.text-input.mono { font-family: var(--font-mono); }
	.text-input.num { max-width: 6rem; text-align: right; }
	.text-input:focus {
		outline: 0;
		border-color: var(--color-primary);
		box-shadow: 0 0 0 3px var(--color-glow);
	}
	.text-input:disabled { cursor: not-allowed; opacity: 0.55; }

	/* Interval */
	.interval { display: inline-flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
	.unit {
		font-family: var(--font-mono);
		font-size: 0.72rem;
		color: var(--color-muted-foreground);
		letter-spacing: 0.08em;
		text-transform: uppercase;
	}
	.footnote {
		font-size: 0.68rem;
		color: var(--color-muted-foreground);
		font-style: italic;
	}

	/* Slider toggle — mirrors the backup ScheduleCassette pattern. */
	.toggle {
		display: inline-flex;
		align-items: center;
		gap: 0.7rem;
		background: transparent;
		border: 0;
		padding: 0;
		font: inherit;
		color: var(--color-foreground);
		cursor: pointer;
	}
	.toggle:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 4px; border-radius: 4px; }
	.toggle-track {
		position: relative;
		width: 40px;
		height: 22px;
		border-radius: 999px;
		background: var(--color-glass-strong);
		border: 1px solid var(--color-rule-strong);
		flex-shrink: 0;
		transition: background 0.2s, border-color 0.2s;
	}
	.toggle-thumb {
		position: absolute;
		top: 2px;
		left: 2px;
		width: 16px; height: 16px;
		border-radius: 50%;
		background: var(--color-muted-foreground);
		transition: transform 0.2s, background 0.2s;
	}
	.toggle-on {
		background: linear-gradient(135deg, color-mix(in srgb, var(--color-mint) 60%, transparent), color-mix(in srgb, var(--color-primary) 60%, transparent));
		border-color: color-mix(in srgb, var(--color-mint) 60%, var(--color-rule-strong));
	}
	.toggle-on .toggle-thumb {
		background: white;
		transform: translateX(18px);
	}
	.toggle-label-text { font-size: 0.82rem; }
	.toggle-disabled { opacity: 0.55; cursor: not-allowed; }

	/* State strip */
	.strip {
		margin: 0 -1.6rem;
		padding: 1rem 1.6rem;
		border-top: 1px solid var(--color-border);
		background:
			linear-gradient(180deg,
				color-mix(in srgb, var(--color-glass-strong) 60%, transparent),
				transparent
			);
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: 1rem;
		flex-wrap: wrap;
		position: relative;
	}
	.strip[data-tone="citrus"]::before {
		content: '';
		position: absolute;
		left: 0;
		right: 0;
		top: 0;
		height: 1px;
		background: linear-gradient(
			90deg,
			transparent 10%,
			color-mix(in srgb, var(--color-citrus, #d4a73a) 70%, transparent) 50%,
			transparent 90%
		);
		animation: aurora-shimmer 4s linear infinite;
	}
	.strip-left { display: flex; align-items: flex-start; gap: 0.7rem; min-width: 0; flex: 1 1 auto; }
	.dot {
		width: 0.55rem;
		height: 0.55rem;
		border-radius: 999px;
		margin-top: 0.45rem;
		flex-shrink: 0;
	}
	.dot[data-tone="mint"]    { background: var(--color-mint, #6fcfa6); box-shadow: 0 0 8px color-mix(in srgb, var(--color-mint, #6fcfa6) 60%, transparent); }
	.dot[data-tone="citrus"]  { background: var(--color-citrus, #d4a73a); box-shadow: 0 0 10px color-mix(in srgb, var(--color-citrus, #d4a73a) 70%, transparent); }
	.dot[data-tone="coral"]   { background: var(--color-coral, #d27a7a); box-shadow: 0 0 8px color-mix(in srgb, var(--color-coral, #d27a7a) 60%, transparent); }
	.dot[data-tone="sky"]     { background: var(--color-muted-foreground); }
	.strip-text { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
	.strip-state {
		font-family: var(--font-display);
		font-style: italic;
		font-size: 0.95rem;
		letter-spacing: -0.01em;
		color: var(--color-foreground);
	}
	.strip-meta {
		display: inline-flex;
		align-items: center;
		flex-wrap: wrap;
		gap: 0.4rem;
		font-size: 0.74rem;
		color: var(--color-muted-foreground);
	}
	.versions { display: inline-flex; align-items: center; gap: 0.35rem; }
	.v-current { font-family: var(--font-mono); color: var(--color-foreground); }
	.arrow { color: var(--color-muted-foreground); }
	.v-latest { font-family: var(--font-mono); color: var(--color-foreground); }
	.v-latest-update { color: var(--color-citrus, #d4a73a); font-weight: 600; }
	.sep { opacity: 0.5; }
	.rel-time { color: var(--color-foreground); }
	.strip-error {
		font-size: 0.72rem;
		color: var(--color-coral, #d27a7a);
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		margin-top: 0.15rem;
	}
	.strip-test-ok {
		font-size: 0.72rem;
		color: var(--color-mint, #6fcfa6);
		display: inline-flex;
		align-items: center;
		gap: 0.3rem;
		margin-top: 0.15rem;
	}

	.strip-actions { display: inline-flex; gap: 0.5rem; flex-shrink: 0; flex-wrap: wrap; }
	.strip-btn {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.5rem 0.85rem;
		border: 1px solid var(--color-rule-strong);
		border-radius: 0.55rem;
		background: var(--color-input-bg);
		font-size: 0.76rem;
		color: var(--color-foreground);
		cursor: pointer;
		text-decoration: none;
		transition: background 0.18s, border-color 0.18s, transform 0.18s;
	}
	.strip-btn:hover:not(:disabled) {
		background: var(--color-glass-strong);
		border-color: var(--color-primary);
	}
	.strip-btn:active:not(:disabled) { transform: translateY(1px); }
	.strip-btn:disabled { opacity: 0.5; cursor: not-allowed; }
	.strip-btn-primary {
		background: color-mix(in srgb, var(--color-primary) 12%, var(--color-input-bg));
		border-color: color-mix(in srgb, var(--color-primary) 35%, var(--color-rule-strong));
	}
	/* The CTA — high-visibility when an update is available. */
	.strip-btn-cta {
		background: linear-gradient(135deg,
			color-mix(in srgb, var(--color-citrus, #d4a73a) 26%, var(--color-input-bg)),
			color-mix(in srgb, var(--color-citrus, #d4a73a) 14%, var(--color-input-bg))
		);
		border-color: color-mix(in srgb, var(--color-citrus, #d4a73a) 55%, var(--color-rule-strong));
		color: var(--color-foreground);
		font-weight: 500;
		box-shadow: 0 0 12px color-mix(in srgb, var(--color-citrus, #d4a73a) 25%, transparent);
	}
	.strip-btn-cta:hover {
		background: linear-gradient(135deg,
			color-mix(in srgb, var(--color-citrus, #d4a73a) 40%, var(--color-input-bg)),
			color-mix(in srgb, var(--color-citrus, #d4a73a) 22%, var(--color-input-bg))
		);
		border-color: color-mix(in srgb, var(--color-citrus, #d4a73a) 75%, var(--color-rule-strong));
	}

	.mono { font-family: var(--font-mono); }

	@keyframes aurora-shimmer {
		0% { transform: translateX(-100%); }
		100% { transform: translateX(100%); }
	}
	@media (prefers-reduced-motion: reduce) {
		.strip[data-tone="citrus"]::before { animation: none; }
		.strip-btn { transition: none; }
	}

	@media (max-width: 720px) {
		.row {
			grid-template-columns: 1fr;
			gap: 0.55rem;
			padding: 0.95rem 0;
		}
		.row-label { padding-top: 0; }
		.repo-grid { grid-template-columns: 1fr; }
		.strip { flex-direction: column; align-items: stretch; }
		.strip-actions { justify-content: stretch; }
		.strip-btn { flex: 1; justify-content: center; }
	}
</style>
