<template>
  <div class="mc-datatable" :class="classObject" :style="styleObject">
    <!-- @slot Use this slot to insert a topbar into the component. We recommend using the `MDataTableTop` component here. -->
    <slot name="topbar"></slot>

    <!-- @slot Use this slot to insert a selectionbar into the component. We recommend using the `MDataTableSelectionBar` component here. -->
    <slot name="selectionbar">
      <MDataTableSelectionBar
        v-if="selectionbar && multiSelectionPage"
        :select-all="getSelectAll"
        :label-page-selected="getLabelSelectedPage"
        :label-all-items-selected="getLabelAllItemsSelected"
        :label-button-select-all="getLabelButtonSelectAll"
        :label-button-clear-selection="labelButtonClearSelection"
        @select-all="state.selectAll = true"
        @clear-all="clearAll"
      />
    </slot>

    <div class="mc-datatable__container">
      <!-- MAIN -->
      <div class="mc-datatable__main" :style="{ 'max-height': maxHeight }">
        <MDataTableEmpty v-if="items.length === 0 && !loading">
          <!-- @slot Use this slot to insert an error message if the component is empty. An empty state is defined if there are no data/results. -->
          <slot name="no-data" />
        </MDataTableEmpty>

        <table
          v-else
          :class="!nested ? 'mc-datatable__table' : 'mc-datatable__subtable'"
        >
          <thead>
            <tr>
              <!-- @slot Use this slot to replace the array's `<th>` headers with your own. We recommend using the `MDataTableHeaderV2` component when using this slot.  -->
              <slot name="headers">
                <!-- Selectable -->
                <MDataTableHeaderV2 v-if="selectable" selectable>
                  <div class="mc-checkbox">
                    <input
                      id="masterCheckbox"
                      v-model="state.pageState"
                      type="checkbox"
                      class="mc-checkbox__input"
                      aria-label="Select all rows"
                      :indeterminate="isPageIndeterminate"
                      :disabled="!items.length || loading"
                      @change="onClickMasterCheckbox"
                    />
                  </div>
                </MDataTableHeaderV2>

                <!-- Expandable -->
                <MDataTableHeaderV2 v-if="expandable" expandable />

                <!-- Headers from Data -->
                <MDataTableHeaderV2
                  v-for="(header, index) in getHeaders"
                  :key="`header-${index}`"
                  v-bind="{
                    ...header,
                    sortDirection: getSortDirection(header.value),
                    additionnalProps: header.additionnalProps,
                  }"
                  @update:sort-direction="updateSort"
                >
                  <!-- @slot Use this slot to replace/format the content inside a header cell -->
                  <slot :name="`header.${header.value}`" :header="header" />
                </MDataTableHeaderV2>
              </slot>
            </tr>
          </thead>
          <tbody>
            <!-- @slot Use this slot to replace the entire content of the `<tbody>` tag -->
            <slot name="body">
              <template v-if="!items.length && loading">
                <MDataTableRow v-for="n in getItemsPerPage" :key="n">
                  <MDataTableCell v-if="selectable" type="checkbox">
                    <span class="skeleton-cell">&nbsp;</span>
                  </MDataTableCell>
                  <MDataTableCell v-if="expandable" type="expand">
                    <span class="skeleton-cell">&nbsp;</span>
                  </MDataTableCell>
                  <td
                    v-for="(header, index) in getHeaders"
                    :key="`skeleton-cell-${index}`"
                  >
                    <span class="skeleton-cell">&nbsp;</span>
                  </td>
                </MDataTableRow>
              </template>

              <template v-else>
                <template
                  v-for="(item, rowIndex) in props.items"
                  :key="item[props.indexKey]"
                >
                  <MDataTableRow
                    :selected="isSelected(item)"
                    :class="rowClass"
                    :clickable="getClickable"
                    :expandable="expandable"
                    :expanded="expandedState(item[dataKeyExpand] ?? rowIndex)"
                    :tabindex="getClickable ? 0 : null"
                    @keyup.enter="getClickable && onClickRow(item, rowIndex)"
                  >
                    <template #default>
                      <!-- Cell (Selectable) -->
                      <MDataTableCell v-if="selectable" type="checkbox">
                        <div class="mc-checkbox">
                          <input
                            v-model="state.selectedItems"
                            type="checkbox"
                            class="mc-checkbox__input"
                            :aria-label="`Select rows n°${rowIndex}`"
                            :value="item"
                            :disabled="loading"
                            @click="
                              onSelectRowWithShift($event, item, rowIndex)
                            "
                          />
                        </div>
                      </MDataTableCell>

                      <!-- Cell (Expandable) -->
                      <MDataTableCell v-if="expandable" type="expand">
                        <MDataTableExpandButton
                          v-if="displayExpandButton(rowIndex)"
                          :label="accessibilityLabels?.expandButtonLabel"
                          :expanded="
                            expandedState(item[dataKeyExpand] ?? rowIndex)
                          "
                          :disabled="loading"
                          @click="onClickExpandButton($event, item, rowIndex)"
                        />
                      </MDataTableCell>

                      <!-- Cells from Data -->
                      <MDataTableCell
                        v-for="(header, cellIndex) in getHeaders"
                        :key="`${rowIndex}-${cellIndex}`"
                        :class="header.class"
                        :value="header.value"
                        :type="header.type"
                        @click="getClickable && onClickRow(item, rowIndex)"
                      >
                        <span v-if="loading" class="skeleton-cell">&nbsp;</span>

                        <!-- @slot Use this slot to replace/format the content inside a table cell -->
                        <slot
                          v-else
                          :name="`cell.${header.value}`"
                          :item="item"
                          :index="rowIndex"
                          :row-index="rowIndex"
                        >
                          {{ item[header.value] }}
                        </slot>
                      </MDataTableCell>
                    </template>
                  </MDataTableRow>
                  <MDataTableRowExpand
                    v-if="expandedState(item[dataKeyExpand] ?? rowIndex)"
                    :headers="getHeaders.length"
                    :expandable="expandable"
                    :selectable="selectable"
                  >
                    <template #default>
                      <!-- @slot Use this slot to insert content inside the expand row -->
                      <slot
                        name="expandContent"
                        :item="item"
                        :index="rowIndex"
                        :row-index="rowIndex"
                      />
                    </template>
                  </MDataTableRowExpand>
                </template>
              </template>
            </slot>
          </tbody>
        </table>
      </div>

      <!-- FOOTER -->
      <div v-if="props.pageable" class="mc-datatable__footer">
        <!-- DataTable Rows Per Page Selector -->
        <MDataTableRowsPerPage
          :label="props.itemsPerPageLabel"
          :model-value="getItemsPerPage"
          :loading="loading"
          :options="getItemsPerPageOptions"
          @update:model-value="updatePageSize($event)"
        />

        <!-- DataTable Info (count) -->
        <MDataTableInfo
          v-if="!props.hidePageCount"
          :label="props.countLabel"
          :label-items="props.infoLabelItems"
          :start="getPageCountInfos.start"
          :end="getPageCountInfos.end"
          :total="getPageCountInfos.total"
        />

        <!-- DataTable Pagination -->
        <div class="mc-datatable__pagination">
          <MPagination
            :value="getCurrentPage"
            :length="getPaginationLength"
            :disabled="loading"
            :show-partial="showPartialPages"
            :select-aria-label="accessibilityLabels?.paginationAriaLabel"
            @update:value="updateCurrentPage"
          >
            <template #text="{ option }">
              <!-- @slot Use this slot to replace/format the text of pagination option items -->
              <slot name="paging.text" :option="option">
                {{ option.text }}
              </slot>
            </template>
          </MPagination>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import {
  defineComponent,
  computed,
  reactive,
  useSlots,
  onMounted,
  watch,
} from 'vue';

const defaultPageSizes = [7, 10, 25, 50, 100];
export default defineComponent({ name: 'MDataTableV2' });
</script>

<script setup>
import MPagination from '../pagination/MPagination.vue';
import MDataTableHeaderV2 from './MDataTableHeaderV2.vue';
import MDataTableRow from './MDataTableRow.vue';
import MDataTableRowExpand from './MDataTableRowExpand.vue';
import MDataTableExpandButton from './MDataTableExpandButton.vue';
import MDataTableCell from './MDataTableCell.vue';
import MDataTableEmpty from './MDataTableEmpty.vue';
import MDataTableInfo from './MDataTableInfo.vue';
import MDataTableRowsPerPage from './MDataTableRowsPerPage.vue';
import MDataTableSelectionBar from './MDataTableSelectionBar.vue';

const props = defineProps({
  /**
   * An array of objects representing the data to be displayed.
   */
  items: {
    type: Array,
    required: true,
  },
  /**
   * An array of objects used to define the DataTable headers.
   * This property is mandatory if you are not using the `<MDataTableColumn />` component or the the `<MDataTableHeaderV2 />` component to define your headers.
   * @type {[{ label: String, value: String, sortable: Boolean, sortDirectionDefault: 'ASC or DESC', additionnalProps: Object, class: String, e2eSelector: String }]}
   */
  headers: {
    type: Array,
    default: undefined,
  },
  /**
   * Displays the loading state to indicate data load is in progress
   */
  loading: {
    type: Boolean,
    default: undefined,
  },
  /**
   * // TODO: To be defined
   * @ignore
   */
  indexKey: {
    type: String,
    default: 'id',
  },
  // Pagination
  /**
   * Displays the pagination du DataTable _(and the component footer as a whole)_.
   * This prop must be defined with the `totalItems` prop.
   */
  pageable: {
    type: Boolean,
    default: false,
  },
  /**
   * Used to define the value of the Select "Rows per page" when the DataTable is initialised.
   * This value must be one of the values in the `itemsPerPageOptions` array.
   */
  itemsPerPage: {
    type: Number,
    default: undefined,
  },
  /**
   * Used to set the desired values for the Select "Rows per page".
   * @type {[7, 10, 25, 50, 100]}
   */
  itemsPerPageOptions: {
    type: Array,
    default: () => defaultPageSizes,
  },
  /**
   * Sets the default value for the Select "Pagination".
   * The options displayed in Select "Pagination" are calculated on the basis of the values in props `totalItems` & `itemsPerPage`.
   */
  currentPage: {
    type: Number,
    default: undefined,
  },
  /**
   * Used to indicate the total number of data items.
   * This property must be defined as soon as the `pageable` property is defined.
   */
  totalItems: {
    type: Number,
    default: undefined,
  },
  /**
   * Used to define the value of the label associated with the Select "Rows per page".
   */
  itemsPerPageLabel: {
    type: String,
    default: 'Rows per page',
  },
  /**
   * Allow to replaces the preposition "of" in the footer sentence _(for example)_: "15-21 of 50 items".
   */
  countLabel: {
    type: String,
    default: 'of',
  },
  /**
   * Allow to replaces the text "items" in the footer sentence _(for example)_: "15-21 of 50 items".
   */
  infoLabelItems: {
    type: String,
    default: 'items',
  },
  /**
   * Allows you to display only the first 3 & last 3 options in the pagination
   */
  showPartialPages: {
    type: Boolean,
    default: false,
  },
  // Sort
  /**
   * Allows to visually define the default state of a sorted column.<br/>
   * _This prop works in conjunction with a `v-model:sort` Two-way binding or a `@update:sort` event._
   * @type {{ column: string, direction: null | 'ASC' | 'DESC' }}
   */
  sort: {
    type: Object,
    default: undefined,
  },
  /**
   * Allows you yo visually define the default state of one or several sorted columns.<br/>
   * _This prop works in conjunction with a `v-model:sortMultiple` Two-way binding or a `@update:sortMultiple` event._
   * @type {[{ column: string, direction: null | 'ASC' | 'DESC' }]}
   */
  sortMultiple: {
    type: Array,
    default: undefined,
  },
  /**
   * Allows to define wether sorting is done on single column or on multiple columns.<br/>
   * The use of this prop depends on the presence of the `sortable` prop on elements `<MDataTableColumn />`
   * or the presence of a `sortable` key inside object passed to the `headers` prop.
   */
  sortMode: {
    type: String,
    default: 'single',
    validator: (value) => ['single', 'multiple'].includes(value),
  },
  // Display Options
  /**
   * Allows you to change the line size
   * @values s, m, l
   */
  size: {
    type: String,
    default: 'm',
    validator: (value) => ['s', 'm', 'l'].includes(value),
  },
  /**
   * Hide "XX - YY of ZZ items" indication in footer
   */
  hidePageCount: {
    type: Boolean,
    default: false,
  },
  /**
   * Used to define a maximum height for the DataTable
   */
  maxHeight: {
    type: String,
    default: undefined,
  },
  /**
   * Allows you to make the header fixed.
   * This property works in conjunction with the `maxHeight`
   */
  fixedHeader: {
    type: Boolean,
    default: false,
  },
  // Selection
  selectable: {
    type: Boolean,
    default: false,
  },
  /**
   * Displays or hides the selection bar
   */
  selectionbar: {
    type: Boolean,
    default: false,
  },
  /** @ignore */
  selection: {
    type: Array,
    default: () => [],
  },
  /** @ignore */
  labelButtonClearSelection: {
    type: String,
    default: 'Clear selection',
  },
  /** @ignore */
  selectPage: {
    type: Boolean,
    default: false,
  },
  /** @ignore */
  labelPageSelected: {
    type: String,
    default: undefined,
  },
  /** @ignore */
  labelAllItemsSelected: {
    type: String,
    default: undefined,
  },
  /** @ignore */
  selectAll: {
    type: Boolean,
    default: undefined,
  },
  /** @ignore */
  labelButtonSelectAll: {
    type: String,
    default: undefined,
  },
  /**
   * Makes rows clickable. **Caution! This prop is deprecated and will be removed soon!**
   * Use the `clickable` prop instead.
   * @deprecated
   */
  clickableRows: {
    type: Boolean,
    default: false,
  },
  /**
   * Makes rows clickable
   */
  clickable: {
    type: Boolean,
    default: false,
  },
  // Expansion
  /**
   * Allows all the rows in the DataTable to be expandable
   */
  expandable: {
    type: Boolean,
    default: false,
  },
  /**
   * Sets the default text for the "fold/unfold" buttons. Useful for enhancing accessibility
   */
  expanderAccessibleLabel: {
    type: String,
    default: undefined,
  },
  /**
   * Allows to choose the rows on which to insert "expandable" content
   */
  expandableList: {
    type: Array,
    default: () => [],
  },
  /**
   * @ignore
   */
  dataKeyExpand: {
    type: String,
    default: undefined,
  },
  /**
   * Open expandable content by default
   */
  showExpandableContent: {
    type: Boolean,
    default: false,
  },
  /**
   * Allows to pass identical custom CSS classes to all the rows in the table
   */
  rowClass: {
    type: [String, Array, Object],
    default: undefined,
  },
  /**
   * An object for passing labels to make component elements more accessible
   * @type {{ paginationAriaLabel: string, checkboxAriaLabel: string, expandButtonLabel: string }}
   */
  accessibilityLabels: {
    type: Object,
    default: undefined,
  },
  // Misc
  e2eSelector: {
    type: String,
    default: '',
  },
  /**
   * Defines whether this is a DataTable nested within another DataTable
   */
  nested: {
    type: Boolean,
    default: false,
  },
  /**
   * Allows you to pass the z-index for the static header.
   */
  zIndex: {
    type: Number,
  },
});

const emit = defineEmits([
  /**
   * Emits on "Single Sort" mode, each time a click on the sort button of a column is made
   */
  'update:sort',
  /**
   * Emits on "Multiple Sort" mode, each time a click on the sort button of a column is made
   */
  'update:sortMultiple',
  'update:currentPage',
  'update:itemsPerPage',
  'change',
  'click-row',
  'select-row',
  'unselect-row',
  'select-all-rows',
  'unselect-all-rows',
  'select-multiple-rows',
  'unselect-multiple-rows',
  'update:selectPage',
  'update:selectAll',
  'update:selection',
  'expand-row',
  'retract-row',
]);

const slots = useSlots();
const slotHeaders = reactive({
  default: null,
  headers: null,
});

const defaultPageSizeOptions = defaultPageSizes.map((value) => ({
  text: String(value),
  value: String(value),
}));

const state = reactive({
  sort: undefined,
  sortMultiple: [],
  itemsPerPageOptions: defaultPageSizeOptions,
  itemsPerPage: 7,
  currentPage: 1,
  isExpand: false,
  selectAll: false,
  selectPage: false,
  pageState: false,
  selectedItems: props.selection,
  lastSelectedItem: undefined,
  expandedRows: [],
});

/** Initialisation */
onMounted(() => {
  if (props.pageable && isNaN(Number(props.totalItems))) {
    throw new Error('MDataTable: totalItems is required when pageable is true');
  }

  if (isPageSelected.value && isPageSelected.value) {
    state.pageState = isPageSelected.value;
  }
  const headersConfig = getHeaders.value;

  // TODO: To be checked - not sure this behaviour is necessary
  headersConfig?.forEach(
    ({ value, sortDirectionDefault, additionnalProps }) => {
      if (sortDirectionDefault) {
        if (getSort.value && sortDirectionDefault) {
          throw new Error(
            'MDataTable: sortDirectionDefault can be set only once per header',
          );
        }

        updateSort(
          {
            column: value,
            direction: sortDirectionDefault,
            ...additionnalProps,
          },
          true,
        );
      }
    },
  );
});

/** Computed */
const getPageCountInfos = computed(() => {
  const {
    itemsPerPage = state.itemsPerPage,
    currentPage = state.currentPage,
    totalItems = 0,
  } = props;

  const itemsOnCurrentPage = currentPage * itemsPerPage;

  return {
    start: itemsOnCurrentPage - itemsPerPage + 1,
    end: Math.min(itemsOnCurrentPage, totalItems),
    total: totalItems,
  };
});

const getPaginationLength = computed(
  () => Math.ceil(Number(props.totalItems) / getItemsPerPage.value) || 1,
);

const getHeaders = computed(
  () => getDatatableHeadersConfigChildrens.value || props.headers,
);

const getPageState = computed({
  get() {
    return state.pageState;
  },
  set(newValue) {
    state.pageState = newValue;
    emit('update:selectPage', newValue);
  },
});

const multiSelectionPage = computed(() => {
  return getSelectedItems.value.length >= props.items.length;
});

const getSelectedItems = computed({
  get() {
    return state.selectedItems || props.selection;
  },
  set(newValue) {
    emit('update:selection', newValue);
  },
});

const getLabelButtonSelectAll = computed(() => {
  return (
    props.labelButtonSelectAll ??
    `Select all ${props.totalItems} rows of this table`
  );
});

const getLabelSelectedPage = computed(() => {
  return (
    props.labelPageSelected ??
    `${getSelectedItems.value.length} rows on this table are selected`
  );
});

const getLabelAllItemsSelected = computed(() => {
  return (
    props.labelAllItemsSelected ??
    `All ${props.totalItems} rows in the table are selected`
  );
});

const isPageSelected = computed(() => {
  const itemsState =
    props.items.length &&
    props.items.every((item) => {
      return state.selectedItems.includes(item);
    });

  return itemsState;
});

const isPageIndeterminate = computed(() => {
  const itemsState = state.selectedItems.some((item) => {
    return props.items.includes(item);
  });

  return itemsState && !isPageSelected.value;
});

const getSelectAll = computed(() => {
  return props.selectAll || state.selectAll;
});

const getClickable = computed(() => {
  return props.clickable || props.clickableRows;
});

const classObject = computed(() => {
  return {
    'mc-datatable--sticky-header': props.fixedHeader,
    [`mc-datatable--${props.size}`]: props.size,
  };
});

const styleObject = computed(() => {
  return {
    '--datatable-head-z-index': props.zIndex,
  };
});

/**
 * Get headers config from default slot
 */

const getDatatableHeadersConfigChildrens = computed(
  () =>
    getHeadersFromSlotName('default', 'MDataTableColumn') ||
    getHeadersFromSlotName('headers', 'MDataTableHeaderV2'),
);

const getItemsPerPageOptions = computed(() => {
  if (!props.itemsPerPageOptions) {
    return state.itemsPerPageOptions;
  }
  if (
    props.itemsPerPageOptions[0] &&
    typeof props.itemsPerPageOptions[0] === 'number'
  ) {
    return props.itemsPerPageOptions.map((option) => {
      const value = option.toString();
      return { text: value, value };
    });
  }
  return props.itemsPerPageOptions;
});

const getItemsPerPage = computed(
  () => props.itemsPerPage || state.itemsPerPage,
);

const getSort = computed(() => props.sort || state.sort);

const getSortMultiple = computed(
  () => props.sortMultiple || state.sortMultiple,
);

const getCurrentPage = computed(() => props.currentPage || state.currentPage);

const getSortDirection = (headerKey) => {
  // Multiple Sorting
  if (props.sortMode === 'multiple') {
    const sortMultiple = getSortMultiple.value;

    const currentSortColumn = sortMultiple.find(
      (sort) => sort.column === headerKey,
    );

    return currentSortColumn?.direction;
  }

  // Single Sorting
  const sort = getSort.value;

  if (!sort || headerKey !== sort.column) {
    return undefined;
  }

  return sort.direction;
};

/** Methods */
function updateSort(sort, noChange = false) {
  // Update the value of the `sortDirection` key
  // in the header object of the associated column
  getHeaders.value.map((header) => {
    if (header.value === sort.column) {
      header.sortDirection = sort.direction;
    }
  });

  if (props.sortMode !== 'multiple') {
    // Single Sorting
    state.sort = sort;

    emit('update:sort', sort);
  } else {
    // Multiple Sorting
    state.sortMultiple = getSortHeaders(sort);

    emit('update:sortMultiple', state.sortMultiple);
  }

  if (!noChange) {
    emitChange({ sort });
  }
}

function updatePageSize(size) {
  state.itemsPerPage = size;
  emit('update:itemsPerPage', size);
  emitChange({ size });
}

function updateCurrentPage(page) {
  state.currentPage = page;

  emit('update:currentPage', page);
  emitChange({ page });
}

function emitChange({
  size = undefined,
  sort = undefined,
  page = undefined,
  sortMultiple = undefined,
} = {}) {
  const payload = {
    sort: sort || getSort.value,
    size: size || getItemsPerPage.value,
    page: page || getCurrentPage.value,
    sortMultiple: sortMultiple || getSortMultiple.value,
  };

  if (getPaginationLength.value < state.currentPage) {
    state.currentPage = getPaginationLength.value;
    payload.page = state.currentPage;
  }

  emit('change', payload);
}

function getHeadersFromSlotName(slotName, componentName) {
  const config = extractComponentFromVnodesByType(
    slotHeaders[slotName],
    componentName,
  )?.map(({ props: itemProps }) => ({
    ...itemProps,
    sortDirection:
      (props.sort?.column === itemProps?.value && props.sort?.direction) ||
      itemProps?.defaultSortDirection,
  }));

  return config || null;
}

function extractComponentFromVnodesByType(vnodes, type) {
  if (!Array.isArray(vnodes)) {
    return null;
  }
  return vnodes.reduce((components, vnode) => {
    if (vnode.type.name === type) {
      components.push(vnode);
    } else if (typeof vnode.type === 'symbol') {
      components.push(
        ...extractComponentFromVnodesByType(vnode.children, type),
      );
    }
    return components;
  }, []);
}

function onClickRow(rowData, rowIndex) {
  emit('click-row', { data: rowData, index: rowIndex });
}

function onSelectRow(event, rowData, rowIndex) {
  const eventName = event.target.checked ? 'select-row' : 'unselect-row';
  const eventArg = { data: rowData, index: rowIndex };

  if (event.target.checked) {
    state.selectedItems = [...state.selectedItems, rowData];
  } else {
    state.selectedItems;
  }
  emit(eventName, eventArg);
  state.lastSelectedItem = rowData;
}

function onSelectRowWithShift(event, rowData, rowIndex) {
  if (!event.shiftKey) {
    onSelectRow(event, rowData, rowIndex);
    return;
  }
  if (
    state.lastSelectedItem !== undefined &&
    state.lastSelectedItem !== rowData
  ) {
    let inBetween = false;
    const itemsInBetween = [];
    props.items.forEach((item) => {
      if (item === state.lastSelectedItem || item === rowData) {
        inBetween = !inBetween;
      }
      if (inBetween) {
        itemsInBetween.push(item);
      }
    });
    itemsInBetween.push(rowData);
    itemsInBetween.push(state.lastSelectedItem);
    if (event.target.checked) {
      // Remove duplicates by converting to a Set and back to an array
      state.selectedItems = [
        ...new Set([...state.selectedItems, ...itemsInBetween]),
      ];
      emit('select-multiple-rows', { data: itemsInBetween });
    } else {
      state.selectedItems = state.selectedItems.filter(
        (selected) => !itemsInBetween.includes(selected),
      );
      emit('unselect-multiple-rows', { data: itemsInBetween });
    }
  }
  state.lastSelectedItem = rowData;
}

function onPageSelected(checked) {
  if (checked) {
    props.items.forEach((item) => {
      if (!state.selectedItems.includes(item)) {
        state.selectedItems.push(item);
      }
    });

    emit('select-all-rows', { data: state.selectedItems });
  } else if (!checked && !isPageIndeterminate.value) {
    emit('unselect-all-rows');
  }
}

function clearSelection() {
  props.items.forEach((item) => {
    if (state.selectedItems.includes(item)) {
      const index = state.selectedItems.findIndex(
        (selected) => String(selected) === String(item),
      );

      state.selectedItems.splice(index);
    }
  });
}

function clearAll() {
  state.selectedItems = [];
  state.selectAll = false;
}

function onClickMasterCheckbox($event) {
  const checked = $event && $event.target.checked;

  onPageSelected(checked);

  if (!checked) {
    clearSelection();
  }
}

function getSortHeaders(sort) {
  const sortHeaders = getHeaders.value.filter(
    (header) => header.sortDirection != null,
  );

  return sortHeaders.map((header) => {
    return { column: header.value, direction: header.sortDirection };
  });
}

/* watch */
watch(isPageSelected, (value) => {
  getPageState.value = value;
});

watch(
  () => state.selectedItems,
  (value) => {
    emit('update:selection', value);
  },
);

watch(
  () => props.selection,
  (value) => {
    state.selectedItems = value;
  },
);

watch(
  () => props.items,
  () => {
    state.selectAll = false;
  },
);

watch(
  () => [props.items, props.expandableList, props.showExpandableContent],
  () => {
    if (props.showExpandableContent) {
      const expandedRows =
        props.expandableList.length > 0
          ? props.expandableList
          : Array.from({ length: props.items.length }, (_, i) => i);
      state.expandedRows = [...expandedRows];
    }
  },
  { immediate: true },
);

watch(
  () => state.selectAll,
  (value) => {
    emit('update:selectAll', value);
  },
);

watch(
  () => props.selectAll,
  (value) => {
    state.selectAll = value;
  },
);

watch(
  () => [slots.default && slots.default(), slots.headers && slots.headers()],
  ([defaultSlot, headersSlot]) => {
    slotHeaders.default = defaultSlot ?? null;
    slotHeaders.headers = headersSlot ?? null;
  },
  { immediate: true },
);

/** Manage Expandable Button */
function displayExpandButton(rowIndex) {
  return (
    props.expandableList.includes(rowIndex) || !props.expandableList.length
  );
}

function onClickExpandButton(event, rowData, rowIndex) {
  const dataKeyExpand = props.dataKeyExpand;
  const rowKey = dataKeyExpand ? rowData[dataKeyExpand] : rowIndex;
  const expanded = state.expandedRows.includes(rowKey);
  const eventArg = { data: rowData, index: rowIndex };

  if (expanded) {
    const index = state.expandedRows.findIndex(
      (row) => String(row) === String(rowKey),
    );

    state.expandedRows.splice(index, 1);

    emit('retract-row', eventArg);
  } else {
    state.expandedRows.push(rowKey);

    emit('expand-row', eventArg);
  }
}

function expandedState(rowKey) {
  return state.expandedRows.includes(rowKey);
}

function isSelected(item) {
  return state.selectedItems.find((selected) => selected.id === item.id)
    ? true
    : false;
}
</script>

<style lang="scss">
@import 'settings-tools/all-settings';
@import 'components/c.checkbox';
@import 'components/c.datatable';
@import 'components/c.datatable-tools';
@import 'components/c.datatable-footer';
@import 'components/c.datatable-subtable';

.mc-datatable {
  $parent: get-parent-selector(&);

  &__main {
    overflow-y: auto;
  }

  &--sticky,
  &--sticky-header {
    #{$parent} {
      &__table {
        > thead {
          z-index: var(--datatable-head-z-index, 11);
        }
      }
    }
  }
}

// Skeleton
.skeleton-cell {
  background-color: #cdd4d8;
  border-radius: get-border-radius('m');
  display: inline-block;
  height: var(--skeleton-height, $mu125);
  overflow: hidden;
  position: relative;
  width: var(--skeleton-width, 100%);

  &::after {
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background-image: linear-gradient(
      90deg,
      rgba(#ffffff, 0) 0,
      rgba(#ffffff, 0.2) 20%,
      rgba(#ffffff, 0.5) 60%,
      rgba(#ffffff, 0)
    );
    animation: skeleton-animation 2s infinite;
    content: '';
  }
}

@keyframes skeleton-animation {
  100% {
    transform: translateX(100%);
  }
}
</style>
