<script setup lang="ts">
/**
 * The WYSIWYG canvas. Renders the label as an SVG whose viewBox is in
 * millimetres (1 user unit = 1 mm), so element coordinates pass straight
 * through and match the PDF. Owns pointer interaction: select / drag / resize
 * against the shared design model (injected `ldDesigner`). Client-only —
 * mounted inside <ClientOnly> by the page.
 */
import type { LdElement } from '~/composables/useLabelDesign'

const props = withDefaults(defineProps<{ zoom?: number; showGrid?: boolean }>(), {
  zoom: 2,
  showGrid: true
})

const designer = inject('ldDesigner') as ReturnType<typeof useLabelDesign>
const { template, selectedId, selected } = designer

const PX_PER_MM = 96 / 25.4
const svgRef = ref<SVGSVGElement | null>(null)

const w = computed(() => template.value.size.wMm)
const h = computed(() => template.value.size.hMm)
const pxPerMm = computed(() => PX_PER_MM * props.zoom)
const pxW = computed(() => Math.round(w.value * pxPerMm.value))
const pxH = computed(() => Math.round(h.value * pxPerMm.value))

const gridStep = 10
const gridX = computed(() => { const a: number[] = []; for (let i = gridStep; i < w.value; i += gridStep) a.push(i); return a })
const gridY = computed(() => { const a: number[] = []; for (let i = gridStep; i < h.value; i += gridStep) a.push(i); return a })

const localPoint = (e: PointerEvent) => {
  const svg = svgRef.value
  const ctm = svg?.getScreenCTM()
  if (!svg || !ctm) return { x: 0, y: 0 }
  const p = new DOMPoint(e.clientX, e.clientY).matrixTransform(ctm.inverse())
  return { x: p.x, y: p.y }
}

// --- interaction state (non-reactive; read inside handlers) ---
const MIN = 3
let mode: null | 'drag' | 'resize' = null
let activeId: string | null = null
let handle: string | null = null
let startGeom = { x: 0, y: 0, w: 0, h: 0 }
let pointerStart = { x: 0, y: 0 }

const beginDrag = (e: PointerEvent, el: LdElement) => {
  if (el.locked) return
  designer.select(el.id)
  designer.record()
  mode = 'drag'; activeId = el.id; handle = null
  startGeom = { x: el.x, y: el.y, w: el.w, h: el.h }
  pointerStart = localPoint(e)
}

const beginResize = (name: string, e: PointerEvent) => {
  const el = selected.value
  if (!el || el.locked) return
  designer.record()
  mode = 'resize'; activeId = el.id; handle = name
  startGeom = { x: el.x, y: el.y, w: el.w, h: el.h }
  pointerStart = localPoint(e)
}

const onBackgroundDown = () => designer.select(null)

const doDrag = (p: { x: number; y: number }) => {
  const dx = p.x - pointerStart.x
  const dy = p.y - pointerStart.y
  let nx = startGeom.x + dx
  let ny = startGeom.y + dy
  nx = clamp(nx, -(startGeom.w - 5), w.value - 5)
  ny = clamp(ny, -(startGeom.h - 5), h.value - 5)
  designer.updateElement(activeId as string, { x: round2(nx), y: round2(ny) }, false)
}

const doResize = (p: { x: number; y: number }) => {
  const g = startGeom
  if (handle === 'p1' || handle === 'p2') { // line endpoints
    if (handle === 'p1') {
      designer.updateElement(activeId as string, {
        x: round2(p.x), y: round2(p.y),
        w: round2(g.x + g.w - p.x), h: round2(g.y + g.h - p.y)
      }, false)
    } else {
      designer.updateElement(activeId as string, { w: round2(p.x - g.x), h: round2(p.y - g.y) }, false)
    }
    return
  }
  let left = g.x; let right = g.x + g.w; let top = g.y; let bottom = g.y + g.h
  if ((handle as string).includes('w')) left = Math.min(p.x, right - MIN)
  if ((handle as string).includes('e')) right = Math.max(p.x, left + MIN)
  if ((handle as string).includes('n')) top = Math.min(p.y, bottom - MIN)
  if ((handle as string).includes('s')) bottom = Math.max(p.y, top + MIN)
  designer.updateElement(activeId as string, {
    x: round2(left), y: round2(top), w: round2(right - left), h: round2(bottom - top)
  }, false)
}

const onMove = (e: PointerEvent) => {
  if (!mode || !activeId) return
  const p = localPoint(e)
  if (mode === 'drag') doDrag(p)
  else doResize(p)
}
const onUp = () => { mode = null; activeId = null; handle = null }

// --- in-place text editing (double-click a text element) ---
const MM_PER_PT = 25.4 / 72
const FONT_CSS: Record<string, string> = {
  helvetica: 'Helvetica, Arial, sans-serif',
  times: '"Times New Roman", Times, serif',
  courier: '"Courier New", Courier, monospace'
}
const editingId = ref<string | null>(null)
const editAreaRef = ref<HTMLTextAreaElement | null>(null)
let editDirty = false

const editingEl = computed(() => template.value.elements.find(e => e.id === editingId.value) || null)

// All styling inline so it never depends on scoped-CSS reaching the
// foreignObject subtree. Font sizing in px == mm (1 CSS px == 1 SVG user unit
// inside foreignObject), matching the rendered <text> exactly.
const editStyle = computed(() => {
  const base: Record<string, any> = {
    width: '100%', height: '100%', boxSizing: 'border-box',
    margin: '0', padding: '0', border: 'none', outline: '1px solid #3b82f6',
    resize: 'none', overflow: 'hidden', background: 'rgba(255,255,255,0.92)',
    whiteSpace: 'pre-wrap', wordBreak: 'break-word'
  }
  const el: any = editingEl.value
  if (!el || el.type !== 'text') return base
  return {
    ...base,
    fontFamily: FONT_CSS[el.fontFamily] || FONT_CSS.helvetica,
    fontSize: `${(el.fontSizePt || 11) * MM_PER_PT}px`,
    lineHeight: String(el.lineHeight || 1.15),
    color: el.color || '#000000',
    textAlign: el.align || 'left',
    fontWeight: (el.fontStyle === 'bold' || el.fontStyle === 'bolditalic') ? 700 : 400,
    fontStyle: (el.fontStyle === 'italic' || el.fontStyle === 'bolditalic') ? 'italic' : 'normal',
    letterSpacing: `${(el.letterSpacingPt || 0) * MM_PER_PT}px`
  }
})

const enterEdit = (el: LdElement) => {
  if (el.type !== 'text' || el.locked) return
  designer.select(el.id)
  editDirty = false
  editingId.value = el.id
}
const onEditInput = (e: Event) => {
  const id = editingId.value
  if (!id) return
  if (!editDirty) { designer.record(); editDirty = true }
  designer.updateElement(id, { text: (e.target as HTMLTextAreaElement).value }, false)
}
const endEdit = () => { editingId.value = null }

watch(editingId, async (id) => {
  if (!id) return
  await nextTick()
  const ta = editAreaRef.value
  if (ta) { ta.focus(); ta.select() }
})

onMounted(() => {
  window.addEventListener('pointermove', onMove)
  window.addEventListener('pointerup', onUp)
})
onBeforeUnmount(() => {
  window.removeEventListener('pointermove', onMove)
  window.removeEventListener('pointerup', onUp)
})
</script>

<template>
  <div class="ldStage">
    <svg
      ref="svgRef"
      :width="pxW"
      :height="pxH"
      :viewBox="`0 0 ${w} ${h}`"
      class="ldStage__svg"
      @pointerdown.self="onBackgroundDown"
    >
      <!-- label background -->
      <rect
        x="0" y="0" :width="w" :height="h"
        :fill="template.background || '#FFFFFF'"
        stroke="#c9c9cf" :stroke-width="0.15"
        @pointerdown="onBackgroundDown"
      />

      <!-- grid -->
      <g v-if="showGrid" pointer-events="none" stroke="#e3e3e8" :stroke-width="0.1">
        <line v-for="gx in gridX" :key="'gx' + gx" :x1="gx" y1="0" :x2="gx" :y2="h" />
        <line v-for="gy in gridY" :key="'gy' + gy" x1="0" :y1="gy" :x2="w" :y2="gy" />
      </g>

      <!-- elements (array order == z-order) -->
      <g
        v-for="el in template.elements"
        :key="el.id"
        :style="{ cursor: el.locked ? 'default' : 'move' }"
        @pointerdown.stop="beginDrag($event, el)"
        @dblclick.stop="enterEdit(el)"
      >
        <template v-if="el.id !== editingId">
          <IntegrationsLabeldesignerElementRenderer :el="el" />
          <!-- transparent hit target so the whole element is grabbable -->
          <line
            v-if="el.type === 'line'"
            :x1="el.x" :y1="el.y" :x2="el.x + el.w" :y2="el.y + el.h"
            stroke="transparent" :stroke-width="Math.max(el.strokeWidthMm || 0.3, 3)" stroke-linecap="round"
          />
          <rect v-else :x="el.x" :y="el.y" :width="el.w" :height="el.h" fill="transparent" />
        </template>
      </g>

      <!-- in-place text editor (double-click a text element) -->
      <foreignObject
        v-if="editingEl && editingEl.type === 'text'"
        :x="editingEl.x" :y="editingEl.y" :width="editingEl.w" :height="editingEl.h"
        @pointerdown.stop
        @dblclick.stop
      >
        <textarea
          ref="editAreaRef"
          :value="editingEl.text"
          :style="editStyle"
          @input="onEditInput"
          @blur="endEdit"
          @keydown.escape.stop.prevent="endEdit"
          @pointerdown.stop
        ></textarea>
      </foreignObject>

      <!-- selection + resize handles (hidden while editing text) -->
      <IntegrationsLabeldesignerTransformHandles
        v-if="selected && !editingId"
        :el="selected"
        :pxPerMm="pxPerMm"
        @handledown="beginResize"
      />
    </svg>
  </div>
</template>

<style scoped>
.ldStage {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 24px;
  min-height: 100%;
  background:
    repeating-conic-gradient(#f2f2f4 0% 25%, #ffffff 0% 50%) 50% / 16px 16px;
}
.ldStage__svg {
  background: #ffffff;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.18);
  touch-action: none;
}
</style>
