<template>
  <div class="row">
    <div class="col-sm">
      <label class="form-label">{{ $t("created_availabilities") }}</label>
      <vue-multiselect
        v-model="pickedLocation"
        :options="locations"
        :label="'name'"
        :track-by="'id'"
        :selectLabel="'Presser \'Entrée\' pour choisir'"
        :selectedLabel="'Choisir'"
        :deselectLabel="'Presser \'Entrée\' pour enlever'"
        :placeholder="'Choisir'"
      ></vue-multiselect>
    </div>
  </div>
  <div
    class="display-options row justify-content-between"
    style="margin-top: 1rem"
  >
    <div class="col-sm-9 col-xs-12">
      <div class="input-group mb-3">
        <label class="input-group-text" for="slotDuration"
          >Durée des créneaux</label
        >
        <select v-model="slotDuration" id="slotDuration" class="form-select">
          <option value="00:05:00">5 minutes</option>
          <option value="00:10:00">10 minutes</option>
          <option value="00:15:00">15 minutes</option>
          <option value="00:30:00">30 minutes</option>
          <option value="00:45:00">45 minutes</option>
          <option value="00:60:00">60 minutes</option>
        </select>
        <label class="input-group-text" for="slotMinTime">De</label>
        <select v-model="slotMinTime" id="slotMinTime" class="form-select">
          <option value="00:00:00">0h</option>
          <option value="01:00:00">1h</option>
          <option value="02:00:00">2h</option>
          <option value="03:00:00">3h</option>
          <option value="04:00:00">4h</option>
          <option value="05:00:00">5h</option>
          <option value="06:00:00">6h</option>
          <option value="07:00:00">7h</option>
          <option value="08:00:00">8h</option>
          <option value="09:00:00">9h</option>
          <option value="10:00:00">10h</option>
          <option value="11:00:00">11h</option>
          <option value="12:00:00">12h</option>
        </select>
        <label class="input-group-text" for="slotMaxTime">À</label>
        <select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
          <option value="12:00:00">12h</option>
          <option value="13:00:00">13h</option>
          <option value="14:00:00">14h</option>
          <option value="15:00:00">15h</option>
          <option value="16:00:00">16h</option>
          <option value="17:00:00">17h</option>
          <option value="18:00:00">18h</option>
          <option value="19:00:00">19h</option>
          <option value="20:00:00">20h</option>
          <option value="21:00:00">21h</option>
          <option value="22:00:00">22h</option>
          <option value="23:00:00">23h</option>
          <option value="23:59:59">24h</option>
        </select>
      </div>
    </div>
    <div class="col-xs-12 col-sm-3">
      <div class="float-end">
        <div class="form-check input-group">
          <span class="input-group-text">
            <input
              id="showHideWE"
              class="mt-0"
              type="checkbox"
              v-model="showWeekends"
            />
          </span>
          <label for="showHideWE" class="form-check-label input-group-text"
            >Week-ends</label
          >
        </div>
      </div>
    </div>
  </div>
  <FullCalendar :options="calendarOptions" ref="calendarRef">
    <template v-slot:eventContent="{ event }: { event: EventApi }">
      <span :class="eventClasses">
        <b v-if="event.extendedProps.is === 'remote'">{{ event.title }}</b>
        <b v-else-if="event.extendedProps.is === 'range'"
          >{{ formatDate(event.startStr, "time") }} -
          {{ formatDate(event.endStr, "time") }}:
          {{ event.extendedProps.locationName }}</b
        >
        <a
          :href="calendarLink(event.id)"
          v-else-if="event.extendedProps.is === 'local'"
        >
          <b>{{ event.title }}</b>
        </a>
        <b v-else>no 'is'</b>
        <a
          v-if="event.extendedProps.is === 'range'"
          class="fa fa-fw fa-times delete"
          @click.prevent="onClickDelete(event)"
        >
        </a>
      </span>
    </template>
  </FullCalendar>

  <div id="copy-widget">
    <div class="container mt-2 mb-2">
      <div class="row justify-content-between align-items-center mb-4">
        <div class="col-xs-12 col-sm-3 col-md-2">
          <h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
        </div>
        <div class="col-xs-12 col-sm-9 col-md-2">
          <select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
            <option value="day">{{ $t("from_day_to_day") }}</option>
            <option value="week">
              {{ $t("from_week_to_week") }}
            </option>
          </select>
        </div>
        <template v-if="dayOrWeek === 'day'">
          <div class="col-xs-12 col-sm-3 col-md-3">
            <input class="form-control" type="date" v-model="copyFrom" />
          </div>
          <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
            <i class="fa fa-angle-double-right"></i>
          </div>
          <div class="col-xs-12 col-sm-3 col-md-3">
            <input class="form-control" type="date" v-model="copyTo" />
          </div>
          <div class="col-xs-12 col-sm-5 col-md-1">
            <button class="btn btn-action float-end" @click="copyDay">
              {{ $t("copy_range") }}
            </button>
          </div>
        </template>
        <template v-else>
          <div class="col-xs-12 col-sm-3 col-md-3">
            <select
              v-model="copyFromWeek"
              id="copyFromWeek"
              class="form-select"
            >
              <option v-for="w in lastWeeks" :value="w.value" :key="w.value">
                {{ w.text }}
              </option>
            </select>
          </div>
          <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
            <i class="fa fa-angle-double-right"></i>
          </div>
          <div class="col-xs-12 col-sm-3 col-md-3">
            <select v-model="copyToWeek" id="copyToWeek" class="form-select">
              <option v-for="w in nextWeeks" :value="w.value" :key="w.value">
                {{ w.text }}
              </option>
            </select>
          </div>
          <div class="col-xs-12 col-sm-5 col-md-1">
            <button class="btn btn-action float-end" @click="copyWeek">
              {{ $t("copy_range") }}
            </button>
          </div>
        </template>
      </div>
    </div>
  </div>

  <!-- not directly seen, but include in a modal -->
  <edit-location ref="editLocation"></edit-location>
</template>

<script setup lang="ts">
import type {
  CalendarOptions,
  DatesSetArg,
  EventInput,
} from "@fullcalendar/core";
import { computed, ref, onMounted } from "vue";
import { useStore } from "vuex";
import { key } from "./store";
import FullCalendar from "@fullcalendar/vue3";
import frLocale from "@fullcalendar/core/locales/fr";
import interactionPlugin, {
  EventResizeDoneArg,
} from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import {
  EventApi,
  DateSelectArg,
  EventDropArg,
  EventClickArg,
} from "@fullcalendar/core";
import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date";
import VueMultiselect from "vue-multiselect";
import { Location } from "ChillMainAssets/types";
import EditLocation from "./Components/EditLocation.vue";

const store = useStore(key);

const showWeekends = ref(false);
const slotDuration = ref("00:15:00");
const slotMinTime = ref("09:00:00");
const slotMaxTime = ref("18:00:00");
const copyFrom = ref<string | null>(null);
const copyTo = ref<string | null>(null);
const editLocation = ref<InstanceType<typeof EditLocation> | null>(null);
const dayOrWeek = ref("day");
const copyFromWeek = ref<string | null>(null);
const copyToWeek = ref<string | null>(null);

interface Weeks {
  value: string | null;
  text: string;
}

const getMonday = (week: number): Date => {
  const lastMonday = new Date();
  lastMonday.setDate(
    lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7,
  );
  return lastMonday;
};

const dateOptions: Intl.DateTimeFormatOptions = {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric",
};

const lastWeeks = computed((): Weeks[] =>
  Array.from(Array(30).keys()).map((w) => {
    const lastMonday = getMonday(15 - w);
    return {
      value: dateToISO(lastMonday),
      text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
    };
  }),
);

const nextWeeks = computed((): Weeks[] =>
  Array.from(Array(52).keys()).map((w) => {
    const nextMonday = getMonday(w + 1);
    return {
      value: dateToISO(nextMonday),
      text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
    };
  }),
);

const formatDate = (datetime: string, format: null | "time" = null) => {
  const date = ISOToDate(datetime);
  if (!date) return "";

  if (format === "time") {
    return date.toLocaleTimeString("fr-FR", {
      hour: "2-digit",
      minute: "2-digit",
    });
  }

  // French date formatting
  return date.toLocaleDateString("fr-FR", {
    weekday: "short",
    year: "numeric",
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
};

const baseOptions = ref<CalendarOptions>({
  locale: frLocale,
  plugins: [interactionPlugin, timeGridPlugin],
  initialView: "timeGridWeek",
  initialDate: new Date(),
  scrollTimeReset: false,
  selectable: true,
  // when the dates are changes in the fullcalendar view OR when new events are added
  datesSet: onDatesSet,
  // when a date is selected
  select: onDateSelect,
  // when a event is resized
  eventResize: onEventDropOrResize,
  // when an event is moved
  eventDrop: onEventDropOrResize,
  // when an event si clicked
  eventClick: onEventClick,
  selectMirror: false,
  editable: true,
  headerToolbar: {
    left: "prev,next today",
    center: "title",
    right: "timeGridWeek,timeGridDay",
  },
});

const ranges = computed<EventInput[]>(() => {
  return store.state.calendarRanges.ranges;
});

const locations = computed<Location[]>(() => {
  return store.state.locations.locations;
});

const pickedLocation = computed<Location | null>({
  get(): Location | null {
    return (
      store.state.locations.locationPicked ||
      store.state.locations.currentLocation
    );
  },
  set(newLocation: Location | null): void {
    store.commit("locations/setLocationPicked", newLocation, {
      root: true,
    });
  },
});

/**
 * return the show classes for the event
 */
const eventClasses = { calendarRangeItems: true };

/*
// currently, all events are stored into calendarRanges, due to reactivity bug
const remotes = computed<EventInput[]>(() => {
  return store.state.calendarRemotes.remotes;
});

const sources = computed<EventSourceInput[]>(() => {
  const sources = [];

  const rangeSource: EventSourceInput = {
    id: 'ranges',
    events: ranges.value,
  };

  sources.push(rangeSource);

  return sources;
});
*/

const calendarOptions = computed((): CalendarOptions => {
  return {
    ...baseOptions.value,
    weekends: showWeekends.value,
    slotDuration: slotDuration.value,
    events: ranges.value,
    slotMinTime: slotMinTime.value,
    slotMaxTime: slotMaxTime.value,
  };
});

/**
 * launched when the calendar range date change
 */
function onDatesSet(event: DatesSetArg): void {
  store.dispatch("fullCalendar/setCurrentDatesView", {
    start: event.start,
    end: event.end,
  });
}

function onDateSelect(event: DateSelectArg): void {
  if (null === pickedLocation.value) {
    window.alert(
      "Indiquez une localisation avant de créer une période de disponibilité.",
    );
    return;
  }

  store.dispatch("calendarRanges/createRange", {
    start: event.start,
    end: event.end,
    location: pickedLocation.value,
  });
}

/**
 * When a calendar range is deleted
 */
function onClickDelete(event: EventApi): void {
  if (event.extendedProps.is !== "range") {
    return;
  }

  store.dispatch(
    "calendarRanges/deleteRange",
    event.extendedProps.calendarRangeId,
  );
}

function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
  if (payload.event.extendedProps.is !== "range") {
    return;
  }

  store.dispatch("calendarRanges/patchRangeTime", {
    calendarRangeId: payload.event.extendedProps.calendarRangeId,
    start: payload.event.start,
    end: payload.event.end,
  });
}

function onEventClick(payload: EventClickArg): void {
  // @ts-ignore TS does not recognize the target. But it does exists.
  if (payload.jsEvent.target.classList.contains("delete")) {
    return;
  }
  if (payload.event.extendedProps.is !== "range") {
    return;
  }

  editLocation.value?.startEdit(payload.event);
}

function copyDay() {
  if (null === copyFrom.value || null === copyTo.value) {
    return;
  }
  store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
    from: ISOToDate(copyFrom.value),
    to: ISOToDate(copyTo.value),
  });
}

function copyWeek() {
  if (null === copyFromWeek.value || null === copyToWeek.value) {
    return;
  }
  store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
    fromMonday: ISOToDate(copyFromWeek.value),
    toMonday: ISOToDate(copyToWeek.value),
  });
}

const calendarLink = (calendarId: string) => {
  const idStr = calendarId.match(/_(\d+)$/)?.[1];

  return `/fr/calendar/calendar/${idStr}/edit`;
};

onMounted(() => {
  copyFromWeek.value = dateToISO(getMonday(0));
  copyToWeek.value = dateToISO(getMonday(1));
});
</script>

<style scoped>
#copy-widget {
  position: sticky;
  bottom: 0px;
  background-color: white;
  z-index: 9999999999;
  padding: 0.25rem 0 0.25rem;
}
div.copy-chevron {
  text-align: center;
  font-size: x-large;
  width: 2rem;
}
</style>
