<!--
Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
Copyright (C) 2024 HTWKalender support@htwkalender.de

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
-->

<script lang="ts" setup>
import FullCalendar from "@fullcalendar/vue3";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import { computed, ComputedRef, inject, ref, Ref } from "vue";
import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core";
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
import { useI18n } from "vue-i18n";
import allLocales from "@fullcalendar/core/locales-all";
import router from "@/router";
import { formatYearMonthDay } from "@/helpers/dates";
import { useQuery } from "@tanstack/vue-query";
import { watch } from "vue";

const { t } = useI18n({ useScope: "global" });

const props = defineProps({
  room: {
    type: String,
    required: true,
  },
});

const date: Ref<Date> = ref(new Date());

// Set the selected date from the URL
function setDateFromQuery() {
  const queryDate = router.currentRoute.value.query.date;
  if (typeof queryDate === "string") {
    if (queryDate === formatYearMonthDay(date.value)) {
      return;
    }
    // date is in format like YYYYMMDD
    // TODO check if date is valid
    const year = queryDate.substring(0, 4);
    const month = queryDate.substring(4, 6);
    const day = queryDate.substring(6, 8);
    date.value = new Date(`${year}-${month}-${day}`);
  }
}

setDateFromQuery();

const currentDateFrom: Ref<string> = ref("");
const currentDateTo: Ref<string> = ref("");

const mobilePage = inject("mobilePage") as Ref<boolean>;

const selectedRoom = computed(() => props.room);

const { data: occupations } = useQuery({
  queryKey: ["roomOccupation", selectedRoom, currentDateFrom, currentDateTo],
  queryFn: () =>
    fetchEventsByRoomAndDuration(
      selectedRoom.value,
      currentDateFrom.value,
      currentDateTo.value,
    ),
  select: (data) =>
    data.map((event, index) => ({
      id: index,
      start: event.start.replace(/\s\+\d{4}\s\w+$/, "").replace(" ", "T"),
      end: event.end.replace(/\s\+\d{4}\s\w+$/, "").replace(" ", "T"),
      showFree: event.free,
    })),
  enabled: () => selectedRoom.value !== "" && currentDateFrom.value !== "",
  staleTime: 5000000, // 500 seconds
});

watch(occupations, () => fullCalendar.value?.getApi().refetchEvents());

const fullCalendar = ref<InstanceType<typeof FullCalendar>>();

const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
  locales: allLocales,
  locale: t("languageCode"),
  plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
  // local debugging of mobilePage variable in object creation on ternary expression
  initialView: mobilePage.value ? "Day" : "week",
  initialDate: date.value,
  dayHeaderFormat: { weekday: "short", omitCommas: true },
  slotDuration: "00:15:00",
  eventTimeFormat: {
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  },
  height: "auto",
  views: {
    week: {
      type: "timeGrid",
      slotLabelFormat: {
        hour: "numeric",
        minute: "2-digit",
        omitZeroMinute: false,
        meridiem: false,
        hour12: false,
      },
      dateAlignment: "week",
      titleFormat: { month: "short", day: "numeric" },
      slotMinTime: "06:00:00",
      slotMaxTime: "22:00:00",
      duration: { days: 7 },
      firstDay: 1,
      allDaySlot: false,
      hiddenDays: [0],
    },
    Day: {
      type: "timeGrid",
      slotLabelFormat: {
        hour: "numeric",
        minute: "2-digit",
        omitZeroMinute: false,
        meridiem: false,
        hour12: false,
      },
      titleFormat: { month: "short", day: "numeric" },
      slotMinTime: "06:00:00",
      slotMaxTime: "22:00:00",
      duration: { days: 1 },
      allDaySlot: false,
      hiddenDays: [0],
    },
  },
  headerToolbar: {
    end: "prev,next today",
    center: "title",
    start: "week,Day",
  },

  datesSet: function (dateInfo: DatesSetArg) {
    const view = dateInfo.view;
    const offset = new Date().getTimezoneOffset();
    const startDate = new Date(view.activeStart.getTime() - offset * 60 * 1000);
    const endDate = new Date(view.activeEnd.getTime() - offset * 60 * 1000);
    currentDateFrom.value = startDate.toISOString().split("T")[0];
    currentDateTo.value = endDate.toISOString().split("T")[0];
    router.replace({
      query: {
        ...router.currentRoute.value.query,
        date: formatYearMonthDay(endDate),
      },
    });
  },
  events: function (
    _info: unknown,
    successCallback: (events: EventInput[]) => void,
  ) {
    if (!occupations.value) return;
    successCallback(
      occupations.value.map((event) => {
        return {
          id: event.id.toString(),
          start: event.start,
          end: event.end,
          color: event.showFree 
            ? "var(--htwk-gruen-500)" 
            : "var(--htwk-grau-60-500)",
          textColor: event.showFree
            ? "var(--green-50)"
            : "white",
          title: event.showFree
            ? t("roomFinderPage.available")
            : t("roomFinderPage.occupied"),
        } as EventInput;
      }),
    );
  },
}));
</script>
<template>
  <FullCalendar ref="fullCalendar" :options="calendarOptions" />
</template>

<style scoped>
:deep(.fc-toolbar.fc-header-toolbar) {
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem;
}
</style>
