<!--
    Component: Button
    design-system button. Uppercase, zero border-radius, Space Grotesk.
-->
<script lang="ts">
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { HTMLButtonAttributes } from 'svelte/elements';
import type {
    ButtonSize,
    ButtonVariant,
    IconPosition,
} from './types';

interface Props extends HTMLButtonAttributes {
    /**
     * Visual style variant
     * @default 'secondary'
     */
    variant?: ButtonVariant;
    /**
     * Button size
     * @default 'md'
     */
    size?: ButtonSize;
    /**
     * Icon snippet
     */
    icon?: Snippet;
    /**
     * Icon placement
     * @default 'left'
     */
    iconPosition?: IconPosition;
    /**
     * Active toggle state
     * @default false
     */
    active?: boolean;
    /**
     * Tap animation
     * Primary uses translate, others use scale
     * @default true
     */
    animate?: boolean;
    /**
     * Content snippet
     */
    children?: Snippet;
    /**
     * CSS classes
     */
    class?: string;
}

let {
    variant = 'secondary',
    size = 'md',
    icon,
    iconPosition = 'left',
    active = false,
    animate = true,
    children,
    class: className,
    type = 'button',
    disabled,
    ...rest
}: Props = $props();

// Square sizing when icon is present but there is no text label
const isIconOnly = $derived(!!icon && !children);

const variantStyles: Record<ButtonVariant, string> = {
    primary: clsx(
        'bg-swiss-red text-white',
        'hover:bg-swiss-red/90',
        'active:bg-swiss-red/80',
        'border border-swiss-red',
        'shadow-[0.125rem_0.125rem_0_0_rgba(0,0,0,0.1)]',
        'hover:shadow-[0.1875rem_0.1875rem_0_0_rgba(0,0,0,0.15)]',
        'active:shadow-[0.0625rem_0.0625rem_0_0_rgba(0,0,0,0.08)]',
        'active:translate-x-[0.0625rem] active:translate-y-[0.0625rem]',
        'disabled:bg-neutral-300 dark:disabled:bg-neutral-700',
        'disabled:text-neutral-500 dark:disabled:text-neutral-500',
        'disabled:border-neutral-300 dark:disabled:border-neutral-700',
        'disabled:shadow-none',
        'disabled:cursor-not-allowed',
        'disabled:transform-none',
    ),
    secondary: clsx(
        'bg-surface dark:bg-paper',
        'text-swiss-black dark:text-neutral-200',
        'border border-black/10 dark:border-white/10',
        'hover:bg-paper dark:hover:bg-neutral-800',
        'hover:shadow-sm',
        'active:bg-neutral-100 dark:active:bg-neutral-700',
        'disabled:bg-neutral-100 dark:disabled:bg-neutral-800',
        'disabled:text-neutral-400 dark:disabled:text-neutral-600',
        'disabled:cursor-not-allowed',
    ),
    outline: clsx(
        'bg-transparent',
        'text-swiss-black dark:text-neutral-200',
        'border border-black/20 dark:border-white/20',
        'hover:bg-surface dark:hover:bg-paper',
        'hover:border-brand',
        'active:bg-paper dark:active:bg-neutral-700',
        'disabled:border-neutral-300 dark:disabled:border-neutral-700',
        'disabled:text-neutral-400 dark:disabled:text-neutral-600',
        'disabled:cursor-not-allowed',
    ),
    ghost: clsx(
        'bg-transparent',
        'text-secondary',
        'border border-transparent',
        'hover:bg-transparent dark:hover:bg-transparent',
        'hover:text-brand dark:hover:text-brand',
        'active:bg-transparent dark:active:bg-transparent',
        'disabled:text-neutral-400 dark:disabled:text-neutral-600',
        'disabled:cursor-not-allowed',
    ),
    icon: clsx(
        'bg-surface dark:bg-dark-bg',
        'text-secondary',
        'border border-transparent',
        'hover:bg-paper dark:hover:bg-paper',
        'hover:text-brand',
        'hover:border-black/5 dark:hover:border-white/10',
        'active:bg-paper dark:active:bg-neutral-700',
        'disabled:text-neutral-400 dark:disabled:text-neutral-600',
        'disabled:cursor-not-allowed',
    ),
    tertiary: clsx(
        // Font override — must come after base in clsx() to win via tailwind-merge
        'font-secondary font-medium normal-case tracking-normal',
        // Inactive state
        'bg-transparent',
        'text-neutral-400 dark:text-neutral-400',
        'border border-transparent',
        // Hover (inactive) — semi-transparent lift, no bg-paper token
        'hover:bg-paper/50 dark:hover:bg-dark-card/50',
        'hover:text-neutral-900 dark:hover:text-neutral-100',
        'hover:border-black/5 dark:hover:border-white/10',
        // Press
        'active:bg-paper/70 dark:active:bg-dark-card/70',
        // Disabled
        'disabled:text-neutral-300 dark:disabled:text-neutral-600',
        'disabled:cursor-not-allowed',
    ),
};

const sizeStyles: Record<ButtonSize, string> = {
    xs: 'h-6  px-2 text-3xs gap-1',
    sm: 'h-8  px-3 text-2xs gap-1.5',
    md: 'h-10 px-4 text-xs  gap-2',
    lg: 'h-12 px-6 text-xs  gap-2',
    xl: 'h-14 px-8 text-sm  gap-2.5',
};

// Square padding for icon-only mode
const iconSizeStyles: Record<ButtonSize, string> = {
    xs: 'h-6  w-6  p-1',
    sm: 'h-8  w-8  p-1.5',
    md: 'h-10 w-10 p-2',
    lg: 'h-12 w-12 p-2.5',
    xl: 'h-14 w-14 p-3',
};

const activeStyles: Partial<Record<ButtonVariant, string>> = {
    secondary: 'bg-paper dark:bg-paper shadow-sm border-black/20 dark:border-white/20',
    tertiary:
        'bg-paper dark:bg-dark-card border-black/10 dark:border-white/10 shadow-sm text-neutral-900 dark:text-neutral-100',
    ghost: 'bg-transparent dark:bg-transparent text-brand dark:text-brand',
    outline: 'bg-surface dark:bg-paper border-brand',
    icon: 'bg-paper dark:bg-paper text-brand border-subtle',
};

const classes = $derived(clsx(
    // Base
    'inline-flex items-center justify-center',
    'font-primary font-bold tracking-tight uppercase',
    'rounded-none',
    'transition-all duration-200',
    'select-none',
    'outline-none',
    'cursor-pointer',
    'focus-ring',
    'focus-visible:ring-offset-surface dark:focus-visible:ring-offset-dark-bg',
    'disabled:cursor-not-allowed disabled:pointer-events-none',
    // Variant
    variantStyles[variant],
    // Size (square when icon-only)
    isIconOnly ? iconSizeStyles[size] : sizeStyles[size],
    // Animate (CSS tap scale — excluded for primary which uses translate instead)
    animate && !disabled && variant !== 'primary' && 'active:scale-[0.97]',
    // Active override
    active && activeStyles[variant],
    // Consumer override
    className,
));
</script>

<button
    {type}
    {disabled}
    class={classes}
    {...rest}
>
    {#if icon && iconPosition === 'left'}
        <span class="shrink-0 flex items-center justify-center">
            {@render icon()}
        </span>
    {/if}

    {#if children}
        <span class="whitespace-nowrap leading-none">
            {@render children()}
        </span>
    {/if}

    {#if icon && iconPosition === 'right'}
        <span class="shrink-0 flex items-center justify-center">
            {@render icon()}
        </span>
    {/if}
</button>
