<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?: 'default' | 'primary' | 'ghost' | 'danger' | 'success';
		size?: 'sm' | 'md' | 'lg';
		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 raised = $derived(variant !== 'ghost');
	const accentRaised = $derived(variant !== 'default' && variant !== 'ghost');
	const spinnerSize = $derived(spinnerClass ?? (size === 'lg' ? 'size-5' : 'size-4'));
</script>

{#if href}
	<a
		href={resolve(href as '/')}
		class="btn {className}"
		data-variant={variant}
		data-size={size}
		class:raised
		class:accentRaised
		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="btn {className}"
		data-variant={variant}
		data-size={size}
		class:raised
		class:accentRaised
		aria-busy={loading}
		{...rest}
	>
		{#if loading}
			<Spinner class={spinnerSize} />
		{:else if children}
			{@render children()}
		{/if}
	</button>
{/if}

<style>
	.btn {
		display: inline-flex;
		cursor: pointer;
		align-items: center;
		justify-content: center;
		overflow: hidden;
		border-radius: 0.25rem;
		text-decoration: none;
		transition:
			background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
			border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
			color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		user-select: none;
	}

	.btn:hover {
		text-decoration: none;
	}

	.btn:focus-visible {
		outline: 2px solid var(--color-ring);
		outline-offset: 2px;
	}

	.btn:disabled {
		pointer-events: none;
		opacity: 0.5;
	}

	.btn[data-variant='default'] {
		background-color: var(--color-surface);
		border: 1px solid var(--color-border);
		color: var(--color-fg);
	}
	.btn[data-variant='default']:hover {
		background-color: var(--color-surface-hover);
	}
	.btn[data-variant='default']:focus-visible {
		outline-color: var(--color-ring);
	}

	.btn[data-variant='primary'] {
		background-color: var(--color-primary);
		border: 1px solid var(--color-primary-border);
		color: var(--color-primary-fg);
	}
	.btn[data-variant='primary']:hover {
		background-color: var(--color-primary-hover);
	}
	.btn[data-variant='primary']:focus-visible {
		outline-color: var(--color-primary);
	}

	.btn[data-variant='ghost'] {
		color: var(--color-fg-muted);
		background-color: transparent;
	}
	.btn[data-variant='ghost']:hover {
		background-color: var(--color-surface-muted);
	}
	.btn[data-variant='ghost']:focus-visible {
		outline-color: var(--color-ring);
	}

	.btn[data-variant='danger'] {
		background-color: var(--color-danger);
		border: 1px solid var(--color-danger-border);
		color: var(--color-danger-fg);
	}
	.btn[data-variant='danger']:hover {
		background-color: var(--color-danger-hover);
	}
	.btn[data-variant='danger']:focus-visible {
		outline-color: var(--color-danger);
	}

	.btn[data-variant='success'] {
		background-color: var(--color-success);
		border: 1px solid var(--color-success-border);
		color: var(--color-success-fg);
	}
	.btn[data-variant='success']:hover {
		background-color: var(--color-success-hover);
	}
	.btn[data-variant='success']:focus-visible {
		outline-color: var(--color-success);
	}

	.btn[data-size='sm'] {
		min-height: 2rem;
		padding: 0.375rem 0.5rem;
		font-size: 0.875rem;
		gap: 0.375rem;
	}
	.btn[data-size='md'] {
		min-height: 2.25rem;
		padding: 0.5rem 1rem;
		font-size: 0.875rem;
		gap: 0.5rem;
	}
	.btn[data-size='lg'] {
		min-height: 2.5rem;
		padding: 0.5rem 1.5rem;
		font-size: 1rem;
		gap: 0.625rem;
	}

	.raised {
		position: relative;
		z-index: 0;
	}

	.raised::before {
		content: '';
		position: absolute;
		inset: 0;
		z-index: -10;
		border-radius: inherit;
		box-shadow: inset 0 -2px 0 0 var(--color-raised-depth);
		transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
	}

	.raised:hover::before {
		box-shadow: inset 0 -2px 0 0 var(--color-raised-depth-hover);
	}

	.raised:active {
		transform: translateY(0.125rem);
	}

	.raised:active::before {
		box-shadow: inset 0 2px 2px 0 var(--color-raised-depth);
	}

	.accentRaised::before,
	.accentRaised:hover::before {
		box-shadow: inset 0 -2px 0 0 var(--color-raised-accent-depth);
	}

	.accentRaised:active::before {
		box-shadow: inset 0 2px 2px 0 var(--color-raised-accent-depth-active);
	}

	.raised:disabled:active {
		transform: none;
	}
</style>
