import { ItemType, ItemsBoxType } from 'src/types/kanban_types';
import GetNeighborObjects from './GetNeighborObjects';
import { debugLog } from 'src/utils';

class AddDragDropItemBehavior {
  itemsBox: ItemsBoxType;

  constructor(itemsBox: ItemsBoxType) {
    this.itemsBox = itemsBox;
    this.#addDropZone();
    this.#addDragDropItemBehavior();
  }

  #addDragDropItemBehavior() {
    const items: ItemType[] = this.itemsBox.getElement('items');
    for (const item of items) {
      this.#addBehaviorToItem(item);
    }
  }

  #addBehaviorToItem(item: ItemType) {
    item
      .on('sizer.dragstart', this.#handleDragStart.bind(this, item))
      .on('sizer.dragend', this.#handleDragEnd.bind(this, item))
      .on('sizer.drop', this.#handleDrop.bind(this, item));
  }

  #handleDragStart(item, pointer) {
    const neighbors = GetNeighborObjects(item);
    this.#logNeighbors('dragstart', neighbors, item);
    const previousItemsBox = item.getParentSizer();
    item.setData({
      itemsBox: previousItemsBox,
      index: previousItemsBox.getChildIndex(item),
    });

    previousItemsBox.remove(item);
  }

  #handleDragEnd(item, pointer, dragX, dragY, dropped) {
    if (dropped) {
      return;
    }
    const previousItemsBox = item.getData('itemsBox') as ItemsBoxType;
    previousItemsBox.insert(item.getData('index') as number, item, {
      expand: true,
    });
    this.#arrangeItems(previousItemsBox);
    const neighbors = GetNeighborObjects(item);
    this.#logNeighbors('dragend', neighbors, item);
  }

  #handleDrop(item, pointer, dropZone) {
    const currentItemsBox = dropZone.getData('itemsBox') as ItemsBoxType;
    const previousItemsBox = item.getData('itemsBox') as ItemsBoxType;
    currentItemsBox.insertAtPosition(
      pointer.x,
      pointer.y,
      item as unknown as Phaser.GameObjects.Text,
      {
        expand: true,
      },
    );

    this.#arrangeItems(previousItemsBox, currentItemsBox);
    const neighbors = GetNeighborObjects(item);
    this.#logNeighbors('drop', neighbors, item);
  }

  #logNeighbors(event, neighbors, item) {
    if (neighbors[0]) {
      debugLog(`${event} Above`, neighbors[0].text);
    }
    if (neighbors[1]) {
      debugLog(`${event} Below`, neighbors[1].text);
    }
    debugLog('Dragged Item: ', item.text);
  }

  #addDropZone() {
    const background = this.itemsBox.getElement(
      'background',
    ) as unknown as InteractiveItemType;
    if (background && typeof background.setInteractive === 'function') {
      background.setInteractive({ dropZone: true });
      background.setData('itemsBox', this.itemsBox);
    } else {
      console.error('Background is not defined or not interactive');
    }
  }

  #arrangeItems(itemsBox0: ItemsBoxType, itemsBox1?: ItemsBoxType) {
    const items: ItemType[] = [];
    items.push(...itemsBox0.getElement('items'));
    if (itemsBox1 && itemsBox0 !== itemsBox1) {
      items.push(...itemsBox1.getElement('items'));
    }

    for (const item of items) {
      item.setData({ startX: item.x, startY: item.y });
    }

    itemsBox0.getTopmostSizer().layout();

    for (const item of items) {
      const fromX = item.getData('startX') as number;
      const fromY = item.getData('startY') as number;
      if (item.x !== fromX || item.y !== fromY) {
        //@ts-ignore
        item.moveFrom({ x: fromX, y: fromY, duration: 300 });
      }
    }
  }

  addItem(item: any) {
    this.itemsBox.add(item, {
      proportion: 0,
      expand: true,
    });

    this.#addBehaviorToItem(item);
  }
}

type InteractiveItemType = ItemType & {
  setInteractive: (options: { dropZone: boolean }) => void;
};

export default AddDragDropItemBehavior;
