<script setup>
import { useSlots } from "vue";
import { ref, watch, computed, getCurrentInstance } from "vue";

const emit = defineEmits([
  "selected",
  "tr-clicked",
  "tr-mouseover",
  "tr-mouseleave",
]);
const props = defineProps({
  headers: {
    type: Array,
    required: true,
  },
  items: {
    type: [Array, Object],
    default: () => [],
  },
  selectable: {
    type: Boolean,
    default: false,
  },
  selectableKey: {
    type: String,
    default: "id",
  },
  noDataText: {
    type: String,
    default: "Veri bulunamadı.",
  },
  showHeader: {
    type: Boolean,
    default: true,
  },
});

const isLastHeader = (key) => key === props.headers.length - 1;
const isLastItem = (key) => key === props.items.length - 1;

const colspan = computed(() =>
  props.selectable ? props.headers.length + 1 : props.headers.length,
);

const hasTrClickListener = computed(
  () => !!getCurrentInstance()?.vnode.props?.onTrClicked,
);
const slots = useSlots();
const selected = ref([]);
const selectAll = ref(false);

const hasFooter = computed(() => !!slots.footer);
const hasNoData = computed(() => !!slots.noData);

// Shift ile aralık seçimi: en son tıklanan satır (çıpa) ile şimdi tıklanan satır
// arasındaki tüm kutuları, tıklanan kutunun yeni durumuna göre işaretler/kaldırır.
// `selected` tek seferde yeniden atandığı için dizi v-model'inde yarış olmaz.
let lastIndex = null;
const onSelectClick = (e, index) => {
  if (e.shiftKey && lastIndex !== null && lastIndex !== index) {
    const shouldCheck = e.target.checked; // tıklanan kutunun yeni durumu
    const start = Math.min(lastIndex, index);
    const end = Math.max(lastIndex, index);
    const set = new Set(selected.value);
    for (let i = start; i <= end; i++) {
      const val = props.items[i][props.selectableKey];
      if (shouldCheck) set.add(val);
      else set.delete(val);
    }
    selected.value = Array.from(set);
  }
  lastIndex = index;
};

watch(selectAll, (value) => {
  if (value) {
    selected.value = props.items.map((item) => item[props.selectableKey]);
  } else {
    selected.value = [];
  }
  lastIndex = null; // "tümünü seç" sonrası çıpayı sıfırla
});

// Liste değişince (sayfa/filtre) eski index çıpası geçersiz olur.
watch(
  () => props.items,
  () => {
    lastIndex = null;
  },
);

watch(selected, (value) => {
  emit("selected", value);
});
</script>

<template>
  <div v-if="items.length === 0 && hasNoData">
    <div
      class="flex flex-col justify-center items-center text-center mx-auto max-w-[900px] bg-white rounded-md space-y-8 mt-8 lg:px-48 px-4 lg:py-10 py-5"
    >
      <slot name="noData" />
    </div>
  </div>
  <div
    v-else
    :class="`border rounded-lg overflow-x-auto overflow-y-hidden sm:overflow-x-hidden ${
      items.length === 0 ? 'p-1' : 'p-0'
    } dark:border-neutral-700`"
  >
    <slot name="top" />
    <table
      class="w-full bg-white divide-y divide-gray-200 dark:divide-neutral-700"
    >
      <thead
        v-if="showHeader && items.length > 0"
        class="text-xs text-gray-700 text-left bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
      >
        <th v-if="selectable" class="px-4 py-3" scope="col">
          <input
            v-model="selectAll"
            type="checkbox"
            class="w-4 h-4 text-primary-800 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-800 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
          />
        </th>
        <th
          v-for="(header, index) in headers"
          :key="index"
          scope="col"
          :class="`px-4 py-3 w-auto ${
            isLastHeader(index) ? 'text-right' : ''
          } ${header.class ? header.class : ''} `"
        >
          <template v-if="slots[`${header.key}-header`]">
            <slot :name="`${header.key}-header`" :header="header" />
          </template>
          <template v-else>
            {{ header.title }}
            <img
              v-if="header.img"
              :src="header.img"
              :alt="header.title"
              class="size-8 rounded inline-block"
            />
          </template>
        </th>
      </thead>
      <tbody v-if="items.length === 0">
        <tr>
          <td :colspan="colspan">
            <div class="flex flex-col rounded items-center justify-center p-4">
              <magnifying-glass-icon class="w-8 h-8" />
              {{ noDataText }}
            </div>
          </td>
        </tr>
      </tbody>
      <tbody>
        <tr
          v-for="(item, key) in items"
          :key="key"
          :class="`${
            hasTrClickListener ? 'cursor-pointer' : ''
          } bg-white dark:bg-gray-800 ${
            isLastItem(key) ? '' : 'border-b'
          } text-sm dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600`"
          @mouseover="emit('tr-mouseover', key)"
          @mouseleave="emit('tr-mouseleave', key)"
        >
          <td v-if="selectable" class="px-4 py-3" scope="col">
            <input
              v-model="selected"
              type="checkbox"
              class="w-4 h-4 text-primary-800 bg-gray-100 border-gray-300 rounded focus:ring-primary-500 dark:focus:ring-primary-800 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
              :value="item[selectableKey]"
              @click="onSelectClick($event, key)"
            />
          </td>
          <td
            v-for="(header, index) in headers"
            :key="header.key"
            :class="`px-4 py-3 w-auto ${
              isLastHeader(index) ? 'text-right' : ''
            } ${header.class ? header.class : ''} `"
            scope="col"
            @click="
              hasTrClickListener ? emit('tr-clicked', item, header) : false
            "
          >
            <slot
              v-if="slots[header.key]"
              :name="header.key"
              :item="item"
              :index="key"
            />
            <template v-else>
              {{ item[header.key] }}
            </template>
          </td>
        </tr>
      </tbody>
      <tfoot
        v-if="hasFooter"
        class="text-xs text-gray-700 bg-white dark:bg-gray-700 dark:text-gray-400"
      >
        <tr>
          <td class="px-4 py-4" :colspan="colspan">
            <slot name="footer" />
          </td>
        </tr>
      </tfoot>
    </table>
    <slot name="bottom" />
  </div>
</template>
