<template>
  <component
    :is="tag"
    :class="classes"
    v-bind="mergedAttrs"
    @click="onClick"
  >
    <Loader2 v-if="loading" :size="iconSize" class="animate-spin" />
    <component :is="icon" v-else-if="icon" :size="iconSize" />
    <slot />
  </component>
</template>

<script setup lang="ts">
// Bouton unifié : variantes + tailles + état loading + anneau de focus cohérent.
// Rend un <RouterLink> quand `to` est fourni (et actionnable), sinon un <button>.
import { computed, useAttrs, type Component } from 'vue';
import { RouterLink, type RouteLocationRaw } from 'vue-router';
import { Loader2 } from '@lucide/vue';

defineOptions({ inheritAttrs: false });

const props = withDefaults(
  defineProps<{
    variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
    size?: 'sm' | 'md';
    loading?: boolean;
    disabled?: boolean;
    iconOnly?: boolean;
    icon?: Component;
    to?: RouteLocationRaw;
    type?: 'button' | 'submit';
  }>(),
  { variant: 'secondary', size: 'md', type: 'button' },
);

const emit = defineEmits<{ click: [MouseEvent] }>();
const attrs = useAttrs();

const isDisabled = computed(() => props.disabled || props.loading);
const asLink = computed(() => props.to !== undefined && !isDisabled.value);
const tag = computed(() => (asLink.value ? RouterLink : 'button'));
const iconSize = computed(() => (props.size === 'sm' ? 15 : 16));

const BASE =
  'inline-flex items-center justify-center gap-1.5 rounded-lg font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950 disabled:cursor-not-allowed disabled:opacity-50';

const VARIANTS: Record<NonNullable<typeof props.variant>, string> = {
  primary: 'bg-emerald-600 text-white hover:bg-emerald-500 focus-visible:ring-emerald-500/70',
  secondary: 'border border-zinc-700 bg-zinc-800 text-zinc-200 hover:bg-zinc-700 focus-visible:ring-emerald-500/70',
  ghost: 'text-zinc-300 hover:bg-zinc-800/60 focus-visible:ring-emerald-500/70',
  danger: 'border border-red-900 bg-red-950 text-red-300 hover:bg-red-900 focus-visible:ring-red-500/70',
};

const SIZES = {
  sm: { text: 'h-8 px-2.5 text-xs', icon: 'h-8 w-8' },
  md: { text: 'h-9 px-3 text-sm', icon: 'h-9 w-9' },
};

const classes = computed(() => {
  const size = SIZES[props.size];
  return [BASE, VARIANTS[props.variant], props.iconOnly ? size.icon : size.text];
});

// Attributs natifs propres au tag rendu (button : type/disabled/aria-busy ; lien : to).
const nativeProps = computed(() => {
  if (asLink.value) return { to: props.to };
  return { type: props.type, disabled: isDisabled.value, 'aria-busy': props.loading || undefined };
});
// useAttrs (class/aria-label/etc. du parent) + props natifs.
const mergedAttrs = computed(() => ({ ...attrs, ...nativeProps.value }));

function onClick(e: MouseEvent): void {
  if (isDisabled.value) {
    e.preventDefault();
    return;
  }
  emit('click', e);
}
</script>
