<script lang="tsx">
import {
  computed, defineComponent, nextTick,
  onBeforeMount,
  onBeforeUnmount, onMounted,
  provide,
  ref, watch,
  type ComputedRef,
  type PropType,
  type Ref,
} from 'vue'
import type ApiInterface from '../../../types/api-interface'
import type DataProviderInterface from '../../../types/data-provider-interface'
import {
  type AdditionalTableRow,
  type ColumnInterface,
  type DefaultTableContextActionInterface,
  type DefaultTableContextMenuInterface,
  type DefaultTableInterface,
  type TableSettingsInterface,
} from '../DefaultTableInterfaces'

import { ActionBar, type ActionBarActionInterface } from '../../action-bar'
import ExpandButton from '../../buttons/expand-button/ARKExpandButton.vue'
import CheckBox from '../../checkbox/ARKCheckbox.vue'
import DataLoader from '../../data-loader/ARKDataLoader.vue'
import LineLoader from '../../line-loader/ARKLineLoader.vue'
import RadioButton from '../../radio-button/RadioButton.vue'
import ARKHeader from '../ARKHeader.vue'
import ContextMenu from '../ContextMenu.vue'
import DragAndDropItem from '../drag-and-drop/DragAndDropItem.vue'
import { type DragAndDropSortingSettings } from '../drag-and-drop/DragAndDropItemInterfaces'
import DefaultHeaders from '../headers/DefaultHeaders.vue'
import NoData from '../no-data/NoData.vue'

import useCheckedItems, { type CheckedItemsInterface } from '../../../composables/use-checked-items'
import useEndlessScrollApi, { type OnLoadEventInterface, type paramsSerializationOptions } from '../../../composables/use-endless-scroll-api'
import useMotionTracker from '../../../composables/use-motion-tracker'
import useScrollPagination from '../../../composables/use-scroll-pagination'
import { DebounceRejectError, setCountSpaces } from '../../../helpers'
import { type QuerySerializationStrategy } from '../../../types/QuerySerializationStrategies/QuerySerializationStrategy'
import useColumns from '../composables/useColumns'
import useSort from '../composables/useSort'
import { prepareSmartFilterParams } from '../headers/headers/prepareSmartFilterParams'
import type { DraggableItem } from './types'
import DefaultTableCell from './DefaultTableCell.vue'

type OnLoadEventCallbackInterface = (
  options: OnLoadEventInterface & {
    columns: ColumnInterface[]
  }
) => {
  columns: ColumnInterface[]
}

/**
 * Плоская таблица с пагинацией. Работает только с бэком
 * <ul>
 * <li><a href="https://www.figma.com/file/Q4Z4CnFx4i0qhD5fbgnPpf/%F0%9F%92%BB-%D0%94%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-ARK-SPACE?type=design&node-id=4959-76222&mode=design&t=5LOBA5zTzEZr6q7a-4" target="_blank">дизайн</a></li>
 * </ul>
 */

export default defineComponent({
  // добавляем пропсы в алфавитном порядке
  props: {
    /**
     * Массив действий с выбранными строками, которые отображаются в экшен баре
     */
    actions: {
      type: Array as PropType<ActionBarActionInterface[]>,
      default: () => [],
    },
    /**
     * Разрешить стартовую загрузку таблицы
     */
    allowInitLoading: {
      type: Boolean,
      default: true,
    },
    /**
     * Интерфейс API
     */
    api: {
      type: Object as PropType<ApiInterface>,
      default: undefined,
    },
    /**
     * Интерфейс dataProvider.
     *
     * Обращение к бэку без использования axios
     */
    dataProvider: {
      type: Object as PropType<DataProviderInterface>,
      default: undefined,
    },
    /**
     * Массив кнопок, отображающихся справа над таблицей
     */
    buttons: {
      type: Function as PropType<() => JSX.Element>,
    },
    /**
     * Ключ кеширования настроек колонок таблицы
     */
    cacheKey: {
      type: String,
      default: undefined,
    },

    /**
     * Выбор элементов в таблице
     *
     * @param 'one' - только один элемент
     * @param 'many' - множественный выбор
     * @param 'none' - выключен
     */
    canCheck: {
      type: String as PropType<'one'|'many'|'none'>,
      default: 'none',
    },
    /**
     * Callback для определения блокировки чекбокса в строке
     * Аналог в TreeTable - canItemBeChecked
     */
    canCheckExactItem: {
      type: Function as PropType<(item: any) => boolean>,
      default: () => true,
    },

    /**
     * Callback для определения отображения кнопки "Развернуть\Свернуть" (для полей с "еще")
     */
    canExpandRow: {
      type: Function as PropType<(item: any) => boolean>,
      default: () => true,
    },
    canResizeColumns: {
      type: Boolean,
      default: true,
    },
    /**
     * Интерфейс внешних выбранных элементов
     */
    checkedItemsInterface: {
      type: Object as PropType<CheckedItemsInterface>,
      default: undefined,
    },
    /**
     * Настройка для снятия выделений после фильтрации
     */
    clearCheckedOnParamsChange: {
      type: Boolean,
      default: true,
    },
    /**
     * Массив колонок или фильтров для шапки таблицы
     */
    columns: {
      type: Array as PropType<ColumnInterface[]>,
      required: true,
    },
    /**
     * Массив действий контекстного меню для выбранной строки
     */
    contextActions: {
      type: Array as PropType<DefaultTableContextActionInterface[]>,
      default: () => [],
    },
    /**
     * Отдельный эндпойнт для запроса количества элементов в таблице
     */
    countEndpoint: {
      type: String,
    },
    /**
     * ???
     */
    defaultCheckedItems: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
    /**
     * Дефолтные квери параметры
     */
    defaultQueryParams: {
      type: Object as PropType<Record<string, string|string[]>>,
      default: () => ({}),
    },
    /**
     * Дефолтная сортировка
     */
    defaultSort: {
      type: String,
      default: 'id',
    },
    /**
     * Настройка отключения/скрытия блока над шапкой таблицы
     */
    disableTop: {
      type: Boolean,
      default: false,
    },
    /**
     * ???
     */
    dragAndDropSorting: {
      type: Object as PropType<DragAndDropSortingSettings>,
    },
    /**
     * Эндпоинд, на который будут отправляться запросы на добавление / удаление файла
     */
    endpoint: {
      type: String,
      default: '',
    },

    /**
     * Принудительная заглушка
     * Если передана, то вместо всего тела таблицы (даже с подгруженными данными) отображается именно она
     */
    forcedStub: {
      type: Function as PropType<() => JSX.Element>,
      default: undefined,
    },

    /**
     * Слот для вставки элементов отображающихся над таблицей слева, после количества элементов
     */
    headerInfo: {
      type: Function as PropType<() => JSX.Element>,
    },
    /**
     * Подсветка строки таблицы разными цветами
     */
    highlightedElements: {
      type: Object as PropType<Record<string | number, string>>,
    },
    /**
     * Отображение столбца с кнопками "Развернуть\Свернуть" строку (для полей с "еще")
     */
    isExpandRowButton: {
      type: Boolean,
      default: false,
    },
    /**
     * Настройка блокировки фильтров при отсутствии данных в таблице
     */
    isFilterBlockNoData: {
      type: Boolean,
      default: false,
    },
    /**
     * Принудительное скрытие 'Action bar'а
     */
    isHideActionBar: {
      type: Boolean,
      default: false,
    },
    /**
     * Принудительное скрытие контекстного меню
     */
    isHideContextMenu: {
      type: Boolean,
      default: false,
    },
    /**
     * Позволяет уменьшать размер кнопки "Настройка колонок"
     * при значении true кнопка-шестеренка будет размером 32px
     * по умолчанию false, это 40px
     */
    isSmallSettingsButton: {
      type: Boolean,
      default: false,
    },
    /**
     * Функция, в которой можно привести массив файлов к нужному формату
     */
    itemConverter: {
      type: Function as PropType<(item: any) => any>,
    },
    /**
     * Слот для разметки вставляемой между названием таблицы и количеством элементов в таблице
     */
    leftHeaderSlot: {
      type: Function as PropType<() => JSX.Element>,
    },
    /**
     * Пропс для изменения текста заглушек
     */
    noDataText: {
      type: String,
      default: '',
    },

    /**
     * Пропс для изменения текста кнопки, отображаемой в заглушке
     */
    noDataButtonLabel: {
      type: String,
      default: 'Добавить',
    },

    /**
     * Функция-колбэк для кнопки в заглушке. При отсвутствии пропса эта кнопка не отображается
     */
    noDataButtonClick: {
      type: Function as PropType<() => void>,
    },
    /**
     * Функция выбора элементов
     */
    onCheckItem: {
      type: Function as PropType<(items: any[]) => void>,
      default: () => {
        // do nothing
      },
    },
    /**
     * Функция смены count элементов ???
     */
    onInitialCountUpdate: {
      type: Function as PropType<(v: number) => void>,
    },
    /**
     * Коллбек, срабатывающий при добавлении новых элементов в таблицу
     */
    onItemsLoaded: {
      type: Array as PropType<OnLoadEventCallbackInterface[]>,
    },
    /**
     * Функция действия производимого по клику на колонку
     */
    onRowClick: {
      type: Function as PropType<(item: any) => void>,
    },
    /**
     * Функция смены count элементов
     */
    onTableCountUpdate: {
      type: Function as PropType<(v: number) => void>,
    },
    /**
     * Тип пагинации скроллом или постраничная
     */
    paginationType: {
      type: String,
    },
    /**
     * Тип сериализации квери параметров: default, custom, comma
     * - default - стандартное перечисление через запятую (name: 123, 234)
     * - custom - (name: 123, name: 234)
     * - comma - что-то новое и не использовал, судя по названию вариант custom
     */
    paramsSerialization: {
      type: String as PropType<paramsSerializationOptions>,
      default: 'default',
    },
    /**
     * Объект, в который прокидывается экземпляр класса QuerySerializationStrategy со своей функцией сериализации параметров - serialize
     */
    queryParamsSerializationStrategy: {
      type: Object as PropType<QuerySerializationStrategy>,
    },
    /**
     * Ключ пагинации текущей страницы
     */
    requestPageKey: {
      type: String,
    },
    /**
     * Ключ пагинации куда указывать количество элементов запрашиваемое в запросе к бэку на одной странице пагинации
     */
    requestPerPageKey: {
      type: String,
    },
    /**
     * Количество элементов запрашиваемое в запросе к бэку на одной странице панинации
     */
    requestSize: {
      type: Number,
      default: 10,
    },
    /**
     * Ключ количества всех элементов
     */
    responseTotalKey: {
      type: String,
    },
    /**
     * Настройка сохранять или нет дефолтных фильтров
     */
    saveDefaultFilterValueOnReset: {
      type: Boolean,
    },
    /**
     * Настройка вкл/выкл умных фильтров
     */
    smartFilter: {
      type: Boolean,
      default: false,
    },
    /**
     * Настройка отображения кнопки, настраивающей колонки таблицы
     */
    showSettings: {
      type: Boolean,
      default: true,
    },
    /**
     * Настройка отображения количества элементов
     */
    showCount: {
      type: Boolean,
      default: true,
    },
    /**
     * Ключ для сортировки
     */
    sortFieldKey: {
      type: String,
      required: true,
    },
    /**
     * Флаг, убирающий кнопку сортировки в заголовке колонки
     */
    enableSorting: {
      type: Boolean,
      default: true,
    },
    /**
     * Массив элементов прилипающих под шапкой таблицы
     */
    stickyItems: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
    /**
     * Название таблицы
     */
    title: {
      type: String,
      default: undefined,
    },
    /**
     * Массив элементов для итоговой строки таблицы
     */
    totalItems: {
      type: Array as PropType<any[]>,
      default: () => [],
    },
    /**
     * Загружать количетсво элементов таблицы отдельным запросом
     */
    useAsyncCountLoad: {
      type: Boolean,
    },
    /**
     * Использовать для пажинации флаги наличия следующей и предыдущей страницы вместо флага count
     */
    usePrevNextFlags: {
      type: Boolean,
      default: false,
    },
    /**
     * Ключ флага наличия следующей страницы в ответе бекенда
     */
    nextPageFlagKey: {
      type: String,
    },
    /**
     * Ключ флага наличия предыдущей страницы в ответе бекенда
     */
    prevPageFlagKey: {
      type: String,
    },
  },

  setup(props, context) {
    const isLoadingPrev = ref(false)
    const isLoadingNext = ref(false)
    const isUseModalFilters: Ref<boolean> = ref(false)

    const triggerDown = ref(null) as unknown as Ref<HTMLElement>
    const triggerUp = ref(null) as unknown as Ref<HTMLElement>
    const root = ref(null) as unknown as Ref<HTMLElement>
    const body = ref(null) as unknown as Ref<HTMLElement>
    const rootContainer = ref(null) as unknown as Ref<HTMLElement>
    const checkboxCell = ref<HTMLElement | null>(null)
    const sortingCell = ref<HTMLElement | null>(null)
    const draggedItem: Ref<DraggableItem | undefined> = ref(undefined)
    const settings = ref() as unknown as Ref<TableSettingsInterface>
    const dataLoaderOutsideActive = ref(true)
    const dataLoaderInsideActive = ref(false)

    let initResize = false
    let blockReloadFlag = false

    const resizeObserver = new ResizeObserver((entry) => {
      const viewportWidth: number = entry[0].target.clientWidth ?? 0
      const viewportHeight: number = entry[0].target.clientHeight ?? 0

      isUseModalFilters.value = viewportWidth <= 900 || viewportHeight <= 500
    })

    const {
      sortField, sortOrder, computedSortField, setSort,
    } = useSort(props.defaultSort, props.cacheKey)

    const staticColumns = props.columns.map((c) => ({ ...c, isStatic: true }))
    const dynamicColumns = ref<ColumnInterface[]>([...staticColumns])
    const bothStaticAndDynamicColumns = computed(() => ([...dynamicColumns.value]))

    const { columns, computedColumns, params } = useColumns(bothStaticAndDynamicColumns, props.defaultQueryParams, props.cacheKey, props.saveDefaultFilterValueOnReset)

    const smartFilterParams = computed(() => prepareSmartFilterParams('none', {}, params.value))

    const onLoadEventCallback = (options: {
      allItems: any[]
      loadedItems: any[]
      unloadedItems: any[]
    }) => {
      if (props.onItemsLoaded && Array.isArray(props.onItemsLoaded)) {
        blockReloadFlag = true
        const result = props.onItemsLoaded.reduce((acc, f) => {
          const result = f({ ...options, columns: staticColumns })
          return {
            columns: [...acc.columns, ...(result?.columns ? result.columns : [])],
          }
        }, {
          columns: [] as ColumnInterface[],
        })

        dynamicColumns.value = result.columns

        nextTick(() => {
          blockReloadFlag = false
        })
      }
    }

    const {
      initialLoad,
      items,
      loadNext,
      loadPrev,
      count,
      offset,
      startOffset,
      endOffset,
      refresh,
      reload,
      isLoading: isLoadingScrollApi,
      isCountLoading,
    } = useEndlessScrollApi<unknown>({
      params: smartFilterParams,
      endpoint: props.endpoint,
      countEndpoint: props.countEndpoint,
      sortField: computedSortField,
      paginationType: props.paginationType,
      sortFieldKey: props.sortFieldKey,
      requestPageKey: props.requestPageKey,
      responseTotalKey: props.responseTotalKey,
      requestPerPageKey: props.requestPerPageKey,
      inMemorySize: 3 * props.requestSize,
      requestSize: computed(() => props.requestSize),
      itemConverter: props.itemConverter,
      api: props.api,
      dataProvider: props.dataProvider,
      paramsSerialization: props.paramsSerialization,
      autoReload: false,
      queryParamsSerializationStrategy: props.queryParamsSerializationStrategy,
      onLoadNext: props.onItemsLoaded ? [onLoadEventCallback] : [],
      onLoadPrev: props.onItemsLoaded ? [onLoadEventCallback] : [],
      onInitialLoad: props.onItemsLoaded ? [onLoadEventCallback] : [],
      useAsyncCountLoad: props.useAsyncCountLoad,
      usePrevNextFlags: props.usePrevNextFlags,
      prevPageFlagKey: props.prevPageFlagKey,
      nextPageFlagKey: props.nextPageFlagKey,
    })

    const computedItems: Ref<DraggableItem[]> = ref([])

    watch(items, () => {
      computedItems.value = items.value.map((item) => ({
        draftOrder: props.dragAndDropSorting ? props.dragAndDropSorting.getSort(item) : 0,
        item,
      }))
    })

    const {
      checkedItems, check, isChecked, clearChecked, checkAll,
    } = props.checkedItemsInterface ?? useCheckedItems(props.defaultCheckedItems, props.canCheck !== 'none' ? props.canCheck : undefined)

    const showActionBar: ComputedRef<boolean> = computed(() => Boolean(checkedItems.value.length))

    const tableWidth = computed(() => {
      let result = computedColumns.value.reduce((acc, column) => acc + column.width, 0)
      if (checkboxCell.value) result += checkboxCell.value.clientWidth
      if (sortingCell.value) result += sortingCell.value.clientWidth
      return result
    })

    const isFiltersSet = computed(() => columns.value.some((c) => c.filter.hasValues()))

    const isFilterBlock = computed(() => (items.value.length === 0 && props.isFilterBlockNoData && !isFiltersSet.value))

    watch(count, () => {
      if (props.onTableCountUpdate) {
        props.onTableCountUpdate(count.value)
      }
    })

    const reloadWithVisibleLoading = async () => {
      if (blockReloadFlag) return

      if (props.clearCheckedOnParamsChange) {
        clearChecked(props.onCheckItem)
      }

      isLoadingScrollApi.value = true
      dataLoaderInsideActive.value = true
      isLoadingNext.value = false
      isLoadingPrev.value = false

      try {
        await reload()
      } catch (error) {
        if (error instanceof DebounceRejectError) return
      } finally {
        if (!isLoadingScrollApi.value) {
          isLoadingNext.value = false
          isLoadingPrev.value = false
          dataLoaderInsideActive.value = false
          rootContainer.value?.scroll({ top: 0 })
        }
      }
    }

    watch([params, computedSortField], reloadWithVisibleLoading)

    useScrollPagination({
      root,
      trigger: triggerDown,

      callback: async () => {
        if (isLoadingNext.value) return
        if (endOffset.value >= count.value || offset.value > count.value) return

        let lastChildId: string | undefined

        if (body.value) {
          lastChildId = (body.value.children[body.value.children.length - 1] as HTMLElement)?.dataset.key
        }

        isLoadingNext.value = true
        const result = await loadNext()
        isLoadingNext.value = false
        if (!lastChildId || !result) return

        const lastChild = Array.from(body.value.children).find((el) => (el as HTMLElement).dataset.key === lastChildId) as HTMLElement
        if (!lastChild) return
        let top = lastChild.offsetTop + lastChild.clientHeight - rootContainer.value.clientHeight - 1
        if (top < 1) top = 1
        rootContainer.value.scrollTo({ top })
      },
    })

    useScrollPagination({
      root,
      trigger: triggerUp,

      callback: async () => {
        if (isLoadingPrev.value) return
        if (items.value.length === 0 || startOffset.value <= 0) return
        const firstChildId = (body.value.children[1] as HTMLElement)?.dataset.key // Первый элемент это блок закрепленных элементов (sticky)

        isLoadingPrev.value = true
        const result = await loadPrev()
        isLoadingPrev.value = false

        if (!firstChildId || !result) return

        const firstChild = Array.from(body.value.children).find((el) => (el as HTMLElement).dataset.key === firstChildId) as HTMLElement
        if (!firstChild) return
        let top = firstChild.offsetTop - firstChild.clientHeight + 5
        if (top < 1) top = 1
        rootContainer.value.scrollTo({ top })
      },
    })

    onBeforeMount(async () => {
      if (props.allowInitLoading) await initialLoad()

      dataLoaderOutsideActive.value = false

      if (props.onInitialCountUpdate) {
        props.onInitialCountUpdate(count.value)
      }

      // await nextTick()

      // isUseModalFilters.value = rootContainer.value.clientWidth <= 900 || rootContainer.value.clientHeight <= 500
    })

    const spanColumns = () => {
      const columnsWidth = tableWidth.value
      const rootWidth = rootContainer.value.scrollWidth < rootContainer.value.offsetWidth ? rootContainer.value.scrollWidth : rootContainer.value.offsetWidth
      if (rootWidth > columnsWidth) {
        const columns = computedColumns.value.filter((column) => !column.wasResized)
        const additionalWidth = (rootWidth - tableWidth.value) / columns.length
        columns.forEach((column) => {
          column.width += additionalWidth
        })
      } else if (initResize && rootWidth < columnsWidth) {
        const columns = computedColumns.value.filter((column) => !column.wasResized)
        const additionalWidth = (tableWidth.value - rootWidth) / columns.length
        columns.forEach((column) => {
          if (column.width - additionalWidth > column.defaultWidth) column.width -= additionalWidth
        })
      }

      if (!initResize) initResize = true
    }

    onMounted(() => {
      resizeObserver.observe(rootContainer.value)

      if (rootContainer.value.clientWidth > tableWidth.value) {
        computedColumns.value[computedColumns.value.length - 1].width += rootContainer.value.clientWidth - tableWidth.value
      }
    })

    const updateColumnSettings = () => {
      nextTick(() => {
        // computedColumns.value.forEach(column => {
        // console.log(column.wasResized)
        // if (!column.wasResized) column.width = 200
        // console.log(column.width)
        // })

        spanColumns()
      })
    }

    onMounted(spanColumns)

    const additionalTableRows = ref<Record<string | number, AdditionalTableRow['render']>>({})

    const onAddingRow = ({ id, render }: AdditionalTableRow) => {
      if (typeof id === 'undefined') return

      if (additionalTableRows.value[id]) {
        delete additionalTableRows.value[id]
        return
      }

      if (render) additionalTableRows.value[id] = render
    }

    window.addEventListener('resize', updateColumnSettings)

    onBeforeUnmount(() => {
      resizeObserver.disconnect()
      window.removeEventListener('resize', updateColumnSettings)
    })

    const reloadAndUpdateCount = async () => {
      await reloadWithVisibleLoading()

      if (props.onInitialCountUpdate) {
        props.onInitialCountUpdate(count.value)
      }
    }

    provide('isUseModalFilters', isUseModalFilters)

    const isNodataStubVisible = computed(() => count.value === 0 && !props.forcedStub && !dataLoaderInsideActive.value && !isFiltersSet.value)

    const sortingComputed = computed<Record<string, string>>(() => {
      if (!sortField.value || sortOrder.value === undefined) return {}

      return {
        [sortField.value]: sortOrder.value === false ? 'DESC' : 'ASC',
      }
    })

    context.expose({
      count: count as unknown as typeof count.value,
      items: items as unknown as typeof items.value,
      reload: reloadWithVisibleLoading,
      reloadAndUpdateCount,
      refresh,
      clearChecked,
      onAddingRow,
      params: smartFilterParams as unknown as typeof smartFilterParams.value,
      sorting: sortingComputed as unknown as typeof sortingComputed.value,
      isLoadingNext,
      isLoadingPrev,
      settings: settings as unknown as typeof settings.value,
      checkedItems: checkedItems as unknown as typeof checkedItems.value,
      columns: columns as unknown as typeof columns.value,
      computedColumns: computedColumns as unknown as typeof computedColumns.value,
      isNodataStubVisible: isNodataStubVisible as unknown as typeof isNodataStubVisible.value,
      additionalRows: additionalTableRows,
    } as DefaultTableInterface)

    const CheckElement = props.canCheck === 'many' ? CheckBox : RadioButton

    const onStopDrag = () => {
      if (!draggedItem.value) return

      draggedItem.value = undefined

      computedItems.value.forEach((item) => {
        props.dragAndDropSorting?.setSort(item.item, item.draftOrder)
      })

      props.dragAndDropSorting?.onSort(items.value)
    }

    function setBackgroundColor(item: any): string | undefined {
      if (!props.highlightedElements || !props.highlightedElements[item.id] || isChecked(item)) {
        return undefined
      }

      return props.highlightedElements[item.id]
    }

    const contextMenu: Ref<DefaultTableContextMenuInterface | undefined> = ref()

    const contextMenuElement: Ref<HTMLElement | undefined> = ref()
    const clickedColumnElement: Ref<HTMLElement | undefined> = ref()

    async function onContextmenu(e: MouseEvent, item: any) {
      if (props.isHideContextMenu) return
      e.preventDefault()
      document.body.click()
      await nextTick()
      if (!contextMenu.value) return

      clickedColumnElement.value = e.target as HTMLElement
      contextMenuElement.value = await contextMenu.value.open(
        props.contextActions,
        (action) => action.callback(item),
      )
    }

    useMotionTracker(clickedColumnElement, contextMenuElement, computed((): boolean => Boolean(contextMenuElement.value)))

    const toggleExpandRow = (row: any) => {
      const index = computedItems.value.findIndex((item) => item.item.id === row.id)
      if (index !== -1) {
        computedItems.value[index].isExpandedRow = !computedItems.value[index].isExpandedRow
      }
    }

    const horizontalEyesightMargins = computed<number[]>(() => {
      let margin = 0
      if (props.isExpandRowButton) margin += 48
      if (props.canCheck !== 'none') margin += 48

      return [
        margin,
        ...computedColumns.value.map((v) => {
          margin += v.horizontalEyesight ? (v.width || 0) : 0
          return margin
        }),
      ]
    })

    const expandRowMargin = computed<number>(() => (props.canCheck !== 'none' ? 48 : 0))

    const hasHorizontalEyesightColumn = computed<boolean>(() => computedColumns.value.some((column) => column.horizontalEyesight === true))

    return () => {
      const useItems = (items: DraggableItem[], isSticky?: boolean) => (
        <>
          { items.sort((a, b) => a.draftOrder - b.draftOrder).map((computedItem) => {
            const item = computedItem.item as any

            return (
              <>
                {/* <div
                  class={`ark-ui-table-body-row ${isChecked(item) ? 'is-selected' : ''}`}
                  onClick={() => props.onRowClick && props.onRowClick(item)}
                  key={startOffset.value + idx}
                > */}
                <div
                  class={`ark-ui-table-body-row ${(isChecked(item) || draggedItem.value?.item.id === item.id) ? 'is-selected' : ''} ${!props.canCheckExactItem(item) ? 'is-disabled-row' : ''}`}
                  onClick={() => props.onRowClick && props.onRowClick(item)}
                  onContextmenu={(e: MouseEvent) => onContextmenu(e, item)}
                  key={item.id}
                  data-key={item.id}
                  style={{ backgroundColor: setBackgroundColor(item) ?? '' }}
                >
                  { props.dragAndDropSorting && (
                    <>
                      {
                        !isSticky
                          ? (
                            <DragAndDropItem
                              item={computedItem}
                              itemList={rootContainer}
                              draggedItem={draggedItem}
                              dragAndDropSorting={props.dragAndDropSorting}
                              computedItems={computedItems.value}
                            />
                          )
                          : (
                            <div class="ark-ui-table-body-cell ark-ui-table-checkbox-cell">
                              <div class="ark-ui-table-body-item-icon" style="opacity: 0; cursor: default" />
                            </div>
                          )
                      }
                    </>
                  ) }

                  { props.canCheck !== 'none' && (
                    <div
                      class={`ark-ui-table-body-cell ark-ui-table-checkbox-cell
                        ${hasHorizontalEyesightColumn.value ? 'ark-ui-table-checkbox-fixed' : ''}`}
                      onClick={(e) => e.stopPropagation()}
                    >
                      { !isSticky && (
                        <CheckElement
                          modelValue={isChecked(item)}
                          onValueChange={() => check(item, props.onCheckItem, props.canCheckExactItem)}
                          isDisabled={!props.canCheckExactItem(item)}
                        />
                      )}
                    </div>
                  ) }

                  { props.isExpandRowButton && (props.canExpandRow(computedItem.item) ? (
                    <div
                      class="ark-ui-table-body-cell ark-ui-table-expand-cell"
                      style={`left: ${hasHorizontalEyesightColumn.value ? expandRowMargin.value : ''}px;`}
                      onClick={(e) => { e.stopPropagation() }}
                    >
                      <div
                        class="ark-ui-table-expand-cell-wrapper"
                        onClick={() => { toggleExpandRow(computedItem.item) }}
                      >
                        <ExpandButton />
                      </div>
                    </div>
                  ) : (
                    <div
                      class="ark-ui-table-body-cell ark-ui-table-expand-cell"
                      style={`left: ${hasHorizontalEyesightColumn.value ? expandRowMargin.value : ''}px;`}
                    >
                      <ExpandButton style='opacity: 0 !important; pointer-events: none !important; user-select: none !important;' />
                    </div>
                  )) }

                  { computedColumns.value.map((column, columnIndex) => <DefaultTableCell
                    key={column.name}
                    column={column}
                    columnIndex={columnIndex}
                    row={computedItem}
                    horizontalEyesightMargins={horizontalEyesightMargins.value}
                  />) }
                </div>

                { Boolean(item.id && additionalTableRows.value[item.id]) && (
                  <div class="ark-ui-table-body-additional-row">{ additionalTableRows.value[item.id]?.(item) }</div>
                ) }
              </>
            )
          }) }
        </>
      )

      return (
        <>
          { dataLoaderOutsideActive.value && <DataLoader /> }

          <div
            style={dataLoaderOutsideActive.value ? 'display: none;' : ''}
            class={`
              ark-ui-table-wrapper
              ${isFilterBlock.value ? 'table-no-data' : ''}
              ${isUseModalFilters.value ? 'with-modal-filters' : ''}
              ${dataLoaderInsideActive.value ? 'is-data-loader-inside' : ''}
            `}
          >
            <ARKHeader
              settingsExpose={settings}
              isDisableTop={props.disableTop}
              isShowCount={props.showCount}
              isShowSettings={props.showSettings}
              isSmallSettingsButton={props.isSmallSettingsButton}
              headerInfo={props.headerInfo}
              buttons={props.buttons}
              count={setCountSpaces(count.value)}
              title={props.title}
              columns={columns}
              computedColumns={computedColumns}
              onUpdateSettings={updateColumnSettings}
              leftHeaderSlot={props.leftHeaderSlot}
              isCountLoading={isCountLoading.value}
            />

            { isLoadingPrev.value && <LineLoader /> }

            <div ref={rootContainer} class="ark-ui-table-body-wrapper">

              <div ref={root} class="ark-ui-table-body-wrapper-inner">
                <div class="ark-ui-table">

                  <div ref={triggerUp} class="ark-ui-table-pagination-trigger-top" />

                  <div class="ark-ui-table-header">
                    <div class="ark-ui-table-header-row">

                      { props.dragAndDropSorting && (
                        <div class="ark-ui-table-header-cell ark-ui-table-checkbox-cell" ref={sortingCell}/>
                      ) }

                      { props.canCheck !== 'none' && (
                        <div
                          ref={checkboxCell}
                          class={`ark-ui-table-header-cell ark-ui-table-checkbox-cell
                            ${hasHorizontalEyesightColumn.value ? 'ark-ui-table-checkbox-fixed' : ''}`}
                        >
                          { props.canCheck === 'many' && (
                            <CheckBox
                              class={`${checkedItems.value.length && checkedItems.value.length !== count.value
                                ? 'ark-ui-table-multiple-checkbox'
                                : ''}`}
                              modelValue={checkedItems.value.length === items.value.length && items.value.length !== 0}
                              onValueChange={() => checkAll(items.value, props.onCheckItem, props.canCheckExactItem)} />
                          ) }
                        </div>
                      ) }

                      { props.isExpandRowButton && (
                        <div
                          class="ark-ui-table-header-cell ark-ui-table-expand-cell"
                          style={`left: ${hasHorizontalEyesightColumn.value ? expandRowMargin.value : ''}px;`}
                        />
                      ) }

                      <DefaultHeaders
                        api={props.api}
                        sortField={sortField.value}
                        sortOrder={sortOrder.value}
                        setSort={setSort}
                        columns={computedColumns}
                        horizontalEyesightMargins={horizontalEyesightMargins.value}
                        smartFilter={props.smartFilter}
                        params={params.value}
                        paramsSerialization={props.paramsSerialization}
                        canResizeColumns={props.canResizeColumns}
                        enableSorting={props.enableSorting}
                        tableContainer={rootContainer.value}
                        resizerHeight={rootContainer.value?.clientHeight}
                      />

                    </div>
                  </div>

                  <div
                    ref={body}
                    style={items.value.length > 0 || props.stickyItems.length ? '' : 'display: none;'}
                    class={'ark-ui-table-body'}
                    onMouseup={onStopDrag}
                  >
                    { props.stickyItems.length > 0 && <div class="ark-ui-table-body-sticky">
                      { useItems(props.stickyItems.map((item) => ({
                        item,
                        draftOrder: 0,
                      })), true) }
                    </div> }

                    { !props.forcedStub && (
                      <>
                        { useItems(computedItems.value) }

                        { props.totalItems.length > 0
                          && <div class="ark-ui-table-body-total">
                            { useItems(props.totalItems.map((item) => ({
                              item,
                              draftOrder: 0,
                            })), true) }
                          </div>
                        }
                      </>
                    ) }

                  </div>

                  <div ref={triggerDown} class="ark-ui-table-pagination-trigger-bottom" />

                </div>
              </div>

              { props.forcedStub ? props.forcedStub() : dataLoaderInsideActive.value && <DataLoader /> }

              { items.value.length === 0 && !props.forcedStub && !dataLoaderInsideActive.value && (
                isFiltersSet.value
                  ? (
                    <div class='wrap-no-data'>
                      <NoData
                        icon="search"
                        text={props.noDataText ? props.noDataText : 'По вашему запросу ничего не найдено'}
                        size="S"
                      />
                    </div>
                  )
                  : (
                    <div class='wrap-no-data'>
                      <NoData
                        icon="box"
                        text={props.noDataText ? props.noDataText : 'Нет данных для отображения'}
                        buttonClick={props.noDataButtonClick}
                        buttonLabel={props.noDataButtonLabel}
                        size="S"
                      />
                    </div>
                  )
              )}

            </div>

            { isLoadingNext.value && <LineLoader /> }

            { !props.isHideActionBar && showActionBar.value && (
              <ActionBar
                actions={props.actions}
                text={`Выбрано: ${setCountSpaces(checkedItems.value.length)} из ${setCountSpaces(count.value)}`}
                callbackValue={checkedItems.value}
                itemsLength={count.value || undefined}
                onClose={clearChecked}
              />
            ) }

            { props.isHideContextMenu ? '' : (
              <ContextMenu
                ref={contextMenu}
                onClose={() => {
                  contextMenuElement.value = undefined
                  clickedColumnElement.value = undefined
                }}
              />
            ) }
          </div>
        </>
      )
    }
  },
})
</script>

<style lang="scss">
@import '../styles.scss';
</style>

<style lang="scss" scoped>
@import './../../../styles/variables';

.ark-ui-table-body-text-highlight {
  background-color: #ECF9FA;
}

.ark-ui-table {
  .ark-ui-table-checkbox-cell {
    padding: 16px;
    max-width: 48px;
    min-width: 48px;
    width: 48px;
  }

  &-body {
    position: relative;

    &-sticky {
      position: sticky;
      position: -webkit-sticky;
      top: 48px;
      background-color: $gray-100;
      z-index: $elementPriority;

      & > * {
        border-bottom: 1px solid $gray-300;
      }

      .ark-ui-table-expand-cell {
        .ark-ui-table-expand-button {
          background: none;
        }
      }
    }

    &-total {
      background-color: $gray-100;
      position: sticky;
      bottom: 0;
      border-top: 1px solid $gray-300;
      z-index: $elementPriority;

      .ark-ui-table-body-cell-inner {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        gap: 7px;
        font-weight: 500;
        font-size: 14px;
        line-height: 14px;

        > *:first-child::before {
          content: '';

          top: 1px;
          position: relative;

          width: 8px;
          height: 12px;
          margin-right: 8px;
          display: inline-block;

          background-size: 100%;
          background: url(./assets/sum.svg) center center no-repeat;
        }
      }

      .ark-ui-table-expand-cell {
        background: inherit;
        box-shadow: none;

        .ark-ui-table-expand-button {
          background: none;
        }
      }
    }
  }
}

.wrap-no-data {
  position: sticky;
  left: 0;
  height: 100%;
}
</style>
