<!-- eslint-disable vue/no-use-v-if-with-v-for -->
<template>
  <div
    class="column full-height no-wrap border-box kettalComponent beatriceRegular"
    test-id="KTable"
  >
    <q-table
      ref="table"
      v-model:selected="selected"
      :visible-columns="localVisibleColumns"
      :columns="localColumns"
      :hide-bottom="hideBottom"
      :loading="tableLoading || rows.length === 0"
      :rows-per-page-options="rowsPerPageOptions"
      :rows="filteredRows"
      :selection="selection || action.selection"
      :class="
        'my-sticky-header-table bg-k-white kTable flex-grow ' + backgroundColor
      "
      flat
      :row-key="action.rowKey as string"
      :style="'background-color:' + backgroundColor"
      virtual-scroll
    >
      <!-- Top Buttons and controlers on table -->
      <template #top>
        <slot name="beforeTop" />
        <!-- This part contains the global search and advanced search plus column selection for the table -->
        <KTableTop
          v-if="!hideTableTop"
          v-model:visible-columns="visibleColumns"
          :page-actions="tableActions"
          :selection="selection || action.selection"
          :columns="localColumns"
          :header-color="headerColor"
          :column-drawer-open="columnDrawerOpen"
          :selected="selected"
          :has-active-filters="hasActiveFilters"
          @toggle-advanced-filters="toggleAdvancedFilters"
          @clear-filters="clearFilters"
          @on-search="
            (params) => {
              searchFilter = params;
              sortAndFilterTable();
            }
          "
          @active-action="
            (parameters) => {
              selected = [];
              activeAction(parameters);
            }
          "
          @on-confirm-selection="confirmSelection"
          @on-cancel-selection="
            () => {
              selected = [];
              clearAction();
            }
          "
        />
        <slot name="afterTop" />
      </template>

      <!-- Header of the table -->
      <template #header="headerProps">
        <KTableHeaderSlot
          v-model:advanced-filters="advancedFilters"
          :selection="selection || action.selection"
          :props="headerProps"
          :action="action"
          :show-advanced-filters="showAdvancedFilters"
          :rows="rows"
          @sort-by="
            (params) => {
              sortBy(params.col, params.arrow);
            }
          "
        />
      </template>
      <!-- Rows of the table -->
      <template #body="bodyProps">
        <q-tr
          :props="bodyProps"
          class="tableBody"
          :style="customStyleRow(bodyProps.row)"
          test-id="tableRow"
        >
          <q-tooltip
            v-if="
              bodyProps.cols.find(
                (col: any) => col.name === props.tooltipColumn,
              )?.value
            "
            class="text-body2"
            anchor="bottom middle"
            :offset="[0, 0]"
          >
            <div>
              <strong>{{ props.tooltipMessage }}</strong>
            </div>
          </q-tooltip>
          <!-- If row is selectable -->
          <q-td
            v-if="showSelectCheckbox && selectionRules(bodyProps.row)"
            auto-width
          >
            <q-checkbox
              :model-value="bodyProps.selected"
              test-id="selectRowCheckbox"
              @update:model-value="
                (val) => {
                  bodyProps.selected = val;
                }
              "
            />
          </q-td>
          <!-- If row is not selectable leave a space -->
          <q-td
            v-if="showSelectCheckbox && !selectionRules(bodyProps.row)"
            auto-width
          />
          <KTableBodySlot
            :key="bodyProps.key"
            :selection="selection"
            :row-data="bodyProps"
            :can-display-q-menu="
              action.selection !== 'single' && action.selection !== 'multiple'
            "
            :page-actions="tableActions"
            @update-table-value="(params) => updateTableValue(params)"
            @active-action-from-row="
              (action, row) => {
                activeActionFromRow(action, row);
              }
            "
            @on-click-row="
              (row, isFromQMenu = false) => {
                selectRow(row, isFromQMenu, bodyProps);
              }
            "
          />
        </q-tr>
      </template>
      <!-- Grid items -->
      <template #item="itemProps">
        <q-card class="full-width">
          <div>{{ itemProps.key }}</div>
        </q-card>
      </template>
      <template #top-right>
        <q-input
          v-model="searchFilter"
          borderless
          dense
          debounce="300"
          placeholder="Search"
        >
          <template #append>
            <q-icon name="search" />
          </template>
        </q-input>
      </template>
      <template #pagination />
      <template #loading>
        <q-inner-loading
          v-if="tableLoading"
          showing
          color="primary"
          style="z-index: 3"
          test-id="loadingTable"
        />
        <q-inner-loading
          v-else-if="rows.length === 0"
          showing
          color="primary text-h4"
          style="z-index: 3"
          label="Test"
          test-id="noDataInTableLoading"
        >
          {{ $t("noDataInTable") }}
        </q-inner-loading>
      </template>
      <template #bottom>
        <div class="column full-width">
          <div
            class="full-width bg-k-accent text-k-white q-pa-sm"
            test-id="bottomTableActionTitle"
          >
            {{ action.title }}
          </div>
          <div class="lt-md">
            <TableBottomActionButtons
              :selected="selected"
              @on-confirm-selection="confirmSelection"
              @on-cancel-selection="
                () => {
                  selected = [];
                  clearAction();
                }
              "
            />
          </div>
        </div>
      </template>
    </q-table>
    <!-- <GridTable :selection="selection" @select-row="(item) => { selectRow(item) }" :rowKey="typeof rowKey === 'string' ? [rowKey] : rowKey" :rows="filteredRows" v-model:selected="selected" :columns="columns" :pageActions="tableActions" :selection="action.selection" :action="action"></GridTable> -->
  </div>
</template>
<script setup lang="ts" generic="T extends TableRow">
import { onMounted, ref, watch, nextTick, computed } from "vue";
import {
  KettalColumnSort,
  KettalPageActions,
  KettalQTableColumn,
  KettalTableAdvancedFilters,
  KettalVisibleColumn,
} from "../../types/KTable";
import KTableBodySlot from "./KTableComponents/KTableBodySlot/KTableBodySlot.vue";
import KTableHeaderSlot from "./KTableComponents/KTableHeaderSlot/KTableHeaderSlot.vue";
import KTableTop from "./KTableComponents/KTableTop.vue";
import TableBottomActionButtons from "./KTableComponents/TableBottomActionButtons.vue";
import createRowKey from "./helpers/createRowKey";
import { TableRow } from ".";
import { QTableColumn } from "quasar";
import usePrepareTableColumns from "./hooks/prepareTableColumns";
import useTableAction from "./hooks/tableAction";
import useAdvancedFilters from "./hooks/advancedFilters";
import useSortAndFilterTable from "./hooks/sortAndFilterTable";
import useUrlSync from "./hooks/useUrlSync";

const props = withDefaults(
  defineProps<{
    /** Boolean that indicates if table is in loading mode */
    tableLoading?: boolean;
    /** List that contains the actions for the actual table */
    tableActions?: KettalPageActions[];
    /** Function that inserts a custom styling into a row */
    customStyleRow?: Function;
    /** Background color of the top header of the table */
    backgroundColor?: string;
    /** The column definitions (Array of Objects) */
    columns: KettalQTableColumn[];
    /** Rows of data to display */
    rows: T[];
    /** */
    startSortedBy?: KettalColumnSort[];
    /** Header Color */
    headerColor?: string;
    /** Key of the row */
    rowKey: string | string[];
    /** Hide the table top */
    hideTableTop?: boolean;
    selection?: "none" | "single" | "multiple" | undefined;
    tooltipColumn?: string;
    tooltipMessage?: string;
    /** External filters to apply to the table fields. Keys are column names, values are filter values. */
    externalFilters?: KettalTableAdvancedFilters;
    /** When true, column visibility and order are synced to the URL query string */
    syncColumnsWithUrl?: boolean;
    /** When true, advanced filters are synced to the URL query string */
    syncFiltersWithUrl?: boolean;
  }>(),
  {
    tableLoading: false,
    tableActions: () => [],
    customStyleRow: () => "",
    backgroundColor: "#fff8f2",
    headerColor: "#faebda",
    columns: () => [],
    rows: () => [],
    startSortedBy: () => {
      return [{ column: "", isAscending: "" }];
    },
    hideTableTop: false,
    selection: undefined,
    tooltipColumn: "",
    tooltipMessage: "",
    externalFilters: () => ({}),
    syncColumnsWithUrl: false,
    syncFiltersWithUrl: false,
  },
);

const emit = defineEmits([
  "callFunction",
  "updateTableValue",
  "openPopup",
  "rowClick",
  "filterChange",
]);

onMounted(() => {
  clearAction();
  syncExternalFilters(props.externalFilters);
});

const table = ref();

const selected = defineModel("selected", {
  type: Array,
  required: true,
  default: [],
});
const rowsPerPageOptions = ref([0, 25, 10, 50]); // Configuration for number of rows shown per page
const columnDrawerOpen = ref<boolean>(false);

const rowKeyRef = ref(props.rowKey);
const { action, activeAction, clearAction, confirmSelection, hideBottom } =
  useTableAction(rowKeyRef, selected, emit);

const visibleColumns = defineModel<KettalVisibleColumn[]>("visibleColumns", {
  type: Array,
  required: false,
  default: [],
});
const { localColumns, localVisibleColumns } = usePrepareTableColumns(
  visibleColumns,
  props,
);

const {
  advancedFilters,
  showAdvancedFilters,
  toggleAdvancedFilters: _toggleAdvancedFilters,
} = useAdvancedFilters();

// Sync column visibility/order and filters with URL query params
useUrlSync(
  props.syncColumnsWithUrl ?? false,
  props.syncFiltersWithUrl ?? false,
  visibleColumns,
  advancedFilters,
  props.columns,
);

// Wrap toggleAdvancedFilters to re-sync external filters after toggling
function toggleAdvancedFilters() {
  _toggleAdvancedFilters();
  syncExternalFilters(props.externalFilters);
}

const { sortAndFilterTable, searchFilter, filteredRows, sortBy } =
  useSortAndFilterTable(props, advancedFilters);

// Flag to avoid reactive loop between externalFilters and advancedFilters
let isSyncingExternal = false;

// Sync external filters into advancedFilters so the UI inputs reflect them
function syncExternalFilters(filters: KettalTableAdvancedFilters | undefined) {
  if (!filters) return;
  isSyncingExternal = true;
  for (const key of Object.keys(filters)) {
    const value = filters[key];
    // Find the column definition to check its type
    const column = props.columns.find((col) => col.name === key);
    // If the column is a select/option type, ensure the value is an array
    if (column && (column.type === "select" || column.type === "option")) {
      if (Array.isArray(value)) {
        advancedFilters.value[key] = value;
      } else if (typeof value === "string") {
        advancedFilters.value[key] = [value];
      }
    } else {
      advancedFilters.value[key] = value;
    }
  }
  nextTick(() => {
    isSyncingExternal = false;
  });
}

const hasActiveFilters = computed(() => {
  const keys = Object.keys(advancedFilters.value);
  return keys.some((key) => {
    const val = advancedFilters.value[key];
    if (val === null || val === undefined || val === "null") return false;
    if (Array.isArray(val)) return val.length > 0;
    if (typeof val === "string") return val.trim() !== "";
    if (typeof val === "boolean") return true;
    return false;
  });
});

function clearFilters() {
  advancedFilters.value = {};
  emit("filterChange", {});
}

// Watch for external filter changes and sync into advancedFilters
watch(
  () => props.externalFilters,
  (newFilters) => {
    syncExternalFilters(newFilters);
  },
  { deep: true },
);

// Watch for advanced filter changes and emit the combined filters
watch(
  advancedFilters,
  (newFilters) => {
    if (!isSyncingExternal) {
      emit("filterChange", { ...newFilters });
    }
  },
  { deep: true },
);

watch(
  () => props.rowKey,
  (newVal) => {
    rowKeyRef.value = newVal;
    if (typeof props.rowKey === "object") {
      filteredRows.value = createRowKey(props.rows, props.rowKey);
    } else {
      filteredRows.value = props.rows;
    }
    sortAndFilterTable();
  },
  { deep: true },
);

function activeActionFromRow(
  action: KettalPageActions,
  row: { [key: string]: string },
) {
  selectRow(row, true);
  if (action.type !== "expand") {
    activeAction(action);
  }
}

/**
 * Function that selects all rows
 *
 * @public
 */

function selectRow(
  row: { [key: string]: string },
  isFromQMenu = false,
  bodyProps = { selected: false },
) {
  emit("rowClick", row);
  if (
    (action.value.selection === "none" && !isFromQMenu) ||
    !selectionRules(row)
  ) {
    return;
  }
  const rowKey: string = action.value.rowKey
    ? (action.value.rowKey as string)
    : typeof props.rowKey === "string"
      ? props.rowKey
      : "rowId";
  const index: number = selected.value.findIndex((value: unknown) => {
    const selectedRow = value as { [key: string]: string };
    return selectedRow[rowKey] === row[rowKey];
  });
  if (index !== -1) {
    selected.value.splice(index, 1);
    return;
  }
  if (action.value.selection === "single") {
    selected.value = [];
  }
  if (isFromQMenu) {
    selected.value.push(row);
    return;
  }
  bodyProps.selected = true;
}

function selectionRules(row: TableRow) {
  if (action.value.actionSelectionRules === null) return true;
  if (action.value.actionSelectionRules === undefined) return true;
  return action.value.actionSelectionRules(row);
}

async function updateTableValue(params: {
  value: string | boolean | number;
  initialValue: string | boolean;
  row: TableRow;
  col: QTableColumn;
}) {
  await nextTick();
  emit(
    "updateTableValue",
    params.value,
    params.initialValue,
    params.row,
    params.col,
  );
}

function openColumnDrawer() {
  columnDrawerOpen.value = !columnDrawerOpen.value;
}

const showSelectCheckbox = computed(() => {
  const selectionFromProps =
    props.selection === "multiple" || props.selection === "single";
  const selectionFromAction =
    action.value.selection === "multiple" ||
    action.value.selection === "single";
  return selectionFromProps || selectionFromAction;
});

function scrollTo(index: number) {
  console.log(index);
  table.value.scrollTo(index);
}

defineExpose({
  activeAction,
  openColumnDrawer,
  scrollTo,
});
</script>

<style lang="scss"></style>
