<script setup lang="ts">
/**
 * Props-driven color picker composition. Native `<input type="color">`
 * inside a Popover plus a hex Input fallback. Optional `swatches` render
 * as preset chips. Round-trips a lowercased hex string (with leading
 * `#`).
 *
 *   <ColorPicker v-model="color" label="Brand color" />
 */
import { computed, watch } from "vue"
import { Pipette } from "@lucide/vue"
import { Button } from "@/components/ui/button"
import { FieldFrame } from "@/components/ui/field-frame"
import { Input } from "@/components/ui/input"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"

export interface ColorPickerProps {
  modelValue?: string | null
  id?: string
  name?: string
  label?: string
  hideLabel?: boolean
  placeholder?: string
  required?: boolean
  readonly?: boolean
  disabled?: boolean
  helpText?: string
  errors?: string[]
  colorFormat?: "hex" | "rgb" | "hsl"
  swatches?: string[]
}

const props = withDefaults(defineProps<ColorPickerProps>(), {
  errors: () => [],
  swatches: () => [],
})

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

const currentColor = computed<string>(() => {
  const raw = model.value
  if (raw && typeof raw === "string") {
    return normalizeHex(raw)
  }
  return "#000000"
})

// Ensure stored value is normalized once the user picks a hex.
watch(model, (next) => {
  if (typeof next === "string" && next.length > 0) {
    const normalized = normalizeHex(next)
    if (normalized !== next) {
      model.value = normalized
    }
  }
})

function normalizeHex(value: string): string {
  if (!value) {
    return "#000000"
  }
  const trimmed = value.trim().toLowerCase()
  if (/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(trimmed)) {
    return trimmed
  }
  if (/^([0-9a-f]{6})$/.test(trimmed)) {
    return `#${trimmed}`
  }
  return value
}

function setColor(next: string): void {
  model.value = normalizeHex(next)
}

function onNativeInput(event: Event): void {
  const target = event.target as HTMLInputElement | null
  setColor(target?.value ?? "#000000")
}

function onHexInput(value: string | number | undefined): void {
  setColor(typeof value === "string" ? value : "")
}

const swatchList = computed<string[]>(() =>
  Array.isArray(props.swatches) ? props.swatches : [],
)
</script>

<template>
  <FieldFrame
    :label="label"
    :required="required"
    :hide-label="hideLabel"
    :for="id"
    :help-text="helpText"
    :errors="errors"
  >
    <div class="flex items-center gap-2" :data-element-name="name ?? undefined">
      <Popover>
        <PopoverTrigger as-child>
          <Button
            type="button"
            variant="outline"
            size="icon"
            :disabled="disabled"
            aria-label="Choose a color"
            data-testid="color-swatch-trigger"
          >
            <span
              class="size-5 rounded-sm border"
              :style="{ backgroundColor: currentColor }"
            />
          </Button>
        </PopoverTrigger>
        <PopoverContent class="w-auto p-3">
          <div class="flex flex-col gap-3">
            <input
              type="color"
              :value="currentColor"
              :disabled="disabled || readonly"
              class="h-10 w-32 cursor-pointer rounded border"
              data-testid="color-native-input"
              @input="onNativeInput"
            >
            <div v-if="swatchList.length" class="flex flex-wrap gap-2">
              <button
                v-for="swatch in swatchList"
                :key="swatch"
                type="button"
                class="size-6 rounded-sm border ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                :style="{ backgroundColor: swatch }"
                :aria-label="`Use ${swatch}`"
                :disabled="disabled"
                data-testid="color-swatch"
                @click="setColor(swatch)"
              />
            </div>
          </div>
        </PopoverContent>
      </Popover>
      <Input
        :model-value="currentColor"
        :placeholder="placeholder ?? '#000000'"
        :disabled="disabled"
        :readonly="readonly"
        :required="required"
        class="w-32 font-mono uppercase"
        data-testid="color-hex-input"
        spellcheck="false"
        @update:model-value="onHexInput"
      />
      <Pipette
        v-if="!swatchList.length"
        class="size-4 text-muted-foreground"
      />
    </div>
  </FieldFrame>
</template>
