<template>
  <div
    ref="dropZone"
    class="draggable-list"
    @drop="onDrop($event)"
    @dragover.prevent
    @dragenter.prevent
  >
    <div class="separator" @dragenter="onSeparatorDragEnter"></div>
    <template v-for="item in modelValue">
      <div
        :data-id="item.key"
        :draggable="gripped"
        @dragend="endDrag"
        class="draggable-row"
        @dragstart="startDrag($event, item.key)"
      >
        <GripVertical
          class="draggable-row__grabber"
          @mousedown="() => (gripped = true)"
          @mouseup="() => (gripped = false)"
        />
        <slot :row="item"></slot>
        <BmsIconButton :mode="StatusType.Danger" @click="onRemove(item)">
          <Trash />
        </BmsIconButton>
      </div>
      <div class="separator" @dragenter="onSeparatorDragEnter"></div>
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GripVertical, Trash } from 'lucide-vue-next';
import { BmsIconButton, StatusType } from '@/index';

const props = defineProps<{
  modelValue: { key: string; value: any }[];
}>();

const emits = defineEmits<{ 'update:modelValue': [modelValue: any[]] }>();

const dropZone = ref<HTMLElement | null>(null);
const gripped = ref(false);

const getElementCentralYPosition = (element: any) => {
  return (
    element.getBoundingClientRect().y +
    element.getBoundingClientRect().height / 2
  );
};

const onMouseDownGrip = () => {
  gripped.value = true;
};

const startDrag = (evt: any, itemKey: any) => {
  if (gripped.value) {
    if (dropZone.value) dropZone.value.style.borderColor = 'var(--bms-main-50)';
    evt.dataTransfer.dropEffect = 'move';
    evt.dataTransfer.effectAllowed = 'move';
    evt.dataTransfer.setData('itemKey', itemKey);
  }
};

const removeSeparators = () => {
  Array.from(document.getElementsByClassName('separator')).forEach(
    (el: Element) => {
      (el as HTMLDivElement).classList.remove('visible');
    },
  );
};

const endDrag = () => {
  removeSeparators();
  if (dropZone.value) dropZone.value.style.borderColor = 'transparent';
  gripped.value = false;
};

const onDrop = (evt: any) => {
  const itemKey = evt.dataTransfer.getData('itemKey');
  if (itemKey) {
    addItemToSelectedList(itemKey, evt);
  }
  endDrag();
};

const onSeparatorDragEnter = (evt: any) => {
  removeSeparators();
  evt.target.classList.add('visible');
};

const addItemToSelectedList = (droppedItemKey: any, event: any) => {
  const droppedItem = props.modelValue.find((i) => i.key === droppedItemKey);
  const newSelected: any[] = [];
  const container = document.getElementsByClassName('draggable-list')[0];
  const listElements = Array.from(container.children).filter((c) =>
    c.classList.contains('draggable-row'),
  );
  if (listElements.length > 0) {
    let inserted = false;
    listElements.forEach((currentItem, i) => {
      const itemKey = currentItem.getAttribute('data-id');
      if (itemKey !== droppedItemKey) {
        // ignore child if same element is being moved
        if (event.y < getElementCentralYPosition(currentItem) && !inserted) {
          // above and not inserted
          newSelected.push(droppedItem);
          inserted = true;
        }
        newSelected.push(props.modelValue[i]);
      }
    });

    if (!inserted) {
      // was not inserted because it is positionned at the end of the list
      newSelected.push(droppedItem);
    }
  } else {
    newSelected.push(droppedItem);
  }
  emits('update:modelValue', newSelected);
};

const onRemove = (item: any) => {
  emits(
    'update:modelValue',
    props.modelValue.filter((i) => i.key !== item.key),
  );
};
</script>

<style scoped lang="scss">
.draggable-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  border-radius: var(--bms-border-radius);
  border: 2px dashed transparent;
  padding: 2em 0.5em;

  .draggable-row {
    padding: 1em;
    border: 1px solid var(--bms-grey-25);
    border-radius: var(--bms-border-radius);
    display: flex;
    gap: 1em;
    align-items: center;
    justify-content: space-between;
    .draggable-row__grabber {
      cursor: grab;
    }
  }
}

.draggable-list-move,
.draggable-list-enter-active,
.draggable-list-leave-active {
  transition: all 0.5s ease;
}

.separator {
  width: 100%;
  margin: 0;
  transition: all 0.2s ease;
  height: 2px;
  margin: 0.5em 0;
  background-color: transparent;
  &.visible {
    margin: 1.5em 0;
    background: radial-gradient(
      circle,
      var(--bms-main-50) 20%,
      var(--bms-main-10) 100%
    );
  }
}
</style>
