<script module lang="ts">
	import { tv, type VariantProps } from "tailwind-variants";

	export const button = tv({
		base: "inline-flex cursor-pointer select-none items-center justify-center overflow-hidden rounded no-underline transition-[background-color,border-color,color] duration-150 ease-[cubic-bezier(0.4,0,0.2,1)] hover:no-underline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50",
		variants: {
			variant: {
				default:
					"border border-border-default bg-background-default text-foreground-default hover:bg-background-subtle focus-visible:outline-border-focus",
				primary:
					"border border-border-success bg-background-success-emphasis text-foreground-on-emphasis hover:bg-background-success-emphasis-hover focus-visible:outline-background-success-emphasis",
				ghost:
					"bg-transparent text-foreground-muted hover:bg-background-inset focus-visible:outline-border-focus",
				flat: "bg-gray-50 text-foreground-default hover:bg-gray-100 focus-visible:outline-border-focus dark:bg-gray-900 dark:hover:bg-gray-700",
				// danger / success / warning are outline buttons in Figma: default surface +
				// border, colored text only (bound to the fg-* tokens), no accent fill.
				danger:
					"border border-border-default bg-background-default text-foreground-danger hover:bg-background-subtle focus-visible:outline-foreground-danger",
				success:
					"border border-border-default bg-background-default text-foreground-success hover:bg-background-subtle focus-visible:outline-foreground-success",
				warning:
					"border border-border-default bg-background-default text-foreground-warning hover:bg-background-subtle focus-visible:outline-foreground-warning"
			},
			size: {
				sm: "min-h-8 gap-1.5 px-2 py-1.5 text-sm",
				md: "min-h-9 gap-2 px-3 py-1.5 text-sm",
				lg: "min-h-10 gap-2.5 px-6 py-2 text-base"
			}
		},
		compoundVariants: [
			// raised variants draw inset depth via a ::before layer.
			{
				variant: "default",
				class:
					"relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] before:content-[''] before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-subtle)] before:transition-all before:duration-150 hover:before:shadow-[inset_0_-2px_0_0_var(--color-shadow-inner-strong-hover)] active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_var(--color-shadow-inner-subtle)] disabled:active:translate-y-0"
			},
			{
				variant: "primary",
				class:
					"relative z-0 before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] before:content-[''] before:shadow-[inset_0_-2px_0_0_rgb(3_7_18/0.25)] before:transition-all before:duration-150 active:translate-y-0.5 active:before:shadow-[inset_0_2px_2px_0_rgb(3_7_18/0.5)] disabled:active:translate-y-0"
			}
		],
		defaultVariants: {
			variant: "default",
			size: "md"
		}
	});

	export type ButtonVariants = VariantProps<typeof button>;
</script>

<script lang="ts">
	import { resolve } from "$app/paths";
	import type { Snippet } from "svelte";
	import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
	import Spinner from "./Spinner.svelte";

	interface Props extends Omit<HTMLAnchorAttributes & HTMLButtonAttributes, "class" | "children"> {
		variant?: ButtonVariants["variant"];
		size?: ButtonVariants["size"];
		href?: string;
		type?: "button" | "submit" | "reset";
		disabled?: boolean;
		class?: string;
		loading?: boolean;
		spinnerClass?: string;
		children?: Snippet;
	}

	let {
		variant = "default",
		size = "md",
		href,
		type = "button",
		disabled = false,
		class: className,
		loading = false,
		spinnerClass,
		children,
		...rest
	}: Props = $props();

	const classes = $derived(button({ variant, size, class: className }));
	const spinnerSize = $derived(spinnerClass ?? (size === "lg" ? "size-5" : "size-4"));
</script>

{#if href}
	<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -- internal hrefs go through resolve(), external ones must pass through untouched -->
	<a
		href={href?.startsWith("/") ? resolve(href as "/") : href}
		class={classes}
		aria-disabled={disabled || loading ? "true" : undefined}
		tabindex={disabled ? -1 : undefined}
		{...rest}
	>
		{#if loading}
			<Spinner class={spinnerSize} />
		{:else if children}
			{@render children()}
		{/if}
	</a>
{:else}
	<button {type} disabled={disabled || loading} class={classes} aria-busy={loading} {...rest}>
		{#if loading}
			<Spinner class={spinnerSize} />
		{:else if children}
			{@render children()}
		{/if}
	</button>
{/if}
