<template>
  <div>
    <q-card>
      <q-toolbar>
        <q-toolbar-title>Select a Boat and Time</q-toolbar-title>
        <q-btn
          icon="close"
          flat
          round
          dense
          v-close-popup />
      </q-toolbar>
      <q-separator />
      <CalendarHeaderComponent v-model="selectedDate" />
      <div class="boat-schedule-table-component">
        <QCalendarDay
          ref="calendar"
          class="q-pa-xs"
          flat
          animated
          dense
          :disabled-before="disabledBefore"
          interval-height="24"
          interval-count="18"
          interval-start="06:00"
          :short-interval-label="true"
          v-model="selectedDate"
          :column-count="boats.length"
          v-touch-swipe.left.right="handleSwipe">
          <template #head-day="{ scope }">
            <div style="text-align: center; font-weight: 800">
              {{ getBoatDisplayName(scope) }}
            </div>
          </template>

          <template #day-body="{ scope }">
            <div
              v-for="block in getAvailableIntervals(
                scope.timestamp,
                boats[scope.columnIndex]
              ).value"
              :key="block.$id">
              <div
                class="timeblock"
                :disabled="beforeNow(new Date(block.end))"
                :class="selectedBlock?.$id === block.$id ? 'selected' : ''"
                :style="
                  blockStyles(
                    block,
                    scope.timeStartPos,
                    scope.timeDurationHeight
                  )
                "
                :id="block.id"
                @click="selectBlock($event, scope, block)">
                {{ boats[scope.columnIndex].name }}
                <br />
                {{
                  selectedBlock?.$id === block.$id ? 'Selected' : 'Available'
                }}
              </div>
            </div>
            <div
              v-for="reservation in getBoatReservations(scope)"
              :key="reservation.$id">
              <div
                class="reservation column"
                :style="
                  reservationStyles(
                    reservation,
                    scope.timeStartPos,
                    scope.timeDurationHeight
                  )
                ">
                {{ getUserName(reservation.user) || 'loading...' }}
                <br />
                <q-chip class="gt-md">{{ reservation.reason }}</q-chip>
              </div>
            </div>
          </template>
        </QCalendarDay>
      </div>
    </q-card>
  </div>
</template>

<script setup lang="ts">
import {
  QCalendarDay,
  Timestamp,
  diffTimestamp,
  today,
  parseTimestamp,
  parseDate,
  addToDate,
} from '@quasar/quasar-ui-qcalendar';
import CalendarHeaderComponent from './CalendarHeaderComponent.vue';

import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useBoatStore } from 'src/stores/boat';
import { useAuthStore } from 'src/stores/auth';
import { Interval, Reservation } from 'src/stores/schedule.types';
import { storeToRefs } from 'pinia';
import { useReservationStore } from 'src/stores/reservation';
import { useIntervalTemplateStore } from 'src/stores/intervalTemplate';
import { useIntervalStore } from 'src/stores/interval';

const intervalTemplateStore = useIntervalTemplateStore();
const reservationStore = useReservationStore();
const { boats } = storeToRefs(useBoatStore());
const selectedBlock = defineModel<Interval | null>();
const selectedDate = ref(today());
const { getAvailableIntervals } = useIntervalStore();
const calendar = ref<QCalendarDay | null>(null);
const now = ref(new Date());
let intervalId: string | number | NodeJS.Timeout | undefined;

onMounted(async () => {
  await useBoatStore().fetchBoats();
  await intervalTemplateStore.fetchIntervalTemplates();
  intervalId = setInterval(function () {
    now.value = new Date();
  }, 60000);
});

onUnmounted(() => clearInterval(intervalId));

function handleSwipe({ ...event }) {
  event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
}
function reservationStyles(
  reservation: Reservation,
  timeStartPos: (t: string) => string,
  timeDurationHeight: (d: number) => string
) {
  return genericBlockStyle(
    parseDate(new Date(reservation.start)) as Timestamp,
    parseDate(new Date(reservation.end)) as Timestamp,
    timeStartPos,
    timeDurationHeight
  );
}

function getUserName(userid: string) {
  return useAuthStore().getUserNameById(userid);
}

function blockStyles(
  block: Interval,
  timeStartPos: (t: string) => string,
  timeDurationHeight: (d: number) => string
) {
  return genericBlockStyle(
    parseDate(new Date(block.start)) as Timestamp,
    parseDate(new Date(block.end)) as Timestamp,
    timeStartPos,
    timeDurationHeight
  );
}

function getBoatDisplayName(scope: DayBodyScope) {
  return boats && boats.value[scope.columnIndex]
    ? boats.value[scope.columnIndex].displayName
    : '';
}

function beforeNow(time: Date) {
  return time < now.value || null;
}

function genericBlockStyle(
  start: Timestamp,
  end: Timestamp,
  timeStartPos: (t: string) => string,
  timeDurationHeight: (d: number) => string
) {
  const s = {
    top: '',
    height: '',
    opacity: '',
  };
  if (timeStartPos && timeDurationHeight) {
    s.top = timeStartPos(start.time) + 'px';
    s.height =
      parseInt(
        timeDurationHeight(diffTimestamp(start, end, false) / 1000 / 60)
      ) -
      1 +
      'px';
  }
  return s;
}

interface DayBodyScope {
  columnIndex: number;
  timeDurationHeight: string;
  timeStartPos: (time: string, clamp: boolean) => string;
  timestamp: Timestamp;
}

function selectBlock(event: MouseEvent, scope: DayBodyScope, block: Interval) {
  if (scope.timestamp.disabled || new Date(block.end) < new Date())
    return false;
  selectedBlock.value = block;
}

const boatReservations = computed((): Record<string, Reservation[]> => {
  return reservationStore
    .getReservationsByDate(selectedDate.value)
    .reduce((result, reservation) => {
      if (!result[reservation.resource]) result[reservation.resource] = [];
      result[reservation.resource].push(reservation);
      return result;
    }, <Record<string, Reservation[]>>{});
});
function getBoatReservations(scope: DayBodyScope): Reservation[] {
  const boat = boats.value[scope.columnIndex];
  return boat ? boatReservations.value[boat.$id] : [];
}

const disabledBefore = computed(() => {
  const todayTs = parseTimestamp(today()) as Timestamp;
  return addToDate(todayTs, { day: -1 }).date;
});
</script>

<style lang="sass">
.boat-schedule-table-component
  display: flex
  max-height: 60vh
  max-width: 98vw
.reservation
  display: flex
  position: absolute
  justify-content: center
  align-items: center
  text-align: center
  width: 100%
  opacity: 1
  margin: 0px
  text-overflow: ellipsis
  font-size: 0.8em
  cursor: pointer
  background: $accent
  color: white
  border: 1px solid black
.timeblock
  display: flex
  position: absolute
  justify-content: center
  text-align: center
  align-items: center
  width: 100%
  opacity: 0.5
  margin: 0px
  text-overflow: ellipsis
  font-size: 0.8em
  cursor: pointer
  background: $primary
  color: white
  border: 1px solid black
.selected
  opacity: 1 !important
.q-calendar-day__interval--text
  font-size: 0.8em
.q-calendar-day__day.q-current-day
  padding: 1px
.q-calendar-day__head--days__column
  background: $primary
  color: white
</style>
