<script lang="ts">
	import { onMount } from 'svelte';
	import { api, getBlockedBy, type BlockedByDetail , errMsg} from '$lib/api';
	import BlockedByModal from '$lib/components/BlockedByModal.svelte';
	import { t } from '$lib/i18n';
	import { commandConfigsCache, commandTemplateConfigsCache, capabilitiesCache } from '$lib/stores/caches.svelte';
	import PageHeader from '$lib/components/PageHeader.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 CrossLink from '$lib/components/CrossLink.svelte';
	import IconGridSelect from '$lib/components/IconGridSelect.svelte';
	import { providerTypeItems, providerTypeFilterItems, responseModeItems } from '$lib/grid-items';
	import EntitySelect from '$lib/components/EntitySelect.svelte';
	import Button from '$lib/components/Button.svelte';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
	import { highlightFromUrl } from '$lib/highlight';
	import { globalProviderFilter } from '$lib/stores/provider-filter.svelte';
	import ErrorBanner from '$lib/components/ErrorBanner.svelte';
	import { getDescriptor } from '$lib/providers';
	import MetaStrip, { type MetaTile } from '$lib/components/MetaStrip.svelte';
	import type { CommandConfig } from '$lib/types';

	function templateName(id: number | null): string {
		if (!id) return '';
		const tpl = cmdTemplateConfigs.find((c) => c.id === id);
		return tpl?.name || `#${id}`;
	}

	let allCmdConfigs = $derived(commandConfigsCache.items);
	let filterText = $state('');
	let filterType = $state('');
	let effectiveType = $derived(globalProviderFilter.providerType || filterType);
	let configs = $derived(allCmdConfigs.filter(c =>
		(!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) &&
		(!effectiveType || c.provider_type === effectiveType)
	));
	let cmdTemplateConfigs = $derived(commandTemplateConfigsCache.items);
	const templateItems = $derived(cmdTemplateConfigs
		.filter((c) => c.provider_type === form.provider_type)
		.map((c) => ({ value: c.id, label: c.name + (c.user_id === 0 ? t('common.systemSuffix') : ''), icon: c.icon || 'mdiCodeBracesBox', desc: c.provider_type }))
	);
	let loaded = $state(false);
	let showForm = $state(false);
	let editing = $state<number | null>(null);
	let error = $state('');
	let submitting = $state(false);
	let confirmDelete = $state<{ id: number; onconfirm: () => Promise<void> } | null>(null);

	// Immich command icons — used as fallback when capabilities don't specify icons
	const commandIcons: Record<string, string> = {
		help: 'mdiHelpCircle', status: 'mdiChartBox', albums: 'mdiImageMultiple',
		events: 'mdiPulse', summary: 'mdiFileDocumentEdit', latest: 'mdiImagePlus',
		memory: 'mdiHistory', random: 'mdiDice3', search: 'mdiMagnify',
		find: 'mdiFileSearch', person: 'mdiAccount', place: 'mdiMapMarker',
		favorites: 'mdiStar', people: 'mdiAccountGroup',
	};

	const defaultForm = () => ({
		name: '',
		icon: '',
		provider_type: '',
		enabled_commands: [] as string[],
		response_mode: 'text',
		default_count: 5,
		rate_limits: { search: 30, default: 10 },
		command_template_config_id: null as number | null,
	});
	let form = $state(defaultForm());
	let nameManuallyEdited = $state(false);

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

	let allCapabilities = $derived(capabilitiesCache.items);
	let providerCommands = $derived<{key: string, icon: string}[]>(
		(allCapabilities[form.provider_type]?.commands || []).map((c: { name: string }) => ({
			key: c.name,
			icon: commandIcons[c.name] || 'mdiConsole',
		}))
	);
	let hasCommands = $derived(providerCommands.length > 0);

	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 {
			await Promise.all([
				commandConfigsCache.fetch(true),
				commandTemplateConfigsCache.fetch(),
				capabilitiesCache.fetch(),
			]);
		} catch (err: unknown) { error = errMsg(err, t('common.loadError')); snackError(error); }
		finally { loaded = true; highlightFromUrl(); }
	}

	function commandConfigTiles(cfg: CommandConfig): MetaTile[] {
		const tiles: MetaTile[] = [];
		tiles.push({
			icon: 'mdiServer',
			label: cfg.provider_type,
			tone: 'lavender',
			mono: true,
		});
		const cmdCount = (cfg.enabled_commands || []).length;
		tiles.push({
			icon: 'mdiSlashForward',
			value: String(cmdCount),
			label: t('commandConfig.commands'),
			tone: cmdCount > 0 ? 'mint' : 'coral',
		});
		tiles.push({
			icon: cfg.response_mode === 'media' ? 'mdiImageOutline' : 'mdiTextBoxOutline',
			label: cfg.response_mode === 'media' ? t('commandConfig.modeMedia') : t('commandConfig.modeText'),
			tone: 'sky',
		});
		tiles.push({
			icon: 'mdiNumeric',
			value: String(cfg.default_count),
			label: t('commandConfig.defaultCount'),
			tone: 'citrus',
		});
		if (cfg.command_template_config_id) {
			tiles.push({
				icon: 'mdiCodeBracesBox',
				label: templateName(cfg.command_template_config_id),
				tone: 'orchid',
			});
		}
		return tiles;
	}

	function openNew() {
		form = defaultForm();
		// Auto-select first provider type with commands
		const types = Object.keys(allCapabilities).filter(t => (allCapabilities[t]?.commands?.length || 0) > 0);
		if (types.length > 0) form.provider_type = types[0];
		// Auto-select first matching template for the chosen provider_type
		const match = cmdTemplateConfigs.find((c) => c.provider_type === form.provider_type);
		if (match) form.command_template_config_id = match.id;
		nameManuallyEdited = false;
		editing = null;
		showForm = true;
	}

	// Re-pick the command-template config when the provider type changes.
	// The previously-selected id may belong to a different provider type and
	// would no longer appear in the filtered EntitySelect, leaving it empty.
	let _prevProviderType = $state('');
	$effect(() => {
		if (showForm && form.provider_type && form.provider_type !== _prevProviderType) {
			_prevProviderType = form.provider_type;
			if (editing === null) {
				const currentTpl = cmdTemplateConfigs.find(
					(c) => c.id === form.command_template_config_id,
				);
				if (!currentTpl || currentTpl.provider_type !== form.provider_type) {
					const first = cmdTemplateConfigs.find(
						(c) => c.provider_type === form.provider_type,
					);
					form.command_template_config_id = first?.id ?? null;
				}
			}
		}
	});
	function editConfig(cfg: CommandConfig) {
		form = {
			name: cfg.name,
			icon: cfg.icon || '',
			provider_type: cfg.provider_type || '',
			enabled_commands: [...(cfg.enabled_commands || [])],
			response_mode: cfg.response_mode || 'media',
			default_count: cfg.default_count ?? 5,
			rate_limits: { search: cfg.rate_limits?.search ?? 30, default: cfg.rate_limits?.default ?? 10 },
			command_template_config_id: cfg.command_template_config_id ?? null,
		};
		nameManuallyEdited = true;
		editing = cfg.id;
		showForm = true;
	}

	function toggleCmd(cmd: string) {
		const enabled = [...form.enabled_commands];
		const idx = enabled.indexOf(cmd);
		if (idx >= 0) enabled.splice(idx, 1);
		else enabled.push(cmd);
		form.enabled_commands = enabled;
	}

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

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

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

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

{#if showForm}
	<Card class="mb-6">
		{#if error}<ErrorBanner message={error} />{/if}
		<form onsubmit={saveConfig} class="space-y-4">
			<div>
				<label for="cfg-name" class="block text-sm font-medium mb-1">{t('commandConfig.name')}</label>
				<div class="flex gap-2">
					<IconPicker value={form.icon} onselect={(v: string) => form.icon = v} />
					<input id="cfg-name" bind:value={form.name} oninput={() => nameManuallyEdited = true} required placeholder={t('commandConfig.namePlaceholder')}
						class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
				</div>
			</div>

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

			{#if hasCommands}
			<!-- Enabled commands -->
			<div>
				<p class="text-sm font-medium mb-2">{t('commandConfig.enabledCommands')}</p>
				<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-1">
					{#each providerCommands as cmd}
						<label class="flex items-center gap-1.5 text-xs cursor-pointer hover:bg-[var(--color-muted)] px-2 py-1 rounded">
							<input type="checkbox" checked={form.enabled_commands.includes(cmd.key)}
								onchange={() => toggleCmd(cmd.key)} />
							<MdiIcon name={cmd.icon} size={14} />
							/{cmd.key}
						</label>
					{/each}
				</div>
			</div>

			<div>
				<div class="block text-sm font-medium mb-1">{t('commandConfig.responseTemplate')}</div>
				<EntitySelect items={templateItems} bind:value={form.command_template_config_id} placeholder={t('commandConfig.responseTemplate')} />
			</div>

			<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
				<div>
					<div class="block text-xs mb-1">{t('commandConfig.responseMode')}</div>
					<IconGridSelect items={responseModeItems(t)} bind:value={form.response_mode} columns={2} compact />
				</div>
				<div>
					<label for="cfg-default-count" class="block text-xs mb-1">{t('commandConfig.defaultCount')}</label>
					<input id="cfg-default-count" type="number" bind:value={form.default_count} min="1" max="20"
						class="w-full px-2 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)]" />
				</div>
				<div>
					<label for="cfg-search-cooldown" class="block text-xs mb-1">{t('commandConfig.searchCooldown')}</label>
					<input id="cfg-search-cooldown" type="number" bind:value={form.rate_limits.search} min="0" max="300"
						class="w-full px-2 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)]" />
				</div>
			</div>

			<div class="w-1/2 sm:w-1/4">
				<label for="cfg-default-cooldown" class="block text-xs mb-1">{t('commandConfig.defaultCooldown')}</label>
				<input id="cfg-default-cooldown" type="number" bind:value={form.rate_limits.default} min="0" max="300"
					class="w-full px-2 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)]" />
			</div>
			{:else}
			<div class="text-sm text-[var(--color-muted-foreground)] p-3 border border-[var(--color-border)] rounded-md">
				{t('commandConfig.noCommandsForProvider')}
			</div>
			{/if}

			<Button type="submit" disabled={submitting}>
				{submitting ? t('common.loading') : (editing ? t('common.save') : t('common.create'))}
			</Button>
		</form>
	</Card>
{/if}

{#if !showForm && allCmdConfigs.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 allCmdConfigs.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiConsoleLine" message={t('commandConfig.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 cfg}
			<Card hover entityId={cfg.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={cfg.icon || 'mdiConsoleLine'} size={20} /></span>
							<p class="font-medium truncate">{cfg.name}</p>
							<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)] font-mono shrink-0">{cfg.provider_type}</span>
						</div>
						<div class="flex items-center gap-2 mt-0.5 list-row__secondary">
							<span class="text-xs text-[var(--color-muted-foreground)]">
								{(cfg.enabled_commands || []).length} {t('commandConfig.commands')}
								&middot; {t('commandConfig.responseMode')}: {cfg.response_mode === 'media' ? t('commandConfig.modeMedia') : t('commandConfig.modeText')}
								&middot; {t('commandConfig.defaultCount')}: {cfg.default_count}
							</span>
							{#if cfg.command_template_config_id}
								<CrossLink href="/command-template-configs" icon="mdiCodeBracesBox" label={templateName(cfg.command_template_config_id)} entityId={cfg.command_template_config_id} />
							{/if}
						</div>
					</div>
					<MetaStrip tiles={commandConfigTiles(cfg)} />
					<div class="list-row__actions">
						<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => editConfig(cfg)} />
						<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => remove(cfg)} variant="danger" />
					</div>
				</div>
			</Card>
		{/each}
	</div>
{/if}

{/if}

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

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