<script lang="ts">
  // A single desktop icon: positioned from the grid store, draggable to any
  // free cell, opens its app on a plain click. wmkit has nothing to do with
  // icons — they're a flat non-overlapping grid, not floating/resizable
  // windows, so this is a small self-contained pointer-drag implementation
  // rather than pressing wmkit's window abstractions into a shape they don't
  // fit. See $lib/stores/icons.ts for the grid model + persistence.
  import { GRID, iconPixelPos, placeIcon, type IconPos } from '$lib/stores/icons'
  import type { AppDef } from '$lib/apps'

  let {
    app,
    pos,
    badge = 0,
    onOpen
  }: { app: AppDef; pos: IconPos; badge?: number; onOpen: () => void } = $props()

  const DRAG_THRESHOLD = 5

  let dragging = $state(false)
  let dragPos = $state<{ x: number; y: number } | null>(null)

  function toCell(x: number, y: number): IconPos {
    return {
      col: Math.round((x - GRID.padding) / (GRID.cell + GRID.gap)),
      row: Math.round((y - GRID.padding) / (GRID.cell + GRID.gap))
    }
  }

  function onPointerDown(e: PointerEvent) {
    if (e.button !== 0) return
    const el = e.currentTarget as HTMLElement
    const startX = e.clientX
    const startY = e.clientY
    const origin = iconPixelPos(pos)
    let moved = false

    el.setPointerCapture(e.pointerId)

    function onMove(ev: PointerEvent) {
      const dx = ev.clientX - startX
      const dy = ev.clientY - startY
      if (!moved && Math.hypot(dx, dy) > DRAG_THRESHOLD) {
        moved = true
        dragging = true
      }
      if (moved) {
        dragPos = { x: origin.x + dx, y: origin.y + dy }
      }
    }

    function onUp() {
      el.removeEventListener('pointermove', onMove)
      el.removeEventListener('pointerup', onUp)
      if (moved && dragPos) {
        const cell = toCell(dragPos.x, dragPos.y)
        placeIcon(app.id, cell.col, cell.row)
      } else {
        onOpen()
      }
      dragging = false
      dragPos = null
    }

    el.addEventListener('pointermove', onMove)
    el.addEventListener('pointerup', onUp)
  }

  function onKeydown(e: KeyboardEvent) {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      onOpen()
    }
  }

  const restPos = $derived(iconPixelPos(pos))
  const left = $derived(dragging && dragPos ? dragPos.x : restPos.x)
  const top = $derived(dragging && dragPos ? dragPos.y : restPos.y)
</script>

<button
  type="button"
  class="group absolute flex flex-col items-center gap-1.5 p-1.5 pointer-events-auto select-none transition-transform focus-visible:outline-2 focus-visible:outline-ring {dragging
    ? 'z-50 cursor-grabbing'
    : 'cursor-pointer hover:-translate-y-0.5'}"
  style="left: {left}px; top: {top}px; width: {GRID.cell}px;"
  onpointerdown={onPointerDown}
  onkeydown={onKeydown}
  title={app.title}
>
  <span
    class="relative flex size-11 items-center justify-center border transition-all {dragging
      ? 'border-foreground bg-foreground text-background shadow-[4px_4px_0_0_var(--foreground)]'
      : 'border-border bg-card text-foreground group-hover:border-foreground group-hover:bg-foreground group-hover:text-background'}"
  >
    <app.icon class="size-5 transition-colors" />
    {#if badge > 0}
      <span
        class="absolute -right-1.5 -top-1.5 flex h-4 min-w-4 items-center justify-center border border-background bg-destructive px-1 text-[10px] font-semibold text-destructive-foreground"
      >
        {badge > 99 ? '99+' : badge}
      </span>
    {/if}
  </span>
  <span
    class="max-w-full truncate text-[10px] uppercase tracking-wider text-muted-foreground transition-colors group-hover:text-foreground"
    >{app.title}</span
  >
</button>
