/**
 * Label Designer — design model, element factories, geometry + undo/redo.
 *
 * The single source of truth for a label is a `LabelTemplate`: a physical size
 * (mm) plus an ordered list of elements (array order == z-order, last on top).
 * The SAME model is consumed by the on-screen SVG renderer
 * (`IntegrationsLabeldesignerStage`) and by the PDF compiler
 * (`usePdfCompiler`), so what the user designs prints true-to-size.
 *
 * All geometry is in millimetres, origin top-left, `rotation` in degrees
 * clockwise about the element's box centre. SSR-safe — no browser APIs here.
 *
 * `useLabelDesign()` returns a fresh, page-local controller (NOT useState) —
 * one editor instance per page mount.
 */

export type LdElementType =
  | 'text' | 'barcode' | 'qr' | 'datamatrix' | 'image' | 'rect' | 'ellipse' | 'line'

export interface LdBaseElement {
  id: string
  type: LdElementType
  name?: string
  x: number; y: number; w: number; h: number
  rotation: number
  locked?: boolean
  visible?: boolean
}

export interface LdTextElement extends LdBaseElement {
  type: 'text'
  text: string
  fontFamily: 'helvetica' | 'times' | 'courier'
  fontSizePt: number
  fontStyle: 'normal' | 'bold' | 'italic' | 'bolditalic'
  color: string
  align: 'left' | 'center' | 'right'
  vAlign: 'top' | 'middle' | 'bottom'
  lineHeight: number
  letterSpacingPt?: number
  wrap: boolean
}

export interface LdBarcodeElement extends LdBaseElement {
  type: 'barcode'
  symbology: 'code128' | 'code39' | 'ean13' | 'ean8' | 'upca' | 'itf14' | 'gs1-128'
  value: string
  showText: boolean
  barColor: string
  bgColor: string
}

export interface LdQrElement extends LdBaseElement {
  type: 'qr'
  value: string
  eccLevel: 'L' | 'M' | 'Q' | 'H'
  barColor: string
  bgColor: string
}

export interface LdDataMatrixElement extends LdBaseElement {
  type: 'datamatrix'
  value: string
  barColor: string
  bgColor: string
}

export interface LdImageElement extends LdBaseElement {
  type: 'image'
  dataUrl: string
  fit: 'fill' | 'contain' | 'cover'
  opacity?: number
}

export interface LdRectElement extends LdBaseElement {
  type: 'rect'
  fill: string
  stroke: string
  strokeWidthMm: number
  radiusMm: number
}

export interface LdEllipseElement extends LdBaseElement {
  type: 'ellipse'
  fill: string
  stroke: string
  strokeWidthMm: number
}

export interface LdLineElement extends LdBaseElement {
  type: 'line'
  stroke: string
  strokeWidthMm: number
  dash?: number[]
}

export type LdElement =
  | LdTextElement | LdBarcodeElement | LdQrElement | LdDataMatrixElement
  | LdImageElement | LdRectElement | LdEllipseElement | LdLineElement

export interface LabelTemplate {
  schemaVersion: 1
  id?: number
  name: string
  description?: string
  size: { wMm: number; hMm: number; preset?: string }
  background: string
  marginMm: number
  defaultPrinter?: string
  elements: LdElement[]
}

export interface LabelSizePreset { name: string; wMm: number; hMm: number }

export const LABEL_SIZE_PRESETS: LabelSizePreset[] = [
  { name: '89 × 36 mm — Address', wMm: 89, hMm: 36 },
  { name: '102 × 152 mm — Shipping 4×6', wMm: 102, hMm: 152 },
  { name: '100 × 150 mm — Shipping', wMm: 100, hMm: 150 },
  { name: '62 × 29 mm', wMm: 62, hMm: 29 },
  { name: '57 × 32 mm', wMm: 57, hMm: 32 },
  { name: '54 × 29 mm', wMm: 54, hMm: 29 },
  { name: '75 × 60 mm', wMm: 75, hMm: 60 },
  { name: '52 × 74 mm — A8', wMm: 52, hMm: 74 }
]

const uid = (): string => {
  try {
    if (typeof globalThis !== 'undefined' && (globalThis as any).crypto?.randomUUID) {
      return (globalThis as any).crypto.randomUUID()
    }
  } catch { /* noop */ }
  return 'el_' + Math.random().toString(36).slice(2, 10)
}

export const round2 = (n: number): number => Math.round((Number(n) || 0) * 100) / 100
export const clamp = (n: number, min: number, max: number): number => Math.min(Math.max(n, min), max)

export function createTemplate (partial: Partial<LabelTemplate> = {}): LabelTemplate {
  return {
    schemaVersion: 1,
    name: partial.name ?? 'Untitled label',
    description: partial.description ?? '',
    size: partial.size ?? { wMm: 89, hMm: 36, preset: '89 × 36 mm — Address' },
    background: partial.background ?? '#FFFFFF',
    marginMm: partial.marginMm ?? 2,
    defaultPrinter: partial.defaultPrinter,
    id: partial.id,
    elements: Array.isArray(partial.elements) ? partial.elements : []
  }
}

/** Default element of `type`, positioned with a small cascade so adds don't stack exactly. */
export function createElement (type: LdElementType, index = 0): LdElement {
  const off = (index % 6) * 3
  const base = {
    id: uid(),
    type,
    rotation: 0,
    visible: true,
    locked: false,
    x: round2(4 + off),
    y: round2(4 + off)
  }
  switch (type) {
    case 'text':
      return { ...base, name: 'Text', w: 45, h: 10, text: 'Text', fontFamily: 'helvetica', fontSizePt: 11, fontStyle: 'normal', color: '#000000', align: 'left', vAlign: 'top', lineHeight: 1.15, letterSpacingPt: 0, wrap: true } as LdTextElement
    case 'barcode':
      return { ...base, name: 'Barcode', w: 50, h: 18, symbology: 'code128', value: '12345678', showText: true, barColor: '#000000', bgColor: '#FFFFFF' } as LdBarcodeElement
    case 'qr':
      return { ...base, name: 'QR Code', w: 25, h: 25, value: 'https://app.logship.de', eccLevel: 'M', barColor: '#000000', bgColor: '#FFFFFF' } as LdQrElement
    case 'datamatrix':
      return { ...base, name: 'DataMatrix', w: 20, h: 20, value: '12345678', barColor: '#000000', bgColor: '#FFFFFF' } as LdDataMatrixElement
    case 'image':
      return { ...base, name: 'Image', w: 30, h: 30, dataUrl: '', fit: 'contain', opacity: 1 } as LdImageElement
    case 'rect':
      return { ...base, name: 'Rectangle', w: 30, h: 20, fill: 'transparent', stroke: '#000000', strokeWidthMm: 0.3, radiusMm: 0 } as LdRectElement
    case 'ellipse':
      return { ...base, name: 'Ellipse', w: 30, h: 20, fill: 'transparent', stroke: '#000000', strokeWidthMm: 0.3 } as LdEllipseElement
    case 'line':
      return { ...base, name: 'Line', w: 40, h: 0, stroke: '#000000', strokeWidthMm: 0.3 } as LdLineElement
  }
}

export function useLabelDesign () {
  const template = ref<LabelTemplate>(createTemplate())
  const selectedId = ref<string | null>(null)

  // --- undo / redo (full-state snapshots, capped) ---
  const past = ref<string[]>([])
  const future = ref<string[]>([])
  const HISTORY_CAP = 60

  const snapshot = () => JSON.stringify(template.value)
  /** Call right BEFORE a mutation so undo returns to the pre-change state. */
  const record = () => {
    past.value.push(snapshot())
    if (past.value.length > HISTORY_CAP) past.value.shift()
    future.value = []
  }
  const canUndo = computed(() => past.value.length > 0)
  const canRedo = computed(() => future.value.length > 0)
  const undo = () => {
    if (!past.value.length) return
    future.value.push(snapshot())
    template.value = JSON.parse(past.value.pop() as string)
    if (selectedId.value && !template.value.elements.some(e => e.id === selectedId.value)) selectedId.value = null
  }
  const redo = () => {
    if (!future.value.length) return
    past.value.push(snapshot())
    template.value = JSON.parse(future.value.pop() as string)
    if (selectedId.value && !template.value.elements.some(e => e.id === selectedId.value)) selectedId.value = null
  }

  // --- selection ---
  const selected = computed<LdElement | null>(
    () => template.value.elements.find(e => e.id === selectedId.value) ?? null
  )
  const select = (id: string | null) => { selectedId.value = id }

  const indexOf = (id: string) => template.value.elements.findIndex(e => e.id === id)

  // --- element CRUD (auto-record history) ---
  const addElement = (type: LdElementType, patch: Partial<LdElement> = {}): LdElement => {
    record()
    const el = { ...createElement(type, template.value.elements.length), ...patch } as LdElement
    template.value.elements.push(el)
    selectedId.value = el.id
    return el
  }

  const removeElement = (id: string) => {
    const i = indexOf(id)
    if (i < 0) return
    record()
    template.value.elements.splice(i, 1)
    if (selectedId.value === id) selectedId.value = null
  }

  const duplicateElement = (id: string) => {
    const el = template.value.elements.find(e => e.id === id)
    if (!el) return
    record()
    const copy = { ...JSON.parse(JSON.stringify(el)), id: uid(), x: round2(el.x + 3), y: round2(el.y + 3), name: (el.name || el.type) + ' copy' } as LdElement
    template.value.elements.push(copy)
    selectedId.value = copy.id
  }

  /** Patch the selected/identified element. Pass `commit:false` to skip a history entry (live drag). */
  const updateElement = (id: string, patch: Partial<LdElement>, commit = true) => {
    const el = template.value.elements.find(e => e.id === id)
    if (!el) return
    if (commit) record()
    Object.assign(el, patch)
  }

  // --- z-order ---
  const move = (id: string, to: number) => {
    const i = indexOf(id)
    if (i < 0) return
    const clampedTo = clamp(to, 0, template.value.elements.length - 1)
    if (i === clampedTo) return
    record()
    const [el] = template.value.elements.splice(i, 1)
    template.value.elements.splice(clampedTo, 0, el)
  }
  const bringToFront = (id: string) => move(id, template.value.elements.length - 1)
  const sendToBack = (id: string) => move(id, 0)
  const bringForward = (id: string) => move(id, indexOf(id) + 1)
  const sendBackward = (id: string) => move(id, indexOf(id) - 1)

  // --- label size ---
  const setSize = (wMm: number, hMm: number, preset?: string) => {
    record()
    template.value.size = { wMm: round2(clamp(wMm, 5, 1000)), hMm: round2(clamp(hMm, 5, 1000)), preset }
  }
  const setBackground = (color: string) => { record(); template.value.background = color }

  // --- load / reset ---
  const loadTemplate = (tpl: LabelTemplate) => {
    template.value = createTemplate(tpl)
    selectedId.value = null
    past.value = []
    future.value = []
  }
  const reset = () => loadTemplate(createTemplate())

  return {
    template,
    selectedId,
    selected,
    canUndo,
    canRedo,
    LABEL_SIZE_PRESETS,
    record,
    undo,
    redo,
    select,
    addElement,
    removeElement,
    duplicateElement,
    updateElement,
    bringToFront,
    sendToBack,
    bringForward,
    sendBackward,
    setSize,
    setBackground,
    loadTemplate,
    reset
  }
}
