<template>
  <div class="min-h-[160vh]">
    <PageHead bordered>
      <div class="flex flex-col">
        <h1 class="text-2xl font-bold">{{ floor?.displayName }}</h1>
        <span class="text-sm font-light text-gray-800 line-clamp-1"
          >Floor {{ floor?.number }} in {{ building?.displayName }}</span
        >
      </div>

      <Menu
        title="Actions"
        class="self-end"
        :options="[
          {
            title: 'Edit floor plan',
            link: {
              name: 'planner',
              query: { buildingId: building?.id, floorId: floor?.id },
            },
          },
        ]"
      />
    </PageHead>

    <button
      @click="showMap = !showMap"
      :class="[
        showMap ? 'bg-gray-100' : 'rounded-md ring-1 ring-black ring-opacity-5',
        'ml-auto flex w-full items-center space-x-2 rounded-tl-lg rounded-tr-lg px-2 py-2 shadow-md md:w-auto',
        'focus:outline-none focus:ring-2 focus:ring-blue-500',
      ]"
    >
      <span>{{ showMap ? 'Hide' : 'Show' }} interactive floor plan</span>
      <ChevronUpIcon v-if="showMap" class="h-4 w-4" />
      <ChevronDownIcon v-else class="h-4 w-4" />
    </button>

    <Transition
      name="menu-transition"
      enter-active-class="transition-height ease-out duration-200"
      enter-from-class="h-0"
      enter-to-class="h-[70vh]"
      leave-active-class="transition-height ease-in duration-150"
      leave-from-class="h-[70vh]"
      leave-to-class="h-0"
    >
      <div v-if="showMap" class="z-10 overflow-hidden bg-gray-100 shadow-md">
        <HousePlanner
          v-if="floorDocument"
          :key="`fdoc${floorDocument.id}`"
          ref="canvas"
          class="h-[70vh]"
          :editable="false"
          :grid="false"
          transparent
          headless
          :floor-document="floorDocument"
          @click="clickOnRoom()"
          @zoom="(newZoom) => (canvasZoom = newZoom)"
        >
          <RoomPolygon
            v-for="room of rooms"
            :room="room"
            :zoom="canvasZoom"
            :highlighted="selectedRoom?.id"
            @clicked-in="(x, y) => clickOnRoom(room, x, y)"
            @mouse-moved-in="(x, y) => mouseMovedInRoom(room, x, y)"
          >
            <template v-if="selectedRoom?.id === room.id">
              <template v-for="storage of storages">
                <StorageBubble
                  v-if="
                    !movingBubble ||
                    (storage.id === movingBubble?.id &&
                      isSet(storage) === isSet(movingBubble))
                  "
                  :storage="storage"
                  :class="{
                    'z-20':
                      storage.id === hoveredBubble?.id ||
                      storage.id === selectedStorage?.id ||
                      (isSet(storage) && storage.id === selectedSet?.id),
                    'pointer-events-none': !!movingBubble,
                    'pointer-events-auto': !movingBubble,
                  }"
                  @mouseenter="() => (hoveredBubble = storage)"
                  @start-moving="moveBubble(storage)"
                >
                  <div class="flex items-center justify-between">
                    <div class="flex items-center space-x-1">
                      <span class="text-md font-bold">{{
                        storage.displayName
                      }}</span>
                      <span
                        v-if="isSet(storage)"
                        class="text-[0.75rem] font-bold uppercase text-gray-400"
                        >(Storage Set)</span
                      >
                    </div>

                    <div class="flex flex-row items-center space-x-1">
                      <button
                        v-if="!isSet(storage)"
                        @click.stop="selectAndAdd(storage)"
                        class="rounded-full px-1 py-1 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
                        title="Insert into Storage"
                      >
                        <ArrowDownOnSquareIcon
                          aria-hidden="true"
                          class="h-4 w-4"
                        />
                      </button>
                      <button
                        @click.stop="selectStorage(storage)"
                        class="rounded-full px-1 py-1 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
                        title="Open Storage"
                      >
                        <ArrowUpOnSquareIcon
                          aria-hidden="true"
                          class="h-4 w-4"
                        />
                      </button>
                    </div>
                  </div>
                  <span class="text-sm" v-if="!isSet(storage)"
                    >Stored items:
                    {{ (storage as StorageListItem).itemCount }}</span
                  >
                  <span class="text-sm" v-else
                    >Storages:
                    {{ (storage as StorageSetListItem).storages.length }}</span
                  >
                </StorageBubble>
              </template>
            </template>
          </RoomPolygon>
        </HousePlanner>
      </div>
    </Transition>

    <ItemSelector
      :class="{ 'mt-2': !showMap }"
      :rooms="rooms"
      :storages="storages"
      :selected-room="selectedRoom"
      :selected-storage="selectedStorage"
      :selected-set="selectedSet"
      @select-room="selectRoomFromList"
      @select-storage="selectStorage"
      @new-storage="addNewStorage"
    />

    <StorageView
      v-if="selectedStorage && building"
      ref="storageViewRef"
      @storage-added="newStoredItemAdded"
      :storage="selectedStorage"
      :building="building"
    />

    <NewStorageModal ref="newStorage" @added="storageAdded" />
  </div>
</template>

<script setup lang="ts">
import {
  ArrowDownOnSquareIcon,
  ArrowUpOnSquareIcon,
  ChevronDownIcon,
  ChevronUpIcon,
} from '@heroicons/vue/24/outline';
import { storeToRefs } from 'pinia';
import PageHead from '../../../components/PageHead.vue';
import { useRoute } from 'vue-router';
import { useBuildingStore } from '../../../store/building.store';
import { computed, ref } from '@vue/reactivity';
import HousePlanner from '../../../components/house-planner/HousePlanner.vue';
import { boundingBox } from '../../../modules/house-planner/utils';
import { Vec2 } from '../../../modules/house-planner/interfaces';
import {
  RoomLayoutObject,
  RoomListItem,
} from '../../../interfaces/room.interfaces';
import jfetch from '../../../utils/jfetch';
import { BACKEND_URL } from '../../../constants';
import { useAccessToken } from '../../../composables/useAccessToken';
import {
  StorageListItem,
  StorageSetListItem,
  StorageSharedType,
  StoredItem,
} from '../../../interfaces/storage.interfaces';
import StorageBubble from './StorageBubble.vue';
import RoomPolygon from './RoomPolygon.vue';
import { useLocalStorage } from '@vueuse/core';
import isSet from '../../../utils/is-storage-set';
import ItemSelector from './StorageSelector.vue';
import StoredItemCard from '../../../components/item/StoredItemCard.vue';
import NewStorageModal from '../../../components/item/NewStorageModal.vue';
import StorageView from './storage/StorageView.vue';
import Menu from '../../../components/menu/Menu.vue';

const route = useRoute();
const buildingStore = useBuildingStore();
const { building } = storeToRefs(buildingStore);
const { authHeader } = useAccessToken();
const canvas = ref<InstanceType<typeof HousePlanner>>();
const showMap = useLocalStorage('showRoomMap', true, { writeDefaults: false });
const canvasZoom = ref(1);
const storages = ref<StorageSharedType[]>([]);
const selectedRoom = ref<RoomLayoutObject>();
const hoveredBubble = ref<StorageSharedType>();
const movingBubble = ref<StorageSharedType>();
const selectedSet = ref<StorageSetListItem>();
const selectedStorage = ref<StorageListItem>();

const newStorage = ref<InstanceType<typeof NewStorageModal>>();
const storageViewRef = ref<InstanceType<typeof StorageView>>();

const floor = computed(() =>
  building.value?.floors.find(
    (floor) => floor.number === Number(route.params.number)
  )
);

const floorDocument = computed(
  () => floor.value?.plan && JSON.parse(floor.value.plan)
);

const clickOnRoom = (room?: RoomLayoutObject, x?: number, y?: number) => {
  canvas.value?.setViewRectangle(room?.boundingBox);
  selectedStorage.value = undefined;
  selectedSet.value = undefined;
  if (room && (!selectedRoom.value || room.id !== selectedRoom.value?.id)) {
    movingBubble.value = undefined;
    getRoomStorages(room);
  } else if (room?.id == selectedRoom.value?.id) {
    if (movingBubble.value && x != null && y != null) {
      setBubbleLocation(x, y);
    }
  } else {
    storages.value = [];
  }
  selectedRoom.value = room;
};

const mouseMovedInRoom = (room: RoomLayoutObject, x: number, y: number) => {
  if (!movingBubble.value) return;
  movingBubble.value.location = `${x},${y}`;
};

const moveBubble = (bubble: StorageSharedType) => {
  movingBubble.value = bubble;
};

const setBubbleLocation = async (x: number, y: number) => {
  if (!movingBubble.value) return;
  await jfetch(
    `${BACKEND_URL}/storage/${
      (movingBubble.value as StorageSetListItem).storages ? 'set' : 'storages'
    }/${movingBubble.value.id}`,
    {
      method: 'PATCH',
      body: {
        location: `${x},${y}`,
      },
      headers: authHeader.value,
    }
  );
  movingBubble.value = undefined;
};

const addNewStorage = (set: boolean | StorageSetListItem) => {
  if (!building.value || !selectedRoom.value) return;
  newStorage.value?.openModal(
    building.value,
    selectedRoom.value as unknown as RoomListItem,
    set
  );
};

const selectRoomFromList = (room: RoomLayoutObject) => {
  canvas.value?.setViewRectangle(room?.boundingBox);
  selectedStorage.value = undefined;
  selectedSet.value = undefined;
  getRoomStorages(room);
  selectedRoom.value = room;
};

const selectStorage = async (
  storage: StorageSharedType,
  parentSet?: StorageSetListItem
) => {
  if (isSet(storage)) {
    selectedSet.value = storage as StorageSetListItem;
    selectedStorage.value = undefined;
    return;
  }

  selectedSet.value = parentSet;
  selectedStorage.value = storage as StorageListItem;
  await jfetch(`${BACKEND_URL}/storage/storages/${storage.id}`, {
    headers: authHeader.value,
  }).then(({ data: storage }) => {
    selectedStorage.value = storage;
  });
};

const rooms = computed<RoomLayoutObject[]>(
  () =>
    (floor.value &&
      floor.value.rooms
        .filter((room) => room.plan?.includes('polygon'))
        .map((room) => {
          const { polygon } = JSON.parse(room.plan);
          const aabb = boundingBox(polygon, [
            [Infinity, Infinity],
            [0, 0],
          ]);
          return {
            ...room,
            plan: polygon as Vec2[],
            boundingBox: aabb,
            storages: [],
          } as RoomLayoutObject;
        })) ||
    []
);

const getRoomStorages = async (room: RoomLayoutObject) => {
  storages.value = [];
  try {
    const { data: storageList } = await jfetch(
      `${BACKEND_URL}/storage/room/${room.id}?includeWithSets=false`,
      {
        headers: authHeader.value,
      }
    );
    const { data: setList } = await jfetch(
      `${BACKEND_URL}/storage/set/room/${room.id}`,
      {
        headers: authHeader.value,
      }
    );
    storages.value = [...storageList, ...setList];
  } catch (e) {
    console.error(e);
  }
};

const storageAdded = async (newStorage: StorageSharedType) => {
  if (!selectedRoom.value) return;
  await getRoomStorages(selectedRoom.value);
  if (selectedSet.value) {
    selectedSet.value.storages = [
      ...selectedSet.value.storages,
      {
        ...(newStorage as StorageListItem),
        itemCount: 0,
      },
    ];
  }
};

const newStoredItemAdded = async (newItem: StoredItem) => {
  if (!selectedStorage.value) return;
  selectedStorage.value.items = [
    newItem,
    ...(selectedStorage.value.items || []),
  ];
  selectedStorage.value.itemCount = selectedStorage.value.items.length;
};

const selectAndAdd = async (storage: StorageSharedType) => {
  await selectStorage(storage);
  storageViewRef.value?.addNewItem();
};
</script>
