<template>
  <div class="wt-tree-table wt-scrollbar">
    <table class="wt-tree-table-wrapper">
      <thead class="wt-tree-table-head">
        <tr class="wt-tree-table-tr wt-tree-table-tr-head">
          <th
            v-for="(col, key) of dataHeaders"
            :key="String(key) + col?.sort"
            :class="[
              {
                'wt-tree-table-th--sortable': isColSortable(col),
              },
              `wt-tree-table-th--sort-${col.sort}`,
            ]"
            :style="col.width ? `min-width:${col.width}` : ''"
            class="wt-tree-table-th typo-body-1"
            @click="sort(col, key)"
          >
            <div
              class="wt-tree-table-th__content"
              :class="{ 'wt-tree-table-th__content--selectable': key === 0 && selectable }"
            >
              <div
                v-if="key === 0 && selectable"
                @click.stop
              >
                <wt-checkbox
                  :selected="isAllSelected"
                  @update:selected="selectAll"
                />
              </div>
              <div class="wt-tree-table-th__text">
                {{ col.text }}
              </div>
              <wt-icon
                v-if="sortable"
                class="wt-tree-table-th-sort-arrow wt-tree-table-th-sort-arrow--asc"
                icon="sort-arrow-up"
                size="sm"
              />
              <wt-icon
                v-if="sortable"
                class="wt-tree-table-th-sort-arrow wt-tree-table-th-sort-arrow--desc"
                icon="sort-arrow-down"
                size="sm"
              />
            </div>
          </th>
          <th
            v-if="gridActions"
            class="wt-tree-table-th__actions"
          >
            <div class="wt-tree-table-th__content">
              <slot name="actions-header" />
            </div>
          </th>
        </tr>
      </thead>

      <tbody class="wt-tree-table-body">
        <wt-tree-table-row
          v-for="(row, dataKey) of data"
          :key="dataKey"
          :row-position="dataKey"
          :data-headers="dataHeaders"
          :data="row"
          :selectable="selectable"
          :children-prop="childrenProp"
          :selected-elements="selectedElements"
          :searched-prop="searchedProp"
          @update:selected="handleSelection($event.data, $event.select)"
        >
          <template #actions="{ item }">
            <slot
              name="actions"
              :item="item"
            />
          </template>
          <template
            v-for="(col, headerKey) of dataHeaders"
            :key="headerKey"
            #[col.value]="{ item }"
          >
            <slot
              :index="dataKey"
              :item="item"
              :name="col.value"
            >
              {{ item[col.value] }}
            </slot>
          </template>
        </wt-tree-table-row>
      </tbody>
    </table>
  </div>
</template>

<script
  setup
  lang="ts"
>
import { computed, toRef } from 'vue';
import { getNextSortOrder } from '../../scripts/sortQueryAdapters';
import { useWtTable } from '../_internals/composables/useWtTable/useWtTable';
import WtCheckbox from '../wt-checkbox/wt-checkbox.vue';
import type { WtTableHeader } from '../wt-table/types/WtTable.d.ts';
import WtTreeTableRow from '../wt-tree-table-row/wt-tree-table-row.vue';

const props = withDefaults(
	defineProps<{
		/**
		 * 'Accepts list of header objects. Draws text depending on "text" property, looks for data values through "value", "show" boolean controls visibility of a column (if undefined, all visible by default). ' Column width is calculated by "width" param. By default, sets minmax(150px, 1fr). '
		 */
		headers: WtTableHeader[];
		/**
		 * 'List of data, represented by table. '
		 */
		data: Record<string, unknown>[];
		/**
		 * 'If true, draws sorting arrows and sends sorting events at header click. Draws a sorting arrow by "sort": "asc"/"desc" header value. '
		 */
		sortable?: boolean;
		/**
		 * 'If true, draws row selection checkboxes. Checkbox toggles data object _isSelected property. It's IMPORTANT to set this property before sending data to table. '
		 */
		selectable?: boolean;
		selected: unknown[];
		/**
		 * 'If true, reserves space for 3 icon actions in the last column. Accessible by "actions" slot. '
		 */
		gridActions?: boolean;
		/**
		 * 'It's a key in data object, which contains children array. '
		 */
		childrenProp: string;
		/**
		 * 'It's a key in data object, which contains field what display searched elements. By this field, table will be opened to elements with this field value. '
		 */
		searchedProp?: string;
	}>(),
	{
		sortable: false,
		selectable: true,
		gridActions: true,
		searchedProp: 'searched',
	},
);

const emit = defineEmits([
	'sort',
	'update:selected',
]);

const checkHasChildItems = (item: Record<string, unknown>) => {
	return item[props.childrenProp] && Array.isArray(item[props.childrenProp]);
};

const getSelectedValue = (items: Record<string, unknown>[]) => {
	const selected = [];

	const pushSelectedElement = (item: Record<string, unknown>) => {
		if (item._isSelected) {
			return [
				item,
			];
		}

		if (checkHasChildItems(item)) {
			item[props.childrenProp].forEach(pushSelectedElement);
		}
	};

	items.forEach(pushSelectedElement);

	return selected;
};

const getAllNestedElements = (item: Record<string, unknown>) => {
	const nested = [];

	const pushElement = (item: Record<string, unknown>) => {
		nested.push(item);

		if (checkHasChildItems(item)) {
			item[props.childrenProp].forEach(pushElement);
		}
	};

	item.forEach(pushElement);

	return nested;
};

const selectedElements = computed<Record<string, unknown>[]>(() => {
	// _isSelected for backwards compatibility
	return props.selected || getSelectedValue(props.data);
});

const isAllSelected = computed(() => {
	return (
		selectedElements.value.length === getAllNestedElements(props.data).length &&
		getAllNestedElements(props.data).length > 0
	);
});

const { tableHeaders: dataHeaders } = useWtTable({
	headers: toRef(props, 'headers'),
});

const isColSortable = ({ sort }: WtTableHeader) => {
	/*       --sortable = sortable && col.sort === undefined cause there may be some columns we don't want to sort
        strict check for  === undefined is used because col.sort = null is sort order too (actualu, without sort)
        so we need to check if this property is present
*/
	return props.sortable && sort !== undefined;
};

const sort = (col: WtTableHeader) => {
	if (!isColSortable(col)) return;
	const nextSort = getNextSortOrder(col.sort);

	emit('sort', col, nextSort);
};

const changeSelectItem = (
	items: Record<string, unknown>[],
	selected: boolean,
) => {
	items.forEach((item) => {
		item._isSelected = selected;

		if (checkHasChildItems(item)) {
			// Change selected for all nested elements
			changeSelectItem(item[props.childrenProp], selected);
		}
	});
};

const selectAll = () => {
	if (props.selected) {
		emit(
			'update:selected',
			isAllSelected.value
				? []
				: [
						...getAllNestedElements(props.data),
					],
		);
	} else {
		// for backwards compatibility

		// https://webitel.atlassian.net/browse/WTEL-4634
		// Value for _isSelected must be assigned explicitly.
		// Because allSelected recomputes after each change

		if (isAllSelected.value) {
			changeSelectItem(props.data, false);
		} else {
			changeSelectItem(props.data, true);
		}
	}
};

const handleSelection = (row, select) => {
	if (props.selected) {
		if (select) {
			emit('update:selected', [
				...selectedElements.value,
				row,
			]);
		} else {
			emit(
				'update:selected',
				selectedElements.value.filter((item) => item !== row),
			);
		}
	} else {
		// for backwards compatibility
		row._isSelected = !row._isSelected;
	}
};
</script>

<style scoped>
.wt-tree-table {
  max-height: 100%;
  overflow: auto;
}

.wt-tree-table-head {
  border: var(--wt-tree-table-head-border);
  border-color: var(--wt-tree-table-head-border-color);
  border-radius: var(--border-radius);
  background: var(--wt-tree-table-head-background-color);
}

.wt-tree-table-th__content {
  display: flex;
  align-items: center;
}

.wt-tree-table-th__content--selectable {
  display: flex;
}

.wt-tree-table-wrapper {
  border-collapse: collapse;
  width: 100%;
}

.wt-tree-table-tr-wrapper {
  background: var(--wt-tree-table-primary-color);
}

.wt-tree-table-tr-wrapper:nth-child(2n) {
  background: var(--wt-tree-table-zebra-color);
}

.wt-tree-table-th,
.wt-tree-table-td {
  padding: var(--spacing-xs);
  height: fit-content;
  min-height: var(--wt-tree-table-min-height);
  word-break: break-all;
  overflow-wrap: break-word;
}

.wt-tree-table-td__actions {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  gap: var(--spacing-xs);
}

.wt-tree-table-td__content {
  display: flex;
  align-items: center;
}

.wt-tree-table-td__text {
  text-wrap: nowrap;
}

.wt-tree-table-th {
  font-weight: normal;
  text-wrap: nowrap;
  text-align: start;
}

.wt-tree-table-th--sortable {
  cursor: pointer;
}

.wt-tree-table-th:hover :deep(.wt-icon) {
  fill: var(--icon-active-color);
}

.wt-tree-table-th-sort-arrow {
  display: none;
  margin-left: var(--wt-tree-table-head-sort-arrow-margin);
}

.wt-tree-table-th--sort-asc .wt-tree-table-th-sort-arrow--asc {
  display: block;
}

.wt-tree-table-th--sort-desc .wt-tree-table-th-sort-arrow--desc {
  display: block;
}

.wt-tree-table-foot {
  border-color: var(--wt-tree-table-head-border-color);
  border-top: var(--wt-tree-table-head-border-color);
}
</style>
