<script setup lang="ts">
/**
 * Props-driven button-group composition. Renders an array of child
 * actions as a row of Buttons, each capable of emitting a click or
 * navigating to its href.
 *
 *   <ButtonGroupAction
 *     :items="[
 *       { label: 'Edit', emit: 'edit' },
 *       { label: 'Delete', variant: 'destructive', emit: 'delete' },
 *     ]"
 *     @action="onAction"
 *   />
 */
import { computed } from "vue"
import * as LucideIcons from "@lucide/vue"
import type { FunctionalComponent } from "vue"

type LucideIcon = FunctionalComponent

import { Button, type ButtonVariants } from "@/components/ui/button"
import type {
  ButtonActionSize,
  ButtonActionVariant,
} from "./ButtonAction.vue"

export interface ButtonGroupItem {
  id?: string
  label: string
  /** Lucide icon name rendered before the label. */
  icon?: string
  /** Navigation target — renders as an anchor when set. */
  href?: string
  target?: "_self" | "_blank" | "_parent" | "_top"
  /** Visual variant; falls back to the group's variant prop. */
  variant?: ButtonActionVariant
  /** Custom event name emitted via `action` when clicked. */
  emit?: string
  disabled?: boolean
}

export interface ButtonGroupActionProps {
  items?: ButtonGroupItem[]
  /** Default variant applied to items that don't specify one. */
  variant?: ButtonActionVariant
  size?: ButtonActionSize
  /** Render the group stretched (each child becomes flex-1). */
  block?: boolean
  /** Group HTML id. */
  id?: string
  /** Group name (used as `data-element-name`). */
  name?: string
}

const props = withDefaults(defineProps<ButtonGroupActionProps>(), {
  items: () => [],
  variant: "default",
  size: "md",
})

const emit = defineEmits<{
  (
    e: "action",
    payload: { name: string; item: ButtonGroupItem },
  ): void
  (e: "click", event: MouseEvent, item: ButtonGroupItem): void
}>()

const sizeMap: Record<ButtonActionSize, ButtonVariants["size"]> = {
  sm: "sm",
  md: "default",
  lg: "lg",
}

const mappedSize = computed<ButtonVariants["size"]>(
  () => sizeMap[props.size] ?? "default",
)

const iconLibrary = LucideIcons as unknown as Record<string, LucideIcon>
function resolveIcon(name: string | undefined): LucideIcon | undefined {
  if (!name) {
    return undefined
  }
  const pascal = name
    .split(/[-_\s]/)
    .filter(Boolean)
    .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
    .join("")
  return iconLibrary[pascal] ?? iconLibrary[name]
}

function variantFor(item: ButtonGroupItem): ButtonActionVariant {
  return item.variant ?? props.variant
}

function onClick(child: ButtonGroupItem, event: MouseEvent): void {
  emit("click", event, child)
  if (child.href) {
    return
  }
  event.preventDefault()
  if (child.emit) {
    emit("action", { name: child.emit, item: child })
  }
}
</script>

<template>
  <div
    :id="id"
    class="inline-flex flex-wrap items-center gap-2"
    role="group"
    :data-element-name="name ?? undefined"
  >
    <Button
      v-for="(child, idx) in items"
      :key="child.id ?? `${child.label}-${idx}`"
      :as="child.href ? 'a' : 'button'"
      :href="child.href"
      :target="child.href ? child.target : undefined"
      :variant="variantFor(child)"
      :size="mappedSize"
      :disabled="child.disabled"
      :class="block ? 'flex-1' : ''"
      :data-item-emit="child.emit"
      @click="(e: MouseEvent) => onClick(child, e)"
    >
      <component :is="resolveIcon(child.icon)" v-if="resolveIcon(child.icon)" class="h-4 w-4" />
      <span>{{ child.label }}</span>
    </Button>
  </div>
</template>
