<template>
  <div :id="componentId" class="w-full" v-bind="attrs">
    <table :class="twMerge('w-full border-separate border-spacing-0 border border-black rounded-malio overflow-hidden', tableClass)">
      <thead>
        <tr class="bg-m-surface">
          <th
            v-for="col in columns"
            :key="col.key"
            scope="col"
            class="border-b border-black px-3 py-3 text-left align-middle text-[16px]"
          >
            <slot
              v-if="$slots[`header-${col.key}`]"
              :name="`header-${col.key}`"
              :column="col"
            />
            <span v-else class="font-semibold text-black">{{ col.label }}</span>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="(item, index) in items"
          :key="index"
          :class="rowClickable ? 'cursor-pointer hover:bg-m-bg' : ''"
          :tabindex="rowClickable ? 0 : undefined"
          data-test="row"
          @click="rowClickable ? emit('row-click', item) : undefined"
          @keydown.enter="rowClickable ? emit('row-click', item) : undefined"
          @keydown.space.prevent="rowClickable ? emit('row-click', item) : undefined"
        >
          <td
            v-for="col in columns"
            :key="col.key"
            class="px-3 py-4 text-[14px] text-black"
            :class="index < items.length - 1 ? 'border-b border-black' : ''"
          >
            <slot
              v-if="$slots[`cell-${col.key}`]"
              :name="`cell-${col.key}`"
              :item="item"
              :column="col"
            />
            <template v-else>{{ item[col.key] }}</template>
          </td>
        </tr>
        <tr v-if="!items.length" data-test="empty-row">
          <td
            :colspan="columns.length"
            class="px-3 py-4 text-center text-m-muted"
          >
            <slot name="empty">{{ emptyMessage }}</slot>
          </td>
        </tr>
      </tbody>
    </table>

    <div
      v-if="totalItems > 0"
      class="flex items-center justify-between pt-3"
      data-test="pagination"
    >
      <div class="flex items-center gap-4">
        <span class="whitespace-nowrap text-[16px] text-black">Lignes :</span>
        <div class="h-[30px]">
          <MalioSelect
            :model-value="perPage"
            :options="perPageSelectOptions"
            group-class="w-20 h-[30px]"
            field-class="h-[30px]"
            rounded="rounded"
            text-field="text-sm"
            text-value="text-sm"
            text-label="text-xs"
            data-test="per-page-select"
            @update:model-value="onPerPageChange"
          />
        </div>
      </div>

      <nav aria-label="Pagination" class="flex items-center gap-1" data-test="pagination-nav">
        <MalioButton
          variant="tertiary"
          label="Préc."
          :disabled="page <= 1"
          button-class="h-[30px] w-auto min-w-0 px-3 text-sm"
          aria-label="Page précédente"
          data-test="prev-button"
          @click="changePage(page - 1)"
        />

        <span class="flex items-center gap-2 text-sm">
          <label :for="pageInputId" class="text-m-muted">Page</label>
          <input
            :id="pageInputId"
            v-model="pageInput"
            type="text"
            inputmode="numeric"
            aria-label="Aller à la page"
            data-test="page-input"
            class="h-[30px] w-[58px] rounded-malio border border-m-border text-center text-sm text-m-text outline-none focus:border-m-primary"
            @input="onPageInput"
            @keydown.enter="commitPageInput"
            @blur="commitPageInput"
          >
          <span class="text-m-muted">/ <span data-test="total-pages">{{ totalPages }}</span></span>
        </span>

        <MalioButton
          variant="tertiary"
          label="Suiv."
          :disabled="page >= totalPages"
          button-class="h-[30px] w-auto min-w-0 px-3 text-sm"
          aria-label="Page suivante"
          data-test="next-button"
          @click="changePage(page + 1)"
        />
      </nav>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch, onBeforeUnmount, useAttrs, useId } from 'vue'
import { twMerge } from 'tailwind-merge'
import MalioSelect from '../select/Select.vue'
import MalioButton from '../button/Button.vue'

defineOptions({ name: 'MalioDataTable', inheritAttrs: false })

type DataTableColumn = {
  key: string
  label: string
}

const attrs = useAttrs()

const props = withDefaults(
  defineProps<{
    id?: string
    columns: DataTableColumn[]
    items: Record<string, unknown>[]
    totalItems: number
    page?: number
    perPage?: number
    perPageOptions?: number[]
    rowClickable?: boolean
    tableClass?: string
    emptyMessage?: string
  }>(),
  {
    id: '',
    page: 1,
    perPage: 10,
    perPageOptions: () => [10, 25, 50],
    rowClickable: true,
    tableClass: '',
    emptyMessage: 'Aucune donnée',
  },
)

const emit = defineEmits<{
  (e: 'update:page' | 'update:per-page', value: number): void
  (e: 'row-click', item: Record<string, unknown>): void
}>()

const generatedId = useId()
const componentId = computed(() => props.id || `malio-datatable-${generatedId}`)

const totalPages = computed(() => Math.max(1, Math.ceil(props.totalItems / props.perPage)))

const PAGE_JUMP_DEBOUNCE = 400
let debounceTimer: ReturnType<typeof setTimeout> | null = null
const pageInputId = computed(() => `${componentId.value}-page-input`)
const pageInput = ref(String(props.page))

watch(() => props.page, (p) => { pageInput.value = String(p) })

onBeforeUnmount(() => { if (debounceTimer) clearTimeout(debounceTimer) })

const perPageSelectOptions = computed(() =>
  props.perPageOptions.map(n => ({ label: String(n), value: n }))
)

function onPerPageChange(value: string | number | null) {
  if (value !== null) {
    emit('update:per-page', Number(value))
    emit('update:page', 1)
  }
}

function changePage(page: number) {
  if (page >= 1 && page <= totalPages.value && page !== props.page) {
    emit('update:page', page)
  }
}

function onPageInput() {
  pageInput.value = pageInput.value.replace(/[^0-9]/g, '')
  if (debounceTimer) clearTimeout(debounceTimer)
  if (pageInput.value === '') return
  const n = Number(pageInput.value)
  if (n >= 1 && n <= totalPages.value) {
    debounceTimer = setTimeout(() => changePage(n), PAGE_JUMP_DEBOUNCE)
  }
}

function commitPageInput() {
  if (debounceTimer) clearTimeout(debounceTimer)
  const raw = pageInput.value.trim()
  const n = Number(raw)
  if (raw === '' || n === 0 || Number.isNaN(n)) {
    pageInput.value = String(props.page)
    return
  }
  const clamped = Math.min(Math.max(1, Math.round(n)), totalPages.value)
  changePage(clamped)
  pageInput.value = String(clamped)
}
</script>
