<script lang="ts">
	import { onMount } from 'svelte';
	import { slide } from 'svelte/transition';
	import { api, getBlockedBy, type BlockedByDetail , errMsg} from '$lib/api';
	import BlockedByModal from '$lib/components/BlockedByModal.svelte';
	import { t } from '$lib/i18n';
	import { sanitizePreview } from '$lib/sanitize';
	import { commandTemplateConfigsCache, supportedLocalesCache } from '$lib/stores/caches.svelte';
	import PageHeader from '$lib/components/PageHeader.svelte';
	import Button from '$lib/components/Button.svelte';
	import Card from '$lib/components/Card.svelte';
	import Loading from '$lib/components/Loading.svelte';
	import IconPicker from '$lib/components/IconPicker.svelte';
	import MdiIcon from '$lib/components/MdiIcon.svelte';
	import EmptyState from '$lib/components/EmptyState.svelte';
	import ConfirmModal from '$lib/components/ConfirmModal.svelte';
	import IconButton from '$lib/components/IconButton.svelte';
	import IconGridSelect from '$lib/components/IconGridSelect.svelte';
	import { providerTypeItems as providerTypeItemsFn, providerTypeFilterItems } from '$lib/grid-items';
	import Modal from '$lib/components/Modal.svelte';
	import JinjaEditor from '$lib/components/JinjaEditor.svelte';
	import CollapsibleSlot from '$lib/components/CollapsibleSlot.svelte';
	import Hint from '$lib/components/Hint.svelte';
	import EntitySelect, { type EntityItem } from '$lib/components/EntitySelect.svelte';
	import { getLocaleMeta } from '$lib/locales';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
	import { highlightFromUrl } from '$lib/highlight';
	import { globalProviderFilter } from '$lib/stores/provider-filter.svelte';
	import { getDescriptor } from '$lib/providers';
	import MetaStrip, { type MetaTile } from '$lib/components/MetaStrip.svelte';

	interface CmdTemplateConfig {
		id: number;
		user_id: number;
		provider_type: string;
		name: string;
		description: string;
		icon: string;
		slots: Record<string, Record<string, string>>;
		created_at: string;
	}

	interface SlotDef {
		name: string;
		description: string;
	}

	let LOCALES = $derived(supportedLocalesCache.items);
	let primaryLocale = $derived(LOCALES[0] || 'en');

	let allCmdTplConfigs = $state<CmdTemplateConfig[]>([]);
	let filterText = $state('');
	let filterType = $state('');
	let effectiveType = $derived(globalProviderFilter.providerType || filterType);
	let configs = $derived(allCmdTplConfigs.filter(c =>
		(!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) &&
		(!effectiveType || c.provider_type === effectiveType)
	));
	let loaded = $state(false);
	let showForm = $state(false);
	let editing = $state<number | null>(null);
	let error = $state('');
	let confirmDelete = $state<{ id: number; onconfirm: () => Promise<void> } | null>(null);
	let confirmReset = $state<{
		kind: 'slot' | 'all';
		slotKey?: string;
		message: string;
	} | null>(null);
	let slotPreview = $state<Record<string, string>>({});
	let slotErrors = $state<Record<string, string>>({});
	let slotErrorLines = $state<Record<string, number | null>>({});
	let slotErrorTypes = $state<Record<string, string>>({});
	let validateTimers: Record<string, ReturnType<typeof setTimeout>> = {};
	/** Clean up validate timers on unmount */
	onMount(() => {
		return () => {
			for (const timer of Object.values(validateTimers)) clearTimeout(timer);
		};
	});
	let varsRef = $state<Record<string, any>>({});
	let showVarsFor = $state<string | null>(null);
	let activeLocale = $state<string>('');
	const localeItems = $derived<EntityItem[]>(LOCALES.map((code, i) => {
		const m = getLocaleMeta(code);
		return {
			value: code,
			label: m.native,
			desc: i === 0 ? `${code.toUpperCase()} · ${t('locales.primary')}` : code.toUpperCase(),
		};
	}));
	$effect(() => {
		if (!activeLocale && LOCALES.length > 0) activeLocale = primaryLocale;
	});
	let expandedSlots = $state<Set<string>>(new Set());
	let slotFilter = $state('');
	let showPreviewFor = $state<Set<string>>(new Set());

	function toggleSlot(key: string) {
		const next = new Set(expandedSlots);
		if (next.has(key)) next.delete(key); else next.add(key);
		expandedSlots = next;
	}

	function togglePreview(key: string) {
		const next = new Set(showPreviewFor);
		if (next.has(key)) next.delete(key); else next.add(key);
		showPreviewFor = next;
	}

	function getSlotStatus(key: string): 'empty' | 'valid' | 'error' | 'warning' {
		if (slotErrors[key] && slotErrorTypes[key] !== 'undefined') return 'error';
		if (slotErrors[key] && slotErrorTypes[key] === 'undefined') return 'warning';
		if (getSlotValue(key)) return 'valid';
		return 'empty';
	}

	const defaultForm = () => ({
		provider_type: '',
		name: '',
		description: '',
		icon: '',
		slots: {} as Record<string, Record<string, string>>,
	});
	let form = $state(defaultForm());
	let nameManuallyEdited = $state(false);

	$effect(() => {
		if (showForm && !nameManuallyEdited && !editing) {
			const desc = getDescriptor(form.provider_type);
			form.name = desc ? `${desc.defaultName} Command Templates` : 'Command Templates';
		}
	});

	// Provider capabilities
	let allCapabilities = $state<Record<string, any>>({});
	let providerTypes = $derived(Object.keys(allCapabilities));
	let commandSlots = $derived<SlotDef[]>(
		allCapabilities[form.provider_type]?.command_slots || []
	);

	const ERROR_SLOTS = new Set(['rate_limited', 'no_results']);

	/**
	 * Group command slots by purpose so the form mirrors how notification
	 * templates are split (event vs scheduled vs settings).
	 *
	 *   commandResponses  — primary reply templates (/start, /help, /status, data slots)
	 *   commandErrors     — fallback messages (rate_limited, no_results)
	 *   commandDescriptions — desc_* slots: short menu blurbs in Telegram's command picker
	 *   commandUsage      — usage_* slots: invocation examples shown by /help
	 */
	let commandSlotGroups = $derived([
		{
			group: 'commandResponses',
			slots: commandSlots.filter(s =>
				!s.name.startsWith('desc_') &&
				!s.name.startsWith('usage_') &&
				!ERROR_SLOTS.has(s.name)
			),
		},
		{
			group: 'commandErrors',
			slots: commandSlots.filter(s => ERROR_SLOTS.has(s.name)),
		},
		{
			group: 'commandDescriptions',
			slots: commandSlots.filter(s => s.name.startsWith('desc_')),
		},
		{
			group: 'commandUsage',
			slots: commandSlots.filter(s => s.name.startsWith('usage_')),
		},
	]);

	/** Get slot template for current locale, with fallback. */
	function getSlotValue(slotName: string): string {
		return form.slots[slotName]?.[activeLocale] || '';
	}

	/** Resolve variable reference for a slot, preferring provider-specific over shared. */
	function getVarsFor(slotName: string) {
		const providerVars = varsRef[form.provider_type];
		return providerVars?.[slotName] ?? varsRef[slotName];
	}

	let modalVars = $derived(showVarsFor ? getVarsFor(showVarsFor) : null);

	/** Set slot template for current locale (immutable update). */
	function setSlotValue(slotName: string, value: string) {
		form.slots = {
			...form.slots,
			[slotName]: { ...(form.slots[slotName] || {}), [activeLocale]: value }
		};
	}

	onMount(load);

	const headerPills = $derived.by(() => {
		const pills: Array<{ label: string; tone: 'sky' }> = [];
		const types = new Set(configs.map(c => c.provider_type)).size;
		if (types > 0) pills.push({ label: `${types} ${types === 1 ? t('providers.typeSingular') : t('providers.typePlural')}`, tone: 'sky' });
		return pills;
	});

	async function load() {
		try {
			const [cfgs, caps, vars] = await Promise.all([
				commandTemplateConfigsCache.fetch(true),
				api('/providers/capabilities'),
				api('/command-template-configs/variables'),
				supportedLocalesCache.fetch(),
			]);
			allCmdTplConfigs = cfgs;
			allCapabilities = caps;
			varsRef = vars;
		} catch (err: unknown) {
			error = errMsg(err, t('common.loadError'));
			snackError(error);
		} finally {
			loaded = true;
			highlightFromUrl();
		}
	}

	function validateSlot(slotKey: string, template: string, immediate = false) {
		if (validateTimers[slotKey]) clearTimeout(validateTimers[slotKey]);
		if (!template) {
			slotErrors = { ...slotErrors, [slotKey]: '' };
			slotErrorLines = { ...slotErrorLines, [slotKey]: null };
			slotErrorTypes = { ...slotErrorTypes, [slotKey]: '' };
			const { [slotKey]: _, ...rest } = slotPreview;
			slotPreview = rest;
			return;
		}

		const doValidate = async () => {
			try {
				const res = await api('/command-template-configs/preview-raw', {
					method: 'POST',
					body: JSON.stringify({ template }),
				});
				slotErrors = { ...slotErrors, [slotKey]: res.error || '' };
				slotErrorLines = { ...slotErrorLines, [slotKey]: res.error_line || null };
				slotErrorTypes = { ...slotErrorTypes, [slotKey]: res.error_type || '' };
				if (res.rendered) {
					slotPreview = { ...slotPreview, [slotKey]: res.rendered };
				} else {
					const { [slotKey]: _, ...rest } = slotPreview;
					slotPreview = rest;
				}
			} catch {
				slotErrors = { ...slotErrors, [slotKey]: '' };
			}
		};
		if (immediate) doValidate();
		else validateTimers[slotKey] = setTimeout(doValidate, 800);
	}

	function refreshAllPreviews() {
		for (const slot of commandSlots) {
			const template = getSlotValue(slot.name);
			if (template) validateSlot(slot.name, template, true);
		}
	}

	function cmdTemplateConfigTiles(config: CmdTemplateConfig): MetaTile[] {
		const tiles: MetaTile[] = [];
		tiles.push({
			icon: 'mdiServer',
			label: config.provider_type,
			tone: 'lavender',
			mono: true,
		});
		const slotCount = Object.keys(config.slots || {}).length;
		tiles.push({
			icon: 'mdiViewGridOutline',
			value: String(slotCount),
			label: t('templateConfig.slots'),
			tone: slotCount > 0 ? 'sky' : 'default',
		});
		const locales = new Set<string>();
		for (const s of Object.values(config.slots || {})) {
			for (const loc of Object.keys(s || {})) locales.add(loc);
		}
		if (locales.size > 0) {
			tiles.push({
				icon: 'mdiTranslate',
				value: String(locales.size),
				label: locales.size === 1 ? t('locales.label') : t('locales.labelPlural'),
				hint: [...locales].sort().join(', '),
				tone: 'mint',
			});
		}
		if (config.user_id === 0) {
			tiles.push({
				icon: 'mdiShieldStarOutline',
				label: t('common.system'),
				tone: 'orchid',
			});
		}
		return tiles;
	}

	function openNew() {
		form = defaultForm();
		const typesWithCmdSlots = providerTypes.filter(t => (allCapabilities[t]?.command_slots?.length || 0) > 0);
		if (typesWithCmdSlots.length > 0) form.provider_type = typesWithCmdSlots[0];
		nameManuallyEdited = false;
		editing = null;
		showForm = true;
		activeLocale = primaryLocale;
		slotPreview = {};
		slotErrors = {};
		expandedSlots = new Set();
		showPreviewFor = new Set();
		slotFilter = '';
	}

	function edit(c: CmdTemplateConfig) {
		// Deep copy nested slots
		const slotsCopy: Record<string, Record<string, string>> = {};
		for (const [k, v] of Object.entries(c.slots)) {
			slotsCopy[k] = { ...v };
		}
		form = {
			provider_type: c.provider_type,
			name: c.name,
			description: c.description || '',
			icon: c.icon || '',
			slots: slotsCopy,
		};
		nameManuallyEdited = true;
		editing = c.id;
		showForm = true;
		activeLocale = primaryLocale;
		slotPreview = {};
		slotErrors = {};
		expandedSlots = new Set();
		showPreviewFor = new Set();
		slotFilter = '';
		setTimeout(() => refreshAllPreviews(), 100);
	}

	async function save(e: SubmitEvent) {
		e.preventDefault();
		error = '';
		try {
			if (editing) {
				await api(`/command-template-configs/${editing}`, { method: 'PUT', body: JSON.stringify(form) });
			} else {
				await api('/command-template-configs', { method: 'POST', body: JSON.stringify(form) });
			}
			showForm = false;
			editing = null;
			await load();
			snackSuccess(t('snack.cmdTemplateSaved'));
		} catch (err: unknown) {
			const m = errMsg(err);
			error = m;
			snackError(m);
		}
	}

	function resetSlotToDefault(slotKey: string) {
		if (!form.provider_type) return;
		confirmReset = {
			kind: 'slot',
			slotKey,
			message: t('templateConfig.resetSlotConfirm').replace('{locale}', activeLocale.toUpperCase()),
		};
	}

	function resetAllToDefaults() {
		if (!form.provider_type) return;
		confirmReset = {
			kind: 'all',
			message: t('templateConfig.resetAllConfirm').replace(/\{locale\}/g, activeLocale.toUpperCase()),
		};
	}

	async function performReset() {
		if (!confirmReset || !form.provider_type) return;
		const { kind, slotKey } = confirmReset;
		confirmReset = null;
		try {
			if (kind === 'slot' && slotKey) {
				const res = await api<Record<string, Record<string, string>>>(
					`/command-template-configs/defaults?provider_type=${encodeURIComponent(form.provider_type)}&slot_name=${encodeURIComponent(slotKey)}&locale=${encodeURIComponent(activeLocale)}`,
				);
				const text = res?.[slotKey]?.[activeLocale];
				if (!text) {
					snackError(t('templateConfig.resetNoDefault'));
					return;
				}
				setSlotValue(slotKey, text);
				validateSlot(slotKey, text, true);
			} else {
				const res = await api<Record<string, Record<string, string>>>(
					`/command-template-configs/defaults?provider_type=${encodeURIComponent(form.provider_type)}&locale=${encodeURIComponent(activeLocale)}`,
				);
				const nextSlots = { ...form.slots };
				for (const [key, localeMap] of Object.entries(res || {})) {
					const text = localeMap?.[activeLocale];
					if (text === undefined) continue;
					nextSlots[key] = { ...(nextSlots[key] || {}), [activeLocale]: text };
				}
				form.slots = nextSlots;
				refreshAllPreviews();
			}
			snackSuccess(t('templateConfig.resetApplied'));
		} catch (err: unknown) {
			snackError(errMsg(err));
		}
	}

	function clone(c: CmdTemplateConfig) {
		const slotsCopy: Record<string, Record<string, string>> = {};
		for (const [k, v] of Object.entries(c.slots)) {
			slotsCopy[k] = { ...v };
		}
		form = {
			provider_type: c.provider_type,
			name: `${c.name} (Copy)`,
			description: c.description || '',
			icon: c.icon || '',
			slots: slotsCopy,
		};
		editing = null;
		showForm = true;
		activeLocale = primaryLocale;
		slotPreview = {};
		slotErrors = {};
		expandedSlots = new Set();
		showPreviewFor = new Set();
		slotFilter = '';
		setTimeout(() => refreshAllPreviews(), 100);
	}

	let blockedBy = $state<BlockedByDetail | null>(null);
	function remove(id: number) {
		confirmDelete = {
			id,
			onconfirm: async () => {
				try {
					await api(`/command-template-configs/${id}`, { method: 'DELETE' });
					await load();
					snackSuccess(t('snack.cmdTemplateDeleted'));
				} catch (err: unknown) {
					const bb = getBlockedBy(err);
					if (bb) { blockedBy = bb; return; }
					const m = errMsg(err);
					error = m;
					snackError(m);
				} finally {
					confirmDelete = null;
				}
			},
		};
	}
</script>

<PageHeader
	title={t('cmdTemplateConfig.title')}
	emphasis={t('cmdTemplateConfig.titleEmphasis')}
	description={t('cmdTemplateConfig.description')}
	crumb={t('crumbs.routingCommands')}
	count={configs.length}
	countLabel={t('cmdTemplateConfig.countLabel')}
	pills={headerPills}
>
	<Button size="sm" onclick={() => { showForm ? (showForm = false, editing = null) : openNew(); }}>
		{showForm ? t('common.cancel') : t('cmdTemplateConfig.newConfig')}
	</Button>
</PageHeader>

{#if !loaded}<Loading />{:else}

{#if showForm}
<div in:slide={{ duration: 200 }}>
<Card class="mb-6">
	{#if error}<div class="bg-[var(--color-error-bg)] text-[var(--color-error-fg)] text-sm rounded-md p-3 mb-4">{error}</div>{/if}
	<form onsubmit={save} class="space-y-5">
		<div>
			<label for="ct-name" class="block text-sm font-medium mb-1">{t('cmdTemplateConfig.name')}</label>
			<div class="flex gap-2">
				<IconPicker value={form.icon} onselect={(v: string) => form.icon = v} />
				<input id="ct-name" bind:value={form.name} oninput={() => nameManuallyEdited = true} required placeholder={t('cmdTemplateConfig.namePlaceholder')}
					class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
			</div>
		</div>
		<div>
			<label for="ct-desc" class="block text-sm font-medium mb-1">{t('common.description')}</label>
			<input id="ct-desc" bind:value={form.description} placeholder={t('cmdTemplateConfig.descriptionPlaceholder')}
				class="w-full px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
		</div>

		{#if !editing}
		<div>
			<div class="block text-sm font-medium mb-1">{t('templateConfig.providerType')}</div>
			<IconGridSelect items={providerTypeItemsFn()} bind:value={form.provider_type} columns={2} />
		</div>
		{:else}
		<div>
			<span class="block text-sm font-medium mb-1">{t('templateConfig.providerType')}</span>
			<span class="text-sm text-[var(--color-muted-foreground)]">{allCapabilities[form.provider_type]?.display_name || form.provider_type}</span>
		</div>
		{/if}

		<!-- Language picker -->
		<div class="flex items-center gap-2 mb-3">
			<span class="text-xs font-medium text-[var(--color-muted-foreground)] shrink-0">
				{t('templateConfig.language')}
			</span>
			<div class="flex-1 max-w-xs">
				<EntitySelect
					items={localeItems}
					value={activeLocale}
					size="sm"
					onselect={(v) => { activeLocale = (v as string) || primaryLocale; refreshAllPreviews(); }}
				/>
			</div>
			{#if form.provider_type}
				<button type="button" onclick={resetAllToDefaults}
					title={t('templateConfig.resetAllToDefaults')}
					class="ml-auto flex items-center gap-1 text-xs px-2 py-1 rounded-md border border-[var(--color-border)] text-[var(--color-muted-foreground)] hover:bg-[var(--color-muted)]">
					<MdiIcon name="mdiRefresh" size={12} />
					{t('templateConfig.resetAllToDefaults')}
				</button>
			{/if}
		</div>

		<!-- Slot filter -->
		{#if commandSlots.length > 4}
		<div>
			<input type="text" bind:value={slotFilter} placeholder={t('templateConfig.filterSlots')}
				class="w-full px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
		</div>
		{/if}

		{#each commandSlotGroups.filter(g => g.slots.length > 0) as group}
			{@const filteredSlots = slotFilter ? group.slots.filter(s => s.name.toLowerCase().includes(slotFilter.toLowerCase()) || s.description.toLowerCase().includes(slotFilter.toLowerCase())) : group.slots}
			{#if filteredSlots.length > 0}
			<fieldset class="border border-[var(--color-border)] rounded-md p-3">
				<legend class="text-sm font-medium px-1">
					{t(`cmdTemplateConfig.${group.group}`)}<Hint text={t(`hints.${group.group}`)} />
				</legend>
				<div class="space-y-2 mt-2">
					{#each filteredSlots as slot}
						<CollapsibleSlot
							label={slot.name}
							description="/{slot.name} — {slot.description}"
							expanded={expandedSlots.has(slot.name)}
							status={getSlotStatus(slot.name)}
							ontoggle={() => toggleSlot(slot.name)}
						>
							<div class="flex items-center justify-end gap-2 mb-2">
								{#if slotPreview[slot.name] && !slotErrors[slot.name]}
									<button type="button" onclick={() => togglePreview(slot.name)}
										class="text-xs px-2 py-0.5 rounded-md transition-colors {showPreviewFor.has(slot.name) ? 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]' : 'text-[var(--color-muted-foreground)] hover:bg-[var(--color-muted)]'}">
										{t('templateConfig.preview')}
									</button>
								{/if}
								{#if getVarsFor(slot.name)}
									<button type="button" onclick={() => showVarsFor = slot.name}
										class="text-xs text-[var(--color-muted-foreground)] hover:underline">{t('templateConfig.variables')}</button>
								{/if}
								<button type="button" onclick={() => resetSlotToDefault(slot.name)}
									title={t('templateConfig.resetToDefault')}
									class="text-xs text-[var(--color-muted-foreground)] hover:underline">
									{t('templateConfig.resetToDefault')}
								</button>
							</div>

							{#if showPreviewFor.has(slot.name) && slotPreview[slot.name] && !slotErrors[slot.name]}
								<div class="p-2 bg-[var(--color-muted)] rounded text-sm mb-2">
									<pre class="whitespace-pre-wrap text-xs">{@html sanitizePreview(slotPreview[slot.name])}</pre>
								</div>
							{:else}
								<JinjaEditor
									value={getSlotValue(slot.name)}
									onchange={(v: string) => { setSlotValue(slot.name, v); validateSlot(slot.name, v); }}
									rows={3}
									errorLine={slotErrorLines[slot.name] || null}
									variables={getVarsFor(slot.name) || undefined}
								/>
							{/if}

							{#if slotErrors[slot.name]}
								{#if slotErrorTypes[slot.name] === 'undefined'}
									<p class="mt-1 text-xs" style="color: var(--color-warning-fg);">⚠ {t('common.undefinedVar')}: {slotErrors[slot.name]}</p>
								{:else}
									<p class="mt-1 text-xs" style="color: var(--color-error-fg);">РІСљвЂў {t('common.syntaxError')}: {slotErrors[slot.name]}{slotErrorLines[slot.name] ? ` (${t('common.line')} ${slotErrorLines[slot.name]})` : ''}</p>
								{/if}
							{/if}
						</CollapsibleSlot>
					{/each}
				</div>
			</fieldset>
			{/if}
		{/each}

		<button type="submit" class="px-4 py-2 bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-md text-sm font-medium hover:opacity-90">
			{editing ? t('common.save') : t('common.create')}
		</button>
	</form>
</Card>
</div>
{/if}

{#if !showForm && allCmdTplConfigs.length > 0}
<div class="flex items-center gap-2 mb-3">
	<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
		class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
	{#if !globalProviderFilter.id}
	<div class="w-48">
		<IconGridSelect items={providerTypeFilterItems()} bind:value={filterType} columns={2} compact />
	</div>
	{/if}
</div>
{/if}

{#if allCmdTplConfigs.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiConsoleLine" message={t('cmdTemplateConfig.noConfigs')} />
	</Card>
{:else if configs.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiFilterOff" message={t('common.noFilterResults')} />
	</Card>
{:else}
	<div class="list-stack stagger-children">
		{#each configs as config}
			<Card hover entityId={config.id}>
				<div class="list-row">
					<div class="list-row__identity">
						<div class="flex items-center gap-2 min-w-0">
							<span style="color: var(--color-primary);" class="shrink-0"><MdiIcon name={config.icon || 'mdiConsoleLine'} size={20} /></span>
							<p class="font-medium truncate">{config.name}</p>
							<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)] shrink-0">{config.provider_type}</span>
							{#if config.user_id === 0}
								<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)] shrink-0">{t('common.system')}</span>
							{/if}
						</div>
						{#if config.description}
							<p class="text-sm text-[var(--color-muted-foreground)] mt-1 list-row__secondary">{config.description}</p>
						{/if}
					</div>
					<MetaStrip tiles={cmdTemplateConfigTiles(config)} />
					<div class="list-row__actions">
						<IconButton icon="mdiContentCopy" title={t('common.clone')} onclick={() => clone(config)} />
						<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => edit(config)} />
						<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => remove(config.id)} variant="danger" />
					</div>
				</div>
			</Card>
		{/each}
	</div>
{/if}

{/if}

<ConfirmModal open={confirmDelete !== null} message={t('cmdTemplateConfig.confirmDelete')}
	onconfirm={() => confirmDelete?.onconfirm()} oncancel={() => confirmDelete = null} />

<ConfirmModal open={confirmReset !== null}
	title={t('templateConfig.resetToDefault')}
	message={confirmReset?.message || ''}
	confirmLabel={confirmReset?.kind === 'all' ? t('templateConfig.resetAllToDefaults') : t('templateConfig.resetToDefault')}
	confirmIcon="mdiRefresh"
	onconfirm={performReset}
	oncancel={() => confirmReset = null} />

<BlockedByModal open={!!blockedBy} detail={blockedBy} onclose={() => blockedBy = null} />

<!-- Variables reference modal -->
<Modal open={showVarsFor !== null} title="{t('templateConfig.variables')}: /{showVarsFor || ''}" onclose={() => showVarsFor = null}>
	{#if showVarsFor && modalVars}
		<p class="text-sm text-[var(--color-muted-foreground)] mb-3">{modalVars.description}</p>
		<div class="space-y-1">
			<p class="text-xs font-medium mb-1">{t('templateConfig.variables')}:</p>
			{#each Object.entries(modalVars.variables || {}) as [name, desc]}
				<div class="flex items-start gap-2 text-sm">
					<code class="text-xs bg-[var(--color-muted)] px-1 py-0.5 rounded font-mono whitespace-nowrap">{'{{ ' + name + ' }}'}</code>
					<span class="text-xs text-[var(--color-muted-foreground)]">{desc}</span>
				</div>
			{/each}
		</div>
		{#each [
			['asset_fields', 'asset', 'Asset fields'],
			['album_fields', 'album', 'Album fields'],
			['command_fields', 'cmd', 'Command fields'],
			['event_fields', 'event', 'Event fields'],
			['repo_fields', 'repo', 'Repository fields'],
			['issue_fields', 'issue', 'Issue fields'],
			['pr_fields', 'pr', 'Pull request fields'],
			['commit_fields', 'c', 'Commit fields'],
			['board_fields', 'board', 'Board fields'],
			['card_fields', 'card', 'Card fields'],
			['list_fields', 'lst', 'List fields'],
			['device_fields', 'd', 'Device fields'],
		] as [fieldKey, prefix, title]}
			{#if modalVars[fieldKey]}
				<div class="mt-3 pt-3 border-t border-[var(--color-border)]">
					<p class="text-xs font-medium mb-1">{title} <span class="font-normal text-[var(--color-muted-foreground)]">(use {prefix}.field)</span>:</p>
					{#each Object.entries(modalVars[fieldKey]) as [name, desc]}
						<div class="flex items-start gap-2 text-sm">
							<code class="text-xs bg-[var(--color-muted)] px-1 py-0.5 rounded font-mono whitespace-nowrap">{'{{ ' + prefix + '.' + name + ' }}'}</code>
							<span class="text-xs text-[var(--color-muted-foreground)]">{desc}</span>
						</div>
					{/each}
				</div>
			{/if}
		{/each}
	{/if}
</Modal>
