<script setup>
import { ref, computed, watch, h } from 'vue';
import { router } from '@inertiajs/vue3';
import {
  useVueTable,
  getCoreRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  flexRender,
} from '@tanstack/vue-table';
import DataTableToolbar from './DataTableToolbar.vue';
import DataTableColumnHeader from './DataTableColumnHeader.vue';
import DataTablePagination from './DataTablePagination.vue';
import DataTableViewOptions from './DataTableViewOptions.vue';
import SkeletonTable from '../Skeleton/SkeletonTable.vue';
import EmptyState from '../EmptyState.vue';
import Pagination from '../Pagination.vue';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/Components/ui/table';
import Checkbox from '@/Components/ui/checkbox/Checkbox.vue';
import { cn } from '@/lib/utils';

const props = defineProps({
  // Data
  rows: { type: Array, default: () => [] },
  columns: {
    type: Array,
    required: true,
    validator: (cols) =>
      cols.every(
        (col) => col.key && col.label && typeof col.key === 'string' && typeof col.label === 'string'
      ),
  },
  
  // Pagination (for server-side)
  meta: {
    type: Object,
    default: null,
    validator: (meta) =>
      !meta ||
      (typeof meta.current_page === 'number' &&
        typeof meta.per_page === 'number' &&
        typeof meta.total === 'number' &&
        typeof meta.last_page === 'number'),
  },
  
  // Sorting
  sort: {
    type: Object,
    default: () => ({ key: null, direction: null }),
  },
  
  // Search
  search: { type: String, default: '' },
  
  // Loading state
  loading: { type: Boolean, default: false },
  
  // Client-side pagination (when meta is null)
  pageSize: { type: Number, default: 10 },
  pageSizeOptions: { type: Array, default: () => [10, 25, 50, 100] },
  
  // Routing (for server-side)
  routeName: { type: String, default: null },
  routeParams: { type: Object, default: () => ({}) },
  pageParamName: { type: String, default: 'page' },
  onlyProps: { type: Array, default: () => [] },
  
  // Features
  showToolbar: { type: Boolean, default: true },
  showSearch: { type: Boolean, default: false },
  showPageSize: { type: Boolean, default: false },
  showPagination: { type: Boolean, default: true },
  showFilters: { type: Boolean, default: false },
  showExport: { type: Boolean, default: false },
  showAdd: { type: Boolean, default: false },
  showOptions: { type: Boolean, default: false },
  showSelectedCount: { type: Boolean, default: false },
  showOptionsMenu: { type: Boolean, default: false },
  showViewOptions: { type: Boolean, default: false },
  compactToolbar: { type: Boolean, default: false },
  hasActiveFilters: { type: Boolean, default: false },
  rowKey: { type: [String, Function], default: 'uuid' },
  selectable: { type: Boolean, default: false },
  striped: { type: Boolean, default: false },
  hoverable: { type: Boolean, default: true },
  
  // Empty state
  emptyText: { type: String, default: 'Ni podatkov' },
  emptyIcon: { type: [String, Object, Array], default: null },
  emptyDescription: { type: String, default: null },
  
  // Actions
  showActions: { type: Boolean, default: false },
  actionsPosition: { type: String, default: 'right', validator: (v) => ['left', 'right'].includes(v) },
  
  // Mobile
  mobileCardView: { type: Boolean, default: true },
  mobileBreakpoint: { type: Number, default: 768 },
  
  // State preservation
  preserveState: { type: Boolean, default: true },
  preserveScroll: { type: Boolean, default: true },
});

const emit = defineEmits([
  'update:search',
  'update:sort',
  'update:page',
  'update:pageSize',
  'row:click',
  'row:select',
  'selection:change',
]);

// Determine if this is server-side (has meta and routeName)
const isServerSide = computed(() => !!(props.meta && props.routeName));
const isClientSide = computed(() => !isServerSide.value);

// Row key helper
function keyOf(row) {
  if (typeof props.rowKey === 'function') return props.rowKey(row);
  if (typeof props.rowKey === 'string' && row && row[props.rowKey] != null)
    return row[props.rowKey];
  return row?.uuid ?? row?.id ?? Math.random().toString(36).slice(2);
}

// Convert simple column format to TanStack Table ColumnDef format
const columnDefinitions = computed(() => {
  return props.columns.map((col) => ({
    accessorKey: col.key,
    id: col.key,
    header: ({ column }) => {
      return h(DataTableColumnHeader, {
        column,
        title: col.label,
        class: col.class,
      });
    },
    cell: ({ row, getValue }) => {
      return getValue();
    },
    enableSorting: col.sortable !== false,
    enableHiding: col.hideable !== false,
    meta: {
      align: col.align || 'left',
      class: col.class,
    },
  }));
});

// Add selection column if selectable
const columnsWithSelection = computed(() => {
  if (!props.selectable) return columnDefinitions.value;
  
  return [
    {
      id: 'select',
      enableHiding: false,
      enableSorting: false,
      header: ({ table }) => {
        return h(Checkbox, {
          modelValue: table.getIsAllPageRowsSelected(),
          indeterminate: table.getIsSomePageRowsSelected(),
          'onUpdate:modelValue': (value) => table.toggleAllPageRowsSelected(!!value),
          'aria-label': 'Select all',
        });
      },
      cell: ({ row }) => {
        return h(Checkbox, {
          modelValue: row.getIsSelected(),
          'onUpdate:modelValue': (value) => row.toggleSelected(!!value),
          'aria-label': 'Select row',
        });
      },
    },
    ...columnDefinitions.value,
  ];
});

// Add actions column if showActions
const finalColumns = computed(() => {
  if (!props.showActions && !props.$slots.actions) return columnsWithSelection.value;
  
  return [
    ...columnsWithSelection.value,
    {
      id: 'actions',
      enableHiding: false,
      enableSorting: false,
      header: () => h('span', { class: 'sr-only' }, 'Actions'),
      cell: ({ row }) => {
        // Actions will be rendered via slot
        return null;
      },
    },
  ];
});

// Internal search state
const internalSearch = ref(props.search);
watch(
  () => props.search,
  (newVal) => {
    internalSearch.value = newVal;
  }
);

// Internal sorting state
const sorting = computed(() => {
  if (!props.sort?.key || !props.sort?.direction) return [];
  return [
    {
      id: props.sort.key,
      desc: props.sort.direction === 'desc',
    },
  ];
});

// Internal pagination state
const pagination = computed(() => {
  if (isServerSide.value) {
    return {
      pageIndex: (props.meta?.current_page ?? 1) - 1,
      pageSize: props.meta?.per_page ?? props.pageSize,
    };
  }
  return {
    pageIndex: internalPage.value - 1,
    pageSize: internalPageSize.value,
  };
});

const internalPage = ref(1);
const internalPageSize = ref(props.pageSize);

// Row selection
const rowSelection = ref({});

// Create TanStack Table instance
const table = useVueTable({
  get data() {
    return props.rows;
  },
  get columns() {
    return finalColumns.value;
  },
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: isClientSide.value ? getPaginationRowModel() : undefined,
  getSortedRowModel: isClientSide.value ? getSortedRowModel() : undefined,
  getFilteredRowModel: isClientSide.value ? getFilteredRowModel() : undefined,
  onSortingChange: (updater) => {
    const newSorting = typeof updater === 'function' ? updater(sorting.value) : updater;
    if (newSorting.length > 0) {
      const sort = newSorting[0];
      emit('update:sort', {
        key: sort.id,
        direction: sort.desc ? 'desc' : 'asc',
      });
      if (isServerSide.value) {
        doServerRequest({
          sort: sort.id,
          direction: sort.desc ? 'desc' : 'asc',
          page: 1,
        });
      }
    } else {
      emit('update:sort', { key: null, direction: null });
      if (isServerSide.value) {
        doServerRequest({ sort: null, direction: null, page: 1 });
      }
    }
  },
  onPaginationChange: (updater) => {
    const newPagination = typeof updater === 'function' ? updater(pagination.value) : updater;
    if (isServerSide.value) {
      doServerRequest({ page: newPagination.pageIndex + 1 });
    } else {
      internalPage.value = newPagination.pageIndex + 1;
      emit('update:page', newPagination.pageIndex + 1);
    }
    internalPageSize.value = newPagination.pageSize;
    emit('update:pageSize', newPagination.pageSize);
  },
  onRowSelectionChange: (updater) => {
    const newSelection = typeof updater === 'function' ? updater(rowSelection.value) : updater;
    rowSelection.value = newSelection;
    const selectedKeys = Object.keys(newSelection).filter((key) => newSelection[key]);
    emit('selection:change', selectedKeys);
  },
  manualSorting: isServerSide.value,
  manualPagination: isServerSide.value,
  manualFiltering: isServerSide.value,
  enableRowSelection: props.selectable,
  state: {
    get sorting() {
      return sorting.value;
    },
    get pagination() {
      return pagination.value;
    },
    get rowSelection() {
      return rowSelection.value;
    },
    get globalFilter() {
      return internalSearch.value;
    },
  },
});

// Server-side request
function doServerRequest(overrides = {}) {
  const existingParams = Object.fromEntries(
    new URLSearchParams(window.location.search).entries()
  );
  
  const q = {
    ...existingParams,
    per_page: overrides.perPage ?? props.meta?.per_page ?? internalPageSize.value,
    sort: overrides.sort ?? props.sort?.key ?? existingParams.sort ?? null,
    direction: overrides.direction ?? props.sort?.direction ?? existingParams.direction ?? null,
    search: overrides.search ?? internalSearch.value ?? existingParams.search ?? '',
  };
  
  const pageParam = props.pageParamName || 'page';
  q[pageParam] = overrides.page ?? props.meta?.current_page ?? 1;
  if (pageParam !== 'page') {
    delete q.page;
  }
  
  Object.keys(q).forEach((k) => {
    if (q[k] === null || q[k] === undefined || q[k] === '') delete q[k];
  });
  
  const url = route(props.routeName, props.routeParams || {});
  router.get(
    url,
    q,
    {
      preserveScroll: props.preserveScroll,
      preserveState: props.preserveState,
      replace: true,
      only: props.onlyProps.length ? props.onlyProps : undefined,
    }
  );
}

function handleSearchChange(value) {
  internalSearch.value = value;
  emit('update:search', value);
  
  if (isServerSide.value) {
    clearTimeout(searchTimer.value);
    searchTimer.value = setTimeout(() => {
      doServerRequest({ search: value, page: 1 });
    }, 300);
  }
}

function handlePageSizeChange(size) {
  const newSize = Number(size);
  internalPageSize.value = newSize;
  emit('update:pageSize', newSize);
  
  if (isServerSide.value) {
    doServerRequest({ perPage: newSize, page: 1 });
  } else {
    table.setPageSize(newSize);
  }
}

const searchTimer = ref(null);

// Mobile detection
const isMobile = ref(false);
if (typeof window !== 'undefined') {
  const checkMobile = () => {
    isMobile.value = window.innerWidth < props.mobileBreakpoint;
  };
  checkMobile();
  window.addEventListener('resize', checkMobile);
}

// Display rows
const displayRows = computed(() => {
  if (isServerSide.value) return props.rows;
  return table.getRowModel().rows.map((row) => row.original);
});

const total = computed(() => {
  if (isServerSide.value) return props.meta?.total ?? 0;
  return table.getFilteredRowModel().rows.length;
});

const from = computed(() => {
  if (isServerSide.value) return props.meta?.from ?? 0;
  const pageIndex = table.getState().pagination.pageIndex;
  const pageSize = table.getState().pagination.pageSize;
  return total.value === 0 ? 0 : pageIndex * pageSize + 1;
});

const to = computed(() => {
  if (isServerSide.value) return props.meta?.to ?? 0;
  const pageIndex = table.getState().pagination.pageIndex;
  const pageSize = table.getState().pagination.pageSize;
  return Math.min((pageIndex + 1) * pageSize, total.value);
});

// Export functionality
function handleExport(format) {
  const data = displayRows.value.map((row) => {
    const exported = {};
    props.columns.forEach((col) => {
      exported[col.label] = row?.[col.key] ?? '';
    });
    return exported;
  });

  if (format === 'csv') {
    exportToCSV(data);
  } else if (format === 'xlsx') {
    exportToXLSX(data);
  }
}

function exportToCSV(data) {
  if (data.length === 0) return;

  const headers = Object.keys(data[0]);
  const csvContent = [
    headers.join(','),
    ...data.map((row) =>
      headers
        .map((header) => {
          const value = row[header];
          if (value == null) return '';
          const stringValue = String(value).replace(/"/g, '""');
          return `"${stringValue}"`;
        })
        .join(',')
    ),
  ].join('\n');

  const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' });
  const link = document.createElement('a');
  const url = URL.createObjectURL(blob);
  link.setAttribute('href', url);
  link.setAttribute('download', `export_${new Date().toISOString().split('T')[0]}.csv`);
  link.style.visibility = 'hidden';
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

function exportToXLSX(data) {
  exportToCSV(data);
}
</script>

<template>
  <div class="w-full space-y-4">
    <!-- Toolbar -->
    <DataTableToolbar
      v-if="showToolbar"
      :search="internalSearch"
      :show-search="showSearch"
      :show-page-size="showPageSize"
      :page-size="internalPageSize"
      :page-size-options="pageSizeOptions"
      :selected-count="Object.keys(rowSelection).filter((key) => rowSelection[key]).length"
      :show-selected-count="showSelectedCount"
      :show-export="showExport"
      :show-add="showAdd"
      :show-options="showOptions"
      :show-filters="showFilters"
      :show-options-menu="showOptionsMenu"
      :has-active-filters="hasActiveFilters"
      :compact="compactToolbar"
      @update:search="handleSearchChange"
      @update:page-size="handlePageSizeChange"
      @export="handleExport"
    >
      <template #add>
        <slot name="toolbar-add" />
      </template>
      <template #options>
        <slot name="toolbar-options" />
      </template>
      <template #filters>
        <slot name="toolbar-filters" />
      </template>
      <template #actions>
        <slot name="toolbar-actions" />
      </template>
    </DataTableToolbar>

    <!-- View Options -->
    <div v-if="showViewOptions" class="flex items-center space-x-2">
      <DataTableViewOptions :table="table" />
    </div>

    <!-- Table Container -->
    <div data-table-container class="relative overflow-hidden">
      <!-- Desktop Table View -->
      <div v-if="!isMobile || !mobileCardView" class="overflow-x-auto">
        <Table>
          <TableHeader>
            <TableRow
              v-for="headerGroup in table.getHeaderGroups()"
              :key="headerGroup.id"
            >
              <TableHead
                v-for="header in headerGroup.headers"
                :key="header.id"
                :class="[
                  header.column.columnDef.meta?.class,
                  header.column.columnDef.meta?.align === 'right' ? 'text-right' : 
                  header.column.columnDef.meta?.align === 'center' ? 'text-center' : 'text-left',
                ]"
              >
                <div v-if="!header.isPlaceholder">
                  <component
                    :is="flexRender(header.column.columnDef.header, header.getContext())"
                  />
                </div>
              </TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            <!-- Loading State -->
            <template v-if="loading">
              <TableRow>
                <TableCell
                  :colspan="columns.length + (selectable ? 1 : 0) + (showActions ? 1 : 0)"
                  class="h-24 text-center"
                >
                  <SkeletonTable :rows="5" :cols="columns.length" />
                </TableCell>
              </TableRow>
            </template>

            <!-- Empty State -->
            <template v-else-if="!loading && table.getRowModel().rows.length === 0">
              <TableRow>
                <TableCell
                  :colspan="columns.length + (selectable ? 1 : 0) + (showActions ? 1 : 0)"
                  class="h-24 text-center"
                >
                  <EmptyState
                    :icon="emptyIcon"
                    :title="emptyText"
                    :description="emptyDescription"
                    size="sm"
                  />
                </TableCell>
              </TableRow>
            </template>

            <!-- Rows -->
            <template v-else>
              <TableRow
                v-for="row in table.getRowModel().rows"
                :key="row.id"
                :data-state="row.getIsSelected() && 'selected'"
                :class="cn(
                  hoverable && 'cursor-pointer',
                  striped && row.index % 2 === 1 && 'bg-muted/50',
                )"
                @click="(e) => { 
                  const interactive = e.target.closest('button, a, [role=button], [data-dropdown], .relative, input[type=checkbox]'); 
                  if (interactive) return; 
                  $emit('row:click', row.original, row.index); 
                }"
              >
                <TableCell
                  v-for="cell in row.getVisibleCells()"
                  :key="cell.id"
                  :class="[
                    cell.column.columnDef.meta?.class,
                    cell.column.columnDef.meta?.align === 'right' ? 'text-right' : 
                    cell.column.columnDef.meta?.align === 'center' ? 'text-center' : 'text-left',
                  ]"
                >
                  <template v-if="cell.column.id === 'actions'">
                    <slot name="actions" :row="row.original" :index="row.index">
                      <slot name="row-actions" :row="row.original" :index="row.index" />
                    </slot>
                  </template>
                  <template v-else>
                    <slot
                      :name="`cell-${cell.column.id}`"
                      :row="row.original"
                      :column="cell.column.columnDef"
                      :value="cell.getValue()"
                      :index="row.index"
                    >
                      <slot
                        name="cell"
                        :row="row.original"
                        :column="cell.column.columnDef"
                        :value="cell.getValue()"
                        :index="row.index"
                      >
                        <component
                          :is="flexRender(cell.column.columnDef.cell, cell.getContext())"
                        />
                      </slot>
                    </slot>
                  </template>
                </TableCell>
              </TableRow>
            </template>
          </TableBody>
        </Table>
      </div>

      <!-- Mobile Card View -->
      <div v-else-if="isMobile && mobileCardView" class="divide-y divide-gray-200">
        <template v-if="loading">
          <div class="p-4">
            <SkeletonTable :rows="3" :cols="1" />
          </div>
        </template>

        <template v-else-if="!loading && table.getRowModel().rows.length === 0">
          <div class="p-6">
            <EmptyState
              :icon="emptyIcon"
              :title="emptyText"
              :description="emptyDescription"
              size="sm"
            />
          </div>
        </template>

        <template v-else>
          <div
            v-for="row in table.getRowModel().rows"
            :key="row.id"
            @click="$emit('row:click', row.original, row.index)"
            class="p-4 space-y-2 hover:bg-gray-50 transition-colors"
            :class="{ 'cursor-pointer': !!$attrs.onRowClick }"
          >
            <slot name="mobile-card" :row="row.original" :index="row.index">
              <!-- Default mobile card layout -->
              <div
                v-for="col in columns.slice(0, 3)"
                :key="col.key"
                class="flex justify-between items-start"
              >
                <span class="text-xs font-medium text-gray-500 uppercase tracking-wide">
                  {{ col.label }}
                </span>
                <span class="text-sm text-gray-900 text-right">
                  <slot
                    :name="`cell-${col.key}`"
                    :row="row.original"
                    :column="col"
                    :value="row.original?.[col.key]"
                    :index="row.index"
                  >
                    {{ row.original?.[col.key] ?? '—' }}
                  </slot>
                </span>
              </div>
              <div v-if="showActions || $slots.actions" class="pt-2 border-t border-gray-100">
                <slot name="actions" :row="row.original" :index="row.index">
                  <slot name="row-actions" :row="row.original" :index="row.index" />
                </slot>
              </div>
            </slot>
          </div>
        </template>
      </div>
    </div>

    <!-- Pagination -->
    <div v-if="showPagination">
      <!-- Use existing Pagination component for server-side -->
      <template v-if="isServerSide && meta?.links">
        <Pagination
          :links="meta.links"
          :from="from"
          :to="to"
          :total="total"
          :current-page="meta.current_page"
          :last-page="meta.last_page"
          :per-page="meta.per_page"
          :page-param="pageParamName"
          :per-page-param="'per_page'"<!-- legacy component may not have custom per-page name prop -->
        />
      </template>

      <!-- TanStack Table Pagination for client-side -->
      <template v-else>
        <DataTablePagination :table="table" />
      </template>
    </div>
  </div>
</template>

