<template>
  <v-data-table-virtual
    v-model="selected"
    density="compact"
    :headers="headers"
    hide-default-header
    :items="tableItems"
    @click:row="selectRow"
    item-value="Id"
    hover
    must-sort
    :custom-filter="customFilter"
    :row-props="
      (item) => ({
        class: { 'drag-drop': true, 'group-container': isGroup(item.item.Key) },
        id: `item-${item.item.Id}`,
      })
    "
    :search="search"
    :sort-by="sortBy"
    :no-data-text="t('labels.noResults')"
  >
    <template
      #[`item.data-table-expand`]="{ internalItem, isExpanded, toggleExpand }"
    >
      <v-btn
        v-if="isGroup(internalItem.raw.Key)"
        class="pl-3 mr-2"
        variant="text"
        size="25"
        data-testid="expand-btn"
        style="z-index: 10"
        color="shark_600"
        :icon="isExpanded(internalItem) ? '$expand' : 'chevron_forward'"
        @click="toggleExpand(internalItem)"
      />
    </template>
    <template #[`item.lock`]="{ item, isSelected, internalItem }">
      <McadLockBtn
        style="color: rgba(146, 147, 158, 1)"
        :mcad="item"
        class="hover-btn"
        :class="[isSelected(internalItem) && 'hover-btn-selected']"
      />
    </template>
    <template #[`item.visibility`]="{ item, isSelected, internalItem }">
      <McadVisibilityBtn
        style="color: rgba(146, 147, 158, 1)"
        :mcad="item"
        class="hover-btn"
        :class="[isSelected(internalItem) && 'hover-btn-selected']"
      />
    </template>
    <template #[`item.title`]="{ item }">
      <div class="d-flex align-center">
        <McadIcon
          class="pl-2"
          :mcad="item"
        />
        <span
          v-if="!isGroup(item.Key)"
          class="ml-2 text-sm-regular text-truncate"
        >
          {{ item.Title }}
        </span>
        <v-text-field
          v-else
          data-testid="focus-item"
          :model-value="focusedGroup?.Id === item.Id ? newTitle : item.Title"
          @update:model-value="newTitle = $event"
          variant="plain"
          density="compact"
          hide-details
          :class="{ 'background-white': focusedGroup?.Id === item.Id }"
          @focus="handleFocus(item)"
          @blur="handleBlur(item)"
          @keydown.enter="handleBlur(item)"
          class="custom-text-field ml-2"
          :style="{
            backgroundColor: focusedGroup?.Id === item.Id ? 'white' : '',
            borderWidth: focusedGroup?.Id === item.Id ? '1px' : '0',
            boxShadow:
              focusedGroup?.Id === item.Id ? '0 0 0 1px #6875EC' : 'none',
          }"
        />
      </div>
    </template>
    <template #[`item.actions`]="{ item }">
      <div class="d-inline-flex align-center justify-end">
        <McadMenu :mcad="item" />
      </div>
    </template>
    <template v-slot:expanded-row="{ item }">
      <tr>
        <td
          class="pr-0 pl-0"
          :colspan="headers.length"
        >
          <!--                  /* v8 ignore next 7 */-->
          <McadDataTableItems
            v-if="item.Children"
            class="pl-4"
            :items="item.Children"
            :search="search"
            style="background-color: #e1eafe"
          />
        </td>
      </tr>
    </template>
  </v-data-table-virtual>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { useViewer3cr } from '@/composables/useViewer3cr';
import { useMcadStore } from '@/stores/mcad.store';
import interact from 'interactjs';
import { storeToRefs } from 'pinia';
import { useToolStore } from '@/stores/tool.store';
import { DataOverlayMcad } from '@3cr/viewer-types-ts';
import { useI18n } from 'vue-i18n';

interface Props {
  items?: string[];
  isPrimary?: boolean;
  search?: string;
}

const props = withDefaults(defineProps<Props>(), {
  items: () => [],
  isPrimary: false,
  search: '',
});

const { t } = useI18n();

const { mcads, sortBy } = storeToRefs(useMcadStore());
const { mcadSelectTool } = useToolStore();
const viewer3cr = useViewer3cr();

const groupDataCache = ref<
  Record<string, { title: string; children: string[] }>
>({});
const groupNames = ref<Record<string, { name: string; children: string[] }>>(
  {},
);
const newTitle = ref<string>('');
const focusedGroup = ref<DataOverlayMcad | null>(null);

const groups = computed(() =>
  mcads.value.filter((x) => props.items.filter(isGroup).includes(x.Key)),
);

const selected = computed(() => [mcadSelectTool.selected.value?.Id]);

const tableItems = computed(() => {
  return mcads.value.filter(
    (mcad) => props.items.findIndex((childId) => childId === mcad.Key) > -1,
  );
});

const headers = ref([
  {
    key: 'data-table-expand',
    cellProps: (data: any) => ({
      class: [
        'px-1',
        'overflow',
        { active: mcadSelectTool.isSelected(data.item) },
      ],
    }),
  },
  {
    key: 'title',
    sortable: true,
    cellProps: (data: any) => ({
      class: [
        'px-1',
        'overflow',
        { active: mcadSelectTool.isSelected(data.item) },
      ],
    }),
    sortRaw: (a: DataOverlayMcad, b: DataOverlayMcad) => {
      return a.Title.localeCompare(b.Title);
    },
  },
  {
    key: 'lock',
    width: '24px',
    sortable: false,
    cellProps: (data: any) => ({
      class: ['px-0', 'pl-1', { active: mcadSelectTool.isSelected(data.item) }],
    }),
  },
  {
    key: 'visibility',
    width: '24px',
    sortable: false,
    cellProps: (data: any) => ({
      class: ['px-0', 'pl-1', { active: mcadSelectTool.isSelected(data.item) }],
    }),
  },
  {
    key: 'actions',
    width: '32px',
    sortable: false,
    cellProps: (data: any) => ({
      class: ['px-1', { active: mcadSelectTool.isSelected(data.item) }],
    }),
  },
]);

onMounted(() => {
  if (props.isPrimary) {
    interact(`.drag-drop`)
      .draggable({
        inertia: true,
        listeners: {
          start: onDragStart,
          move: onDragMove,
          end: onDragEnd,
        },
      })
      .dropzone({
        accept: '.drag-drop',
        overlap: 0.5,
        ondrop: onDrop,
      });
  }
});

function customFilter(value: string, query: string, item?: { value: any }) {
  const foundItem = mcads.value.find((mcad) => mcad.Key === item?.value);
  return foundItem?.Title.toLowerCase().includes(query.toLowerCase()) || false;
}

async function addToGroup(droppedId: string, groupId: string): Promise<void> {
  const targetGroup = groups.value.find((g) => g.Id === groupId);
  if (targetGroup && targetGroup.Children) {
    if (!targetGroup.Children.includes(droppedId)) {
      await ungroup(targetGroup.Children);
      await removeMcad(targetGroup.Id);
      await group([...targetGroup.Children, droppedId]);

      /* v8 ignore next 3 */
      const newGroup = groups.value.find((g) => {
        return g.Children?.every(
          (c) => targetGroup.Children?.includes(c) || c === droppedId,
        );
      });

      if (newGroup) {
        /* v8 ignore next 1 */
        const previousName = groupNames.value[groupId]?.name || '';
        const cachedData = groupDataCache.value[groupId];
        const titleToRetain = cachedData ? cachedData.title : previousName;
        await updateMcadTitle(newGroup?.Id, titleToRetain);
      }
    }
  }
}

function isGroup(key: string) {
  const node = mcads.value.find((x) => x.Key === key);
  return node && node.Children && node.Children.length > 0;
}
function getChildren(key: string): Array<string> | undefined {
  const node = mcads.value.find((x) => x.Key === key);
  return node && node.Children;
}

function onDragStart(event: any) {
  const target = event.target;
  const rect = target.getBoundingClientRect();
  target.dataset.originalX = rect.left;
  target.dataset.originalY = rect.top;
}

function onDragMove(event: any) {
  const target = event.target;
  const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.delta.x;
  const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.delta.y;
  target.style.transform = `translate(${x}px, ${y}px)`;
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

function onDragEnd(event: any) {
  const target = event.target;
  const wasDroppedOnValidTarget = event.dropzone && event.relatedTarget;
  if (!wasDroppedOnValidTarget) {
    target.style.transform = '';
    target.removeAttribute('data-x');
    target.removeAttribute('data-y');
  }
}

function onDrop(event: any) {
  const droppedId = event.relatedTarget.id.replace('item-', '');
  const targetId = event.target.id.replace('item-', '');
  if (event.target.classList.contains('group-container')) {
    addToGroup(droppedId, targetId);
  } else {
    const idsToGroup = [droppedId, targetId];
    group(idsToGroup);
  }
}

function selectRow(
  _$event: MouseEvent,
  { item }: { item: DataOverlayMcad },
): void {
  mcadSelectTool.toggle(item);
}

async function group(idsToGroup: string[]): Promise<void> {
  const message = { Version: '0.0.0', Parent: '', Children: idsToGroup };
  await viewer3cr.groupMcad({ message });
}

async function ungroup(idsToGroup: string[]): Promise<void> {
  const message = { Version: '0.0.0', Children: idsToGroup };
  await viewer3cr.ungroupMcad({ message });
}

async function removeMcad(id: string): Promise<void> {
  const message = { Version: '0.0.0', Id: id };
  await viewer3cr.removeMcadObject({ message });
}

async function updateMcadTitle(id: string, newTitle: string): Promise<void> {
  const mcad = mcads.value.find((x) => x.Id === id);
  if (mcad && isGroup(mcad.Key)) {
    const titleMessage = { Version: '0.0.0', Id: id, Value: newTitle };
    groupDataCache.value[id] = {
      title: newTitle,
      children: getChildren(mcad.Key)!!,
    };

    await viewer3cr.setMcadObjectTitle({ message: titleMessage });
  }
}

function handleFocus(item: DataOverlayMcad) {
  focusedGroup.value = item;
  newTitle.value = item.Title;
}

async function handleBlur(item: DataOverlayMcad) {
  focusedGroup.value = null;
  if (item.Title !== newTitle.value) {
    await updateMcadTitle(item.Id, newTitle.value);
  }
}

defineExpose({
  onDragEnd,
  onDragStart,
  onDrop,
  onDragMove,
  updateMcadTitle,
  customFilter,
});
</script>

<style scoped lang="scss">
.v-data-table {
  height: 100%;
  width: 100%;
}

:deep(.overflow) {
  max-width: 0;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.custom-text-field {
  padding: 0 !important;
  :deep(input) {
    padding: 0 !important;
  }
}
</style>

<style lang="scss">
.v-data-table {
  background-color: var(--v-background-color);
  height: 100%;
  .active {
    background-color: rgb(var(--v-theme-minsk_050)) !important;
  }
}

.drag-drop {
  cursor: grab !important;
}

.drag-drop:active {
  cursor: grabbing !important;
}

.background-white {
  background-color: white !important;
  border-radius: 8px !important;
  margin: 4px !important;
  padding: 0 !important;
}

tr {
  .hover-btn {
    display: none !important;
  }
  &:hover .hover-btn,
  .hover-btn-selected {
    display: inline-flex !important;
  }
}
</style>
