<script setup lang="ts">
import { ref, computed, onBeforeUnmount } from 'vue'
import { useInvoicesStore, type Invoice } from '../stores/invoices'
import { useToastStore } from '../stores/toast'
import { formatCurrency, formatDate } from '../utils/locale'
import { GripVertical } from 'lucide-vue-next'

const emit = defineEmits<{ open: [id: number] }>()

const invoicesStore = useInvoicesStore()
const toastStore = useToastStore()

const columns = ['draft', 'sent', 'overdue', 'paid'] as const
const columnLabels: Record<string, string> = { draft: 'Draft', sent: 'Sent', overdue: 'Overdue', paid: 'Paid' }

function columnTotal(status: string): string {
  const items = invoicesStore.groupedByStatus[status] || []
  const sum = items.reduce((acc, inv) => acc + inv.total, 0)
  return formatCurrency(sum)
}

// Pointer-based drag (works in Tauri webview unlike HTML5 DnD)
const dragInv = ref<Invoice | null>(null)
const dragStartX = ref(0)
const dragStartY = ref(0)
const dragX = ref(0)
const dragY = ref(0)
const isDragging = ref(false)
const dragOverCol = ref<string | null>(null)
const columnRefs = ref<Record<string, HTMLElement>>({})
const cardWidth = ref(200)

const DRAG_THRESHOLD = 6

function setColumnRef(col: string, el: HTMLElement | null) {
  if (el) columnRefs.value[col] = el
}

function onPointerDown(inv: Invoice, e: PointerEvent) {
  // Only primary button
  if (e.button !== 0) return
  dragInv.value = inv
  dragStartX.value = e.clientX
  dragStartY.value = e.clientY
  dragX.value = e.clientX
  dragY.value = e.clientY
  isDragging.value = false
  // Measure the source card width for the ghost
  const el = (e.currentTarget as HTMLElement)
  if (el) cardWidth.value = el.offsetWidth
  document.addEventListener('pointermove', onPointerMove)
  document.addEventListener('pointerup', onPointerUp)
}

function onPointerMove(e: PointerEvent) {
  if (!dragInv.value) return

  const dx = e.clientX - dragStartX.value
  const dy = e.clientY - dragStartY.value

  // Start drag only after threshold
  if (!isDragging.value) {
    if (Math.abs(dx) + Math.abs(dy) < DRAG_THRESHOLD) return
    isDragging.value = true
  }

  // Track position for the ghost
  dragX.value = e.clientX
  dragY.value = e.clientY

  // Hit-test which column the pointer is over
  // The ghost has pointer-events:none so elementFromPoint sees through it
  const hit = document.elementFromPoint(e.clientX, e.clientY)
  if (hit) {
    let found: string | null = null
    for (const [col, el] of Object.entries(columnRefs.value)) {
      if (el.contains(hit)) {
        found = col
        break
      }
    }
    dragOverCol.value = found
  }
}

async function onPointerUp() {
  document.removeEventListener('pointermove', onPointerMove)
  document.removeEventListener('pointerup', onPointerUp)

  const inv = dragInv.value
  const targetCol = dragOverCol.value
  const wasDragging = isDragging.value

  dragInv.value = null
  dragOverCol.value = null
  isDragging.value = false

  if (!inv) return

  // If we were dragging and landed on a different column, move the invoice
  if (wasDragging && targetCol && targetCol !== inv.status) {
    const oldStatus = inv.status
    const ok = await invoicesStore.updateStatus(inv.id!, targetCol)
    if (ok) {
      toastStore.success(`Moved ${inv.invoice_number} to ${columnLabels[targetCol]}`, {
        onUndo: async () => {
          await invoicesStore.updateStatus(inv.id!, oldStatus)
        }
      })
    }
    return
  }

  // If we didn't drag (just clicked), open the invoice
  if (!wasDragging) {
    emit('open', inv.id!)
  }
}

onBeforeUnmount(() => {
  document.removeEventListener('pointermove', onPointerMove)
  document.removeEventListener('pointerup', onPointerUp)
})

const reducedMotion = computed(() => window.matchMedia('(prefers-reduced-motion: reduce)').matches)
</script>

<template>
  <div class="grid grid-cols-4 gap-4 select-none">
    <div
      v-for="col in columns"
      :key="col"
      :ref="(el) => setColumnRef(col, el as HTMLElement)"
      class="flex flex-col min-h-[300px] bg-bg-elevated rounded-lg overflow-hidden transition-all duration-150"
      :class="[
        dragOverCol === col && isDragging ? 'ring-2 ring-accent bg-accent/5' : '',
        col === 'overdue' ? 'border-t-2 border-status-error' : ''
      ]"
      :aria-label="columnLabels[col] + ' invoices'"
    >
      <div class="px-3 py-2.5 border-b border-border-subtle">
        <div class="flex items-center justify-between">
          <span class="text-[0.75rem] font-medium text-text-primary">{{ columnLabels[col] }}</span>
          <span class="text-[0.625rem] text-text-tertiary bg-bg-base rounded-full px-2 py-0.5">
            {{ (invoicesStore.groupedByStatus[col] || []).length }}
          </span>
        </div>
        <p class="text-[0.6875rem] text-text-tertiary mt-0.5">{{ columnTotal(col) }}</p>
      </div>

      <div class="flex-1 overflow-y-auto p-2 space-y-2" role="list">
        <div
          v-for="inv in (invoicesStore.groupedByStatus[col] || [])"
          :key="inv.id"
          role="listitem"
          tabindex="0"
          class="bg-bg-surface border border-border-subtle rounded-lg p-3 transition-all duration-150 hover:border-accent/50 group"
          :class="[
            isDragging && dragInv?.id === inv.id ? 'opacity-40 scale-95 cursor-grabbing' : 'cursor-grab',
            !reducedMotion ? 'hover:shadow-sm' : ''
          ]"
          @pointerdown="onPointerDown(inv, $event)"
          @keydown.enter="emit('open', inv.id!)"
        >
          <div class="flex items-start gap-2">
            <GripVertical
              class="w-3.5 h-3.5 text-text-tertiary opacity-0 group-hover:opacity-100 transition-opacity shrink-0 mt-0.5"
              :stroke-width="1.5"
              aria-hidden="true"
            />
            <div class="flex-1 min-w-0">
              <p class="text-[0.8125rem] font-medium text-text-primary">{{ inv.invoice_number }}</p>
              <p class="text-[0.6875rem] text-text-tertiary mt-0.5">{{ formatDate(inv.date) }}</p>
              <p class="text-[0.8125rem] font-medium text-text-primary mt-1">
                {{ formatCurrency(inv.total) }}
              </p>
            </div>
          </div>
        </div>

        <!-- Drop zone placeholder when column is empty or drag active -->
        <div
          v-if="!(invoicesStore.groupedByStatus[col] || []).length"
          class="flex items-center justify-center h-20 text-[0.6875rem] text-text-tertiary border-2 border-dashed rounded-lg transition-colors"
          :class="isDragging && dragOverCol === col ? 'border-accent text-accent' : 'border-border-subtle'"
        >
          {{ isDragging ? 'Drop here' : 'No invoices' }}
        </div>
      </div>
    </div>
  </div>

  <!-- Floating ghost tile that follows the cursor during drag -->
  <Teleport to="#app">
    <div
      v-if="isDragging && dragInv"
      class="fixed z-[200] pointer-events-none"
      :style="{
        left: dragX + 'px',
        top: dragY + 'px',
        width: cardWidth + 'px',
        transform: 'translate(-50%, -60%) rotate(-2deg)',
      }"
    >
      <div class="bg-bg-surface border-2 border-accent rounded-lg p-3 shadow-lg shadow-black/30 opacity-90">
        <p class="text-[0.8125rem] font-medium text-text-primary">{{ dragInv.invoice_number }}</p>
        <p class="text-[0.6875rem] text-text-tertiary mt-0.5">{{ formatDate(dragInv.date) }}</p>
        <p class="text-[0.8125rem] font-medium text-text-primary mt-1">{{ formatCurrency(dragInv.total) }}</p>
      </div>
    </div>
  </Teleport>
</template>
