<script setup lang="ts">
import { ref, computed } from 'vue'
import { Minus, Plus } from 'lucide-vue-next'

interface Props {
  modelValue: number
  min?: number
  max?: number
  step?: number
  precision?: number
  prefix?: string
  suffix?: string
  label?: string
  compact?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  min: 0,
  max: Infinity,
  step: 1,
  precision: 0,
  prefix: '',
  suffix: '',
  label: 'Number input',
  compact: false,
})

const emit = defineEmits<{
  'update:modelValue': [value: number]
}>()

const isEditing = ref(false)
const editValue = ref('')
const inputRef = ref<HTMLInputElement | null>(null)

const displayValue = computed(() => {
  return props.modelValue.toFixed(props.precision)
})

function setValue(val: number) {
  const clamped = Math.min(props.max, Math.max(props.min, val))
  const rounded = parseFloat(clamped.toFixed(props.precision))
  emit('update:modelValue', rounded)
}

function increment() {
  setValue(props.modelValue + props.step)
}

function decrement() {
  setValue(props.modelValue - props.step)
}

// ── Press-and-hold ───────────────────────────────────────────────
let holdTimeout: ReturnType<typeof setTimeout> | null = null
let holdInterval: ReturnType<typeof setInterval> | null = null

function startHold(action: () => void) {
  action()
  holdTimeout = setTimeout(() => {
    holdInterval = setInterval(action, 80)
  }, 400)
}

function stopHold() {
  if (holdTimeout) { clearTimeout(holdTimeout); holdTimeout = null }
  if (holdInterval) { clearInterval(holdInterval); holdInterval = null }
}

// ── Inline editing ───────────────────────────────────────────────
function startEdit() {
  isEditing.value = true
  editValue.value = displayValue.value
  setTimeout(() => inputRef.value?.select(), 0)
}

function commitEdit() {
  isEditing.value = false
  const parsed = parseFloat(editValue.value)
  if (!isNaN(parsed)) {
    setValue(parsed)
  }
}

function cancelEdit() {
  isEditing.value = false
}
</script>

<template>
  <div
    class="flex items-center"
    :class="compact ? 'gap-1' : 'gap-2'"
    role="group"
    :aria-label="label"
  >
    <button
      type="button"
      aria-label="Decrease value"
      v-tooltip="'Decrease'"
      @mousedown.prevent="startHold(decrement)"
      @mouseup="stopHold"
      @mouseleave="stopHold"
      @touchstart.prevent="startHold(decrement)"
      @touchend="stopHold"
      class="flex items-center justify-center border border-border-visible rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0"
      :class="compact ? 'w-6 h-6' : 'w-8 h-8'"
      :disabled="modelValue <= min"
    >
      <Minus :class="compact ? 'w-3 h-3' : 'w-3.5 h-3.5'" aria-hidden="true" :stroke-width="2" />
    </button>

    <div
      v-if="!isEditing"
      @click="startEdit"
      @keydown.enter="startEdit"
      @keydown.space.prevent="startEdit"
      tabindex="0"
      role="button"
      :aria-label="'Edit value: ' + displayValue"
      class="text-center font-mono text-text-primary cursor-text select-none"
      :class="compact ? 'min-w-[2.5rem] text-[0.75rem]' : 'min-w-[4rem] text-[0.8125rem]'"
      aria-live="polite"
    >
      <span v-if="prefix" class="text-text-tertiary">{{ prefix }}</span>
      {{ displayValue }}
      <span v-if="suffix" class="text-text-tertiary ml-0.5">{{ suffix }}</span>
    </div>
    <input
      v-else
      ref="inputRef"
      v-model="editValue"
      type="text"
      inputmode="decimal"
      :aria-label="label"
      class="text-center px-1 py-0.5 bg-bg-inset border border-accent rounded-lg font-mono text-text-primary focus:outline-none"
      :class="compact ? 'w-16 text-[0.75rem]' : 'w-20 text-[0.8125rem]'"
      @blur="commitEdit"
      @keydown.enter="commitEdit"
      @keydown.escape="cancelEdit"
    />

    <button
      type="button"
      aria-label="Increase value"
      v-tooltip="'Increase'"
      @mousedown.prevent="startHold(increment)"
      @mouseup="stopHold"
      @mouseleave="stopHold"
      @touchstart.prevent="startHold(increment)"
      @touchend="stopHold"
      class="flex items-center justify-center border border-border-visible rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0"
      :class="compact ? 'w-6 h-6' : 'w-8 h-8'"
      :disabled="modelValue >= max"
    >
      <Plus :class="compact ? 'w-3 h-3' : 'w-3.5 h-3.5'" aria-hidden="true" :stroke-width="2" />
    </button>
  </div>
</template>
