<script setup lang="ts">
/**
 * Props-driven inline icon picker composition. A trigger Button +
 * Popover with a searchable grid of lucide icons.
 *
 * Self-contained — usable directly in any Vue app:
 *
 *   <IconPickerInline v-model="iconName" label="Pick an icon" />
 */
import { computed, h, ref, type Component } from "vue"
import * as LucideIcons from "@lucide/vue"
import { Search } from "@lucide/vue"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { FieldFrame } from "@/components/ui/field-frame"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"

export interface IconPickerInlineProps {
  modelValue?: string
  id?: string
  name?: string
  label?: string
  hideLabel?: boolean
  required?: boolean
  disabled?: boolean
  helpText?: string
  errors?: string[]
}

withDefaults(defineProps<IconPickerInlineProps>(), {
  errors: () => [],
})

const model = defineModel<string>("modelValue")

const query = ref<string>("")

/**
 * Curated subset of lucide icons. Hardcoded so we don't enumerate
 * the entire ~1400-icon set in the picker UI.
 */
const ICON_NAMES: readonly string[] = [
  "Activity",
  "AlertCircle",
  "Archive",
  "Bell",
  "Bookmark",
  "Calendar",
  "Camera",
  "Check",
  "Clock",
  "Cloud",
  "Code",
  "Download",
  "Edit",
  "Eye",
  "File",
  "Flag",
  "Folder",
  "Globe",
  "Heart",
  "Home",
  "Image",
  "Info",
  "Link",
  "Lock",
  "Mail",
  "MapPin",
  "MessageSquare",
  "Plus",
  "Search",
  "Settings",
  "Star",
  "Tag",
  "Trash",
  "Upload",
  "User",
  "Video",
  "X",
  "Zap",
]

const filtered = computed<readonly string[]>(() => {
  const q = query.value.trim().toLowerCase()
  if (!q) {
    return ICON_NAMES
  }
  return ICON_NAMES.filter((name) => name.toLowerCase().includes(q))
})

function resolveIcon(name: string): Component | undefined {
  const registry = LucideIcons as unknown as Record<string, Component | undefined>
  return registry[name]
}

function renderIcon(name: string, cls = "size-4") {
  const icon = resolveIcon(name)
  return icon ? h(icon, { class: cls }) : null
}

const previewIcon = computed<Component | undefined>(() =>
  resolveIcon(model.value ?? ""),
)

function onSelect(name: string): void {
  model.value = name
}
</script>

<template>
  <FieldFrame
    :label="label"
    :required="required"
    :hide-label="hideLabel"
    :for="id"
    :help-text="helpText"
    :errors="errors"
    data-testid="icon-picker-inline"
  >
    <Popover>
      <PopoverTrigger as-child>
        <Button
          type="button"
          variant="outline"
          class="w-fit justify-start gap-2"
          :disabled="disabled"
          :data-element-name="name ?? undefined"
          data-testid="icon-picker-inline-trigger"
        >
          <component
            :is="previewIcon"
            v-if="previewIcon"
            class="size-4"
          />
          <span>{{ model || "Pick an icon" }}</span>
        </Button>
      </PopoverTrigger>
      <PopoverContent class="w-72 p-2">
        <div class="mb-2 flex items-center gap-2">
          <Search class="size-4 text-muted-foreground" />
          <Input
            v-model="query"
            placeholder="Search icons…"
            data-testid="icon-picker-inline-search"
            class="h-8"
          />
        </div>
        <div
          class="grid max-h-60 grid-cols-6 gap-1 overflow-auto"
          data-testid="icon-picker-inline-grid"
        >
          <button
            v-for="iconName in filtered"
            :key="iconName"
            type="button"
            class="flex aspect-square items-center justify-center rounded border border-transparent hover:border-border hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            :class="model === iconName ? 'border-primary bg-primary/10' : ''"
            :aria-label="iconName"
            :title="iconName"
            :data-testid="`icon-picker-inline-item-${iconName}`"
            @click="onSelect(iconName)"
          >
            <component :is="renderIcon(iconName)" />
          </button>
        </div>
        <p
          v-if="!filtered.length"
          class="p-3 text-center text-sm text-muted-foreground"
        >
          No icons match.
        </p>
      </PopoverContent>
    </Popover>
  </FieldFrame>
</template>
