<!--
  Toggle switch replacing raw checkboxes. Accepts an `ariaLabel` /
  `ariaLabelledby` so the switch announces its purpose to assistive
  tech regardless of surrounding markup. `label` remains a thin alias
  that defaults aria-label without forcing a visible <label>.
-->
<script lang="ts">
	interface Props {
		checked?: boolean;
		/** Accessible name. Falls back to ariaLabel for clarity. */
		label?: string;
		/** Explicit aria-label override. Takes precedence over label. */
		ariaLabel?: string;
		/** id of a visible label element. Takes precedence over ariaLabel. */
		ariaLabelledby?: string;
		disabled?: boolean;
		onchange?: (checked: boolean) => void;
	}

	let {
		checked = $bindable(false),
		label = '',
		ariaLabel,
		ariaLabelledby,
		disabled = false,
		onchange
	}: Props = $props();

	function toggle() {
		if (disabled) return;
		checked = !checked;
		onchange?.(checked);
	}

	function handleKey(e: KeyboardEvent) {
		if (disabled) return;
		// Native <button> already handles Space/Enter, but role="switch"
		// best-practice docs (WAI-ARIA APG) call out Space explicitly —
		// pin the behaviour so we don't depend on browser defaults.
		if (e.key === ' ' || e.key === 'Enter') {
			e.preventDefault();
			toggle();
		}
	}

	const computedAriaLabel = $derived(ariaLabel ?? (label || undefined));
</script>

<button
	type="button"
	role="switch"
	aria-checked={checked}
	aria-label={computedAriaLabel}
	aria-labelledby={ariaLabelledby}
	aria-disabled={disabled ? 'true' : undefined}
	class="toggle-switch {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
	onclick={toggle}
	onkeydown={handleKey}
	{disabled}
>
	{#if label}
		<span class="sr-only">{label}</span>
	{/if}
</button>
