<script lang="tsx">
import {
  computed, defineComponent, nextTick,
  onBeforeUnmount, onMounted,
  ref, watch, Teleport,
  type ComputedRef,
  type PropType, type Ref,
} from 'vue'
import { setCountSpaces } from '../../../helpers'
import {
  type AdditionalTableRow,
  type ColumnInterface,
  type TableSettingsInterface,
} from '../DefaultTableInterfaces'
import { DefaultButton } from '../../../lib'

import { ActionBar, type ActionBarActionInterface } from '../../action-bar'
import CheckBox from '../../checkbox/ARKCheckbox.vue'
import DataLoader from '../../data-loader/ARKDataLoader.vue'
import RadioButton from '../../radio-button/RadioButton.vue'
import ARKHeader from '../ARKHeader.vue'
import DefaultHeaders from '../headers/DefaultHeaders.vue'
import NoData from '../no-data/NoData.vue'

import useCheckedItems, { type CheckedItemsInterface } from '../../../composables/use-checked-items'
import { type paramsSerializationOptions } from '../../../composables/use-endless-scroll-api'
import useColumns from '../composables/useColumns'
import type { NodeItem, NodeItemExtended } from '../composables/useNodes'
import useNodes from '../composables/useNodes'
import useResizableColumns from '../composables/useResizableColumns'
import useSort from '../composables/useSort'
import type { AllCheckState } from './types'
import { prepareSmartFilterParams } from '../headers/headers/prepareSmartFilterParams'

/**
 * Компонент представляет из себя таблицу, которая, в отличии от `TreeTable` и `DefaultTable` получает данные в себя через `props`.
 * <br />
 * Так же компонент позволяет отображать в себе элементы, которые в свою очередь имеют в себе дочерние элементы.
 * <br />
 * Еще таблица позволяет выбирать конкретные ее элементы, а затем делать с ними какие-либо действия через 'Action bar'.
 * <br />
 *
 * <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%3A76222&mode=design&t=3l6fbyi0pstfCLJN-1" target="_blank">Дизайн</a></li>
 * </ul>
 */
export default defineComponent({
  // добавляем пропсы в алфавитном порядке
  props: {
    /**
     * Массив действий, отображаемых в `Action bar`е
     */
    actions: {
      type: Array as PropType<ActionBarActionInterface[]>,
      default: () => [],
    },

    /**
     * Клиентская проверка на вид отображения главного чекбокса, выделяющего все элементы
     *
     * @param false чекбокс не активен (обычно это ничего не выбрано)
     * @param multiple выбрано несколько элементов (но не все)
     * @param true чекбокс активен (обычно это выбраны все строки)
     * @param undefined будет использоваться логика по умолчанию
     */
    allCheckState: {
      type: [Boolean, String] as PropType<AllCheckState>,
      default: undefined,
    },

    /**
     * Опция применения фильтрации.
     * Через эту опцию можно отфильтровать все данные в таблице.
     * Принимает в себя все элементы таблицы и заданные пользователем параметры.
     */
    applyFilters: {
      type: Function as PropType<(items: any[], params: Record<string, string | string[]>) => any[]>,
    },

    /**
     * Массив кнопок, отображающихся справа над таблицей
     */
    buttons: {
      type: Function as PropType<() => JSX.Element>,
    },

    /**
     * Ключ кеширования настроек колонок таблицы
     */
    cacheKey: {
      type: String,
    },

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

    /**
     * Массив колонок или фильтров для шапки таблицы
     */
    columns: {
      type: Array as PropType<ColumnInterface[]>,
      required: true,
    },

    /**
     * Общее количество элементов доступное для загрузки
     */
    count: {
      type: Number,
      default: null,
    },

    /**
     * Массив по умолчанию выделенных элементов
     */
    defaultCheckedItems: {
      type: Array as PropType<any[]>,
      default: () => [],
    },

    /**
     * Сортировка по указанному ключу
     * Тип сортировки `ASC` или `DESC` настраивается нажатием на соответствующую настройку, отображающуюся возле названия заголовка колонки
     */
    defaultSort: {
      type: String,
      default: '',
    },
    /**
     * Дефолтные квери параметры
     */
    defaultQueryParams: {
      type: Object as PropType<Record<string, string|string[]>>,
      default: () => ({}),
    },
    /**
     * Настройка скрытия шапки всей таблицы, где отображается ее заголовок и кнопки
     */
    disableTop: {
      type: Boolean,
      default: false,
    },

    /**
     * Флаг, убирающий кнопку сортировки в заголовке колонки
     */
    enableSorting: {
      type: Boolean,
      default: true,
    },

    /**
     * Опция отображения дополнительной информации в шапке таблицы
     * Отображается рядом со счетчиком элементов в таблице
     */
    headerInfo: {
      type: Function as PropType<() => JSX.Element>,
    },

    /**
     * Опция отображения дополнительной информации под названием таблицы
     */
    leftHeaderSlot: {
      type: Function as PropType<() => JSX.Element>,
    },

    /**
     * Опция закрашивания фона строки элемента
     * Передается объект, где ключ – это `id` элемента который нужно закрасить, а значение это цвет (в формате `CSS`), которым необходимо закрасить
     */
    highlightedElements: {
      type: Object as PropType<Record<string | number, string>>,
    },

    /**
     * Принудительное скрытие `Action bar`а
     */
    isHideActionBar: {
      type: Boolean,
      default: false,
    },

    /**
     * Флаг отображения заглушки поверх всей таблицы
     */
    isInitialLoading: {
      type: Boolean,
      default: false,
    },

    /**
     * Флаг отображения внутренней заглушки таблицы
     */
    isLoading: {
      type: Boolean,
      default: false,
    },

    /**
     * Позволяет уменьшать размер кнопки "Настройка колонок"
     * при значении true кнопка-шестеренка будет размером 32px
     * по умолчанию false, это 40px
     */
    isSmallSettingsButton: {
      type: Boolean,
      default: false,
    },

    /**
     * Блокирование всех чекбоксов/радио-кнопок строк \
     * Так же блокирует чекбокс в шапке таблицы, выделяющий все строки
     */
    isDisableChecking: {
      type: Boolean,
      default: false,
    },

    /**
     * ID элемента, у которого нужно показать подсказку о выбранной версии
     */
    itemIdForHelperShown: {
      type: String || Number,
      default: undefined,
    },

    /**
     * Основные элементы таблицы
     */
    items: {
      type: Array as PropType<NodeItem[]>,
      required: true,
    },

    /**
     * Опция изменения текста заглушек
     */
    noDataText: {
      type: String,
      default: '',
    },

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

    /**
     * Функция-колбэк для кнопки в заглушке. При отсвутствии пропса эта кнопка не отображается
     */
    noDataButtonClick: {
      type: Function as PropType<() => void>,
    },

    /**
     * Функция, запускающаяся при выборе элементов
     * Принимает в себя выбранный элемент
     */
    onCheckItem: {
      type: Function as PropType<(items: any[]) => void>,
      default: () => () => ({}),
    },

    /**
     * Функция, запускающаяся при выборе всех элементов
     */
    onCheckAllItem: {
      type: Function as PropType<() => void>,
      default: () => () => ({}),
    },

    /**
     * Хук, запускающийся при изменении параметров фильтрации
     * Принимает в себя новые параметры
     */
    onChangeParams: {
      type: Function as PropType<(params: Record<string, string | string[]>) => void>,
      default: () => () => ({}),
    },

    /**
     * Хук, запускающийся при изменении сортировки
     * Принимает в себя название колонки по которой происходит сортировка
     */
    onChangeSort: {
      type: Function as PropType<(sort: string) => void>,
      default: () => () => ({}),
    },

    /**
     * Хук, запускающийся при клике на строку
     */
    onRowClick: {
      type: Function as PropType<(item: any) => void>,
      default: () => () => ({}),
    },

    /**
     * Тип сериализации параметров: 'default' | 'custom' | 'comma'
     */
    paramsSerialization: {
      type: String as PropType<paramsSerializationOptions>,
      default: 'default',
    },

    /**
     * Настройка сохранять или нет дефолтные фильтры
     */
    saveDefaultFilterValueOnReset: {
      type: Boolean,
    },

    /**
     * Опция, позволяющая установить цвет фона строки \
     * Принимает в себя элемент таблицы и порядок элемента в таблице
     */
    setRowColor: {
      type: Function as PropType<(item: NodeItemExtended, index: number) => string>,
      default: undefined,
    },

    /**
     * Настройка вкл/выкл умных фильтров
     */
    smartFilter: {
      type: Boolean,
      default: false,
    },

    /**
     * Настройка отображения кнопки, настраивающей колонки таблицы
     */
    showSettings: {
      type: Boolean,
      default: true,
    },

    /**
     * Настройка отображения количества элементов
     */
    showCount: {
      type: Boolean,
      default: true,
    },

    /**
     * Заголовок таблицы
     */
    title: {
      type: String,
      default: '',
    },

    /**
     * Массив элементов для итоговой строки таблицы
     */
    totalItems: {
      type: Array as PropType<any[]>,
      default: () => [],
    },

    /**
     * Интерфейс внешних выбранных элементов
     */
    checkedItemsInterface: {
      type: Object as PropType<CheckedItemsInterface>,
      default: undefined,
    },
  },

  setup(props, { expose }) {
    const root = ref<HTMLElement | null>(null)
    const body = ref<HTMLElement | null>(null)
    const rootContainer = ref<HTMLElement | null>(null)
    const checkboxCell = ref<HTMLElement | null>(null)
    const sortingCell = ref<HTMLElement | null>(null)
    const settings = ref(null) as unknown as Ref<TableSettingsInterface>
    const showVersionHelper = ref(localStorage.getItem('isVersionHelperShown') === 'true')
    const highlightedRef = ref<HTMLElement | undefined>(undefined)
    const topPosition = ref(0)
    const leftPosition = ref(0)

    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 {
      sortField, sortOrder, computedSortField, setSort,
    } = useSort(props.defaultSort)

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

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

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

    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 spanColumns = () => {
      if (!rootContainer.value) return
      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
        })
      }
    }

    const filteredItems = computed(() => (props.applyFilters ? props.applyFilters(props.items, smartFilterParams.value) : props.items))
    const { DEFAULT_DEPTH, expandedNodeItems, expand } = useNodes(filteredItems, {
      isUseSingleQuery: true,
      dragAndDropSorting: undefined,

      loadMethod: async (item) => item.children,
    })

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

    const updateColumnSettings = async () => {
      await nextTick()
      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
    }

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

      return props.highlightedElements[item.id]
    }

    const horizontalEyesightMargins = computed<number[]>(() => {
      let margin = 0
      return [
        0,
        ...computedColumns.value.map((v) => {
          margin += v.horizontalEyesight ? (v.width || 0) : 0
          return margin
        }),
      ]
    })

    const checkPosition = () => {
      if (!highlightedRef.value || !props.itemIdForHelperShown) return

      const { top, left } = highlightedRef.value.getBoundingClientRect()
      topPosition.value = Math.round(top) - 129
      leftPosition.value = Math.round(left) + 30
    }

    const renderTableRow = (item: NodeItemExtended, idx: number) => (
      <>
        <div
          class={`ark-ui-table-body-row ${(isChecked(item)) ? 'is-selected' : ''}`}
          ref={props.itemIdForHelperShown && item.id === props.itemIdForHelperShown ? highlightedRef : undefined}
          onClick={() => props.onRowClick(item)}
          key={item.id}
          data-key={item.id}
          style={{ backgroundColor: setBackgroundColor(item) ?? (props.setRowColor ? props.setRowColor(item, idx) : '') }}
        >
          { props.canCheck !== 'none' && (
            <div class="ark-ui-table-body-cell ark-ui-table-checkbox-cell" onClick={(e) => e.stopPropagation()}>
              {
                props.canCheck === 'many'
                  ? (
                    <CheckBox
                      modelValue={isChecked(item)}
                      onValueChange={() => check(item, props.onCheckItem, props.canCheckExactItem)}
                      isDisabled={props.isDisableChecking || !props.canCheckExactItem(item)}
                    />
                  )
                  : (
                    <RadioButton
                      modelValue={isChecked(item)}
                      onValueChange={() => check(item, props.onCheckItem, props.canCheckExactItem)}
                      isDisabled={props.isDisableChecking || !props.canCheckExactItem(item)}
                    />
                  )
              }
            </div>
          ) }

          { computedColumns.value.map((column, idx) => (
            <div
              key={column.name}
              class={`ark-ui-table-body-cell ${column.horizontalEyesight ? 'ark-ui-table-eyesight' : ''}`}
              style={`width: ${column.width}px; min-width: ${column.width}px; max-width: ${column.width}px; overflow: ${column.overflow}; --horizontalEyesightMargin: ${horizontalEyesightMargins.value[idx] || 0}px;`}
            >
              <div class="ark-ui-table-body-cell-inner" style={idx === 0 && item.depth !== DEFAULT_DEPTH ? `padding-left: ${16 * item.depth}px !important` : ''}>
                <div class="ark-ui-table-cell-wrapper">

                  {Boolean(item.children?.length) && (
                    <div>
                      { Boolean(item.children?.length) && idx === 0 && (
                        <span
                          class={`ark-ui-table-expand
                            ${item.isExpanded ? 'is-expanded' : ''}
                            ${item.isLoading ? 'loading' : ''}`}
                          onClick={(e) => { expand(item); e.stopPropagation() }}
                        />
                      ) }
                      { !item.children?.length && idx === 0 && (<div style="width: 16px;"/>) }
                    </div>
                  )}

                  <div>
                    { column.field(item, column) }
                  </div>

                </div>
              </div>
            </div>
          )) }
        </div>

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

    watch(smartFilterParams, (value) => props.onChangeParams(value))
    watch(computedSortField, (value) => props.onChangeSort(value))
    watch(params, () => clearChecked())

    onMounted(() => {
      setInitialWIdth()
      spanColumns()
      window.addEventListener('resize', updateColumnSettings)
      if (props.itemIdForHelperShown) {
        checkPosition()
      }
    })

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

    expose({
      clearChecked,
      onAddingRow,
      checkAll,
      params,
      settings,
      columns,
      computedColumns,
      additionalRows: additionalTableRows,
      checkedItems,
    })

    return () => {
      if (props.isInitialLoading) return <DataLoader />
      return (
        <div class="ark-ui-table-wrapper ark-ui-shallow-table">
          <ARKHeader
            settingsExpose={settings}
            isShowCount={props.showCount}
            isDisableTop={props.disableTop}
            isShowSettings={props.showSettings}
            isSmallSettingsButton={props.isSmallSettingsButton}
            leftHeaderSlot={props.leftHeaderSlot}
            headerInfo={props.headerInfo}
            buttons={props.buttons}
            count={setCountSpaces(props.count || filteredItems.value.length)}
            title={props.title}
            columns={columns}
            computedColumns={computedColumns}
            onUpdateSettings={updateColumnSettings}
          />

          <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 class="ark-ui-table-header">
                  <div class="ark-ui-table-header-row">
                    { props.canCheck !== 'none' && (
                      <div class="ark-ui-table-header-cell ark-ui-table-checkbox-cell" ref={checkboxCell}>
                        { props.canCheck === 'many' && (
                          <CheckBox
                            class={`${(props.allCheckState !== undefined ? props.allCheckState === 'multiple' : checkedItems.value.length && checkedItems.value.length !== props.items.length)
                              ? 'ark-ui-table-multiple-checkbox'
                              : ''}`}
                            modelValue={
                              props.allCheckState !== undefined
                                ? props.allCheckState === true
                                : checkedItems.value.length === props.items.length && props.items.length !== 0
                            }
                            onValueChange={() => {
                              props.onCheckAllItem()
                              checkAll(filteredItems.value, props.onCheckItem, props.canCheckExactItem)
                            }}
                            isDisabled={props.isDisableChecking}
                          />
                        ) }
                      </div>
                    ) }

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

                  </div>
                </div>

                <div
                  ref={body}
                  class={'ark-ui-table-body'}
                >
                  {(!props.isLoading && filteredItems.value.length > 0) && (<>
                    {expandedNodeItems.value.map(renderTableRow)}

                    { props.totalItems.length > 0 && (
                      <div class="ark-ui-table-body-total">
                        {props.totalItems.map((item, index) => renderTableRow({
                          ...item,

                          children: [],
                          depth: 0,
                          draftOrder: 0,
                          isExpanded: false,
                          isLoading: false,
                        }, index))}
                      </div>
                    )}
                  </>)}
                </div>
              </div>
            </div>

            { (!props.isLoading && filteredItems.value.length === 0) && (
              <>

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

              </>
            )}

            {props.isLoading && <DataLoader />}

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

          { highlightedRef.value && !showVersionHelper.value && (
            <Teleport to="body">
              <div
                class="ark-ui-shallow-helper"
                style={`
                  left: ${leftPosition.value}px;
                  top: ${topPosition.value}px;`
                }
              >
                <p class="ark-ui-shallow-helper-text">Выбранная для просмотра версия подсвечивается фиолетовым цветом</p>
                <DefaultButton label="Понятно" type="secondary" onClick={(event) => {
                  event.stopPropagation()
                  localStorage.setItem('isVersionHelperShown', 'true')
                  showVersionHelper.value = true
                }} />
              </div>
            </Teleport>
          ) }
        </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: 50px;
    min-width: 50px;
    width: 50px;
  }

  &-body {
    position: relative;

    &-sticky {
      position: sticky;
      position: -webkit-sticky;
      top: 48px;
      background-color: #fff;
      z-index: $element;

      & > * {
        border-bottom: 1px solid #D3D5E5;
      }

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

        .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;
        }
      }
    }
  }
}

.ark-ui-shallow-helper {
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  width: 276px;
  height: 140px;
  box-sizing: border-box;
  background: #585B69;
  border-radius: 8px;
  transition: .4s;
  padding: 16px;
  z-index: $popup;

  &::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 17.2%;
    border: 11px solid transparent;
    border-top: 9px solid #585B69;
  }

  .ark-ui-shallow-helper-text {
    color: #FFF;
    font-size: 14px;
    line-height: 20px;
    margin: 0;
  }
}
</style>
