<script lang="ts">
	import ChevronsUpDownIcon from '@lucide/svelte/icons/chevrons-up-down';
	import type { ComponentProps } from 'svelte';
	import { tick } from 'svelte';
	import { Button } from '$lib/components/ui/button/index.js';
	import * as Command from '$lib/components/ui/command/index.js';
	import * as Popover from '$lib/components/ui/popover/index.js';
	import { cn } from '$lib/utils.js';

	type Option = {
		value: string;
		label: string;
	};

	const emptyValue = '__form-combobox-empty__';

	let {
		name,
		value = $bindable(''),
		options,
		placeholder = 'Select an option',
		searchPlaceholder = 'Search...',
		emptyMessage = 'No results found.',
		triggerProps,
		onValueChange
	}: {
		name: string;
		value: string;
		options: readonly Option[];
		placeholder?: string;
		searchPlaceholder?: string;
		emptyMessage?: string;
		triggerProps?: ComponentProps<typeof Button>;
		onValueChange?: (value: string) => void;
	} = $props();

	let open = $state(false);
	let triggerRef = $state<HTMLButtonElement>(null!);
	const selectedLabel = $derived(
		options.find((option) => option.value === value)?.label ?? placeholder
	);

	function closeAndFocusTrigger() {
		open = false;
		tick().then(() => triggerRef.focus());
	}
</script>

<Popover.Root bind:open>
	<Popover.Trigger bind:ref={triggerRef}>
		{#snippet child({ props })}
			<Button
				{...props}
				{...triggerProps}
				type="button"
				variant="outline"
				role="combobox"
				aria-expanded={open}
				class={cn(
					'w-full justify-between font-normal',
					!value && 'text-muted-foreground',
					triggerProps?.class
				)}
			>
				{selectedLabel}
				<ChevronsUpDownIcon class="ml-2 size-4 shrink-0 opacity-50" />
			</Button>
		{/snippet}
	</Popover.Trigger>
	<Popover.Content class="w-[var(--bits-popover-anchor-width)] p-0" align="start">
		<Command.Root>
			<Command.Input placeholder={searchPlaceholder} />
			<Command.List>
				<Command.Empty>{emptyMessage}</Command.Empty>
				<Command.Group>
					{#each options as option (option.value)}
						<Command.Item
							value={option.value || emptyValue}
							keywords={[option.label]}
							data-checked={value === option.value}
							onSelect={() => {
								value = option.value;
								onValueChange?.(option.value);
								closeAndFocusTrigger();
							}}
						>
							{option.label}
						</Command.Item>
					{/each}
				</Command.Group>
			</Command.List>
		</Command.Root>
	</Popover.Content>
</Popover.Root>

<input type="hidden" {name} {value} />
