<script setup lang="ts">
/**
 * Props-driven barcode display.
 *
 * Generates a barcode SVG from `value` using the `jsbarcode` library,
 * lazy-imported on mount. Default format is CODE128; supply alternates
 * (EAN13, UPC, ...) via the `format` prop.
 */
import { computed, onMounted, ref, useTemplateRef, watch } from "vue"

export interface BarcodeDisplayProps {
  value?: string | number | null
  format?: string
  classList?: string[] | string
}

const props = withDefaults(defineProps<BarcodeDisplayProps>(), {
  format: "CODE128",
})

const content = computed<string>(() => {
  if (props.value === undefined || props.value === null) {
    return ""
  }
  return String(props.value)
})

const rootClass = computed<string>(() => {
  const cl = props.classList
  if (!cl) {
    return ""
  }
  return Array.isArray(cl) ? cl.join(" ") : cl
})

const svgRef = useTemplateRef<SVGSVGElement>("svgRef")
const error = ref<string>("")

async function renderBarcode(): Promise<void> {
  if (!svgRef.value || !content.value) {
    return
  }
  try {
    const mod = await import("jsbarcode")
    const JsBarcode = (mod as unknown as { default: (el: SVGElement | HTMLElement, value: string, options?: Record<string, unknown>) => void }).default
    JsBarcode(svgRef.value, content.value, {
      format: props.format,
      displayValue: true,
      margin: 4,
    })
    error.value = ""
  } catch (e) {
    error.value = e instanceof Error ? e.message : "Failed to render barcode"
  }
}

onMounted(() => {
  void renderBarcode()
})

watch(
  () => [content.value, props.format],
  () => {
    void renderBarcode()
  },
)
</script>

<template>
  <div
    class="inline-block"
    :class="rootClass"
    data-testid="barcode-display"
  >
    <svg
      ref="svgRef"
      role="img"
      :aria-label="`Barcode: ${content}`"
    />
    <p
      v-if="error"
      class="mt-1 text-xs text-destructive"
      data-testid="barcode-error"
    >
      {{ error }}
    </p>
  </div>
</template>
