<template>
  <div class="container mx-auto">
    <o-loading v-model:active="eventLoading" />
    <div class="wrapper mb-3">
      <event-banner :picture="event?.picture" />
      <div class="intro-wrapper">
        <div class="date-calendar-icon-wrapper" v-if="event?.beginsOn">
          <date-calendar-icon :date="event.beginsOn.toString()" />
        </div>

        <section class="intro" dir="auto">
          <div class="flex flex-wrap">
            <div class="flex-1 min-w-fit">
              <h1
                class="text-4xl font-bold m-0"
                dir="auto"
                :lang="event?.language"
              >
                {{ event?.title }}
              </h1>
              <div class="organizer">
                <div v-if="event?.organizerActor && !event?.attributedTo">
                  <popover-actor-card
                    :actor="event.organizerActor"
                    :inline="true"
                  >
                    <i18n-t
                      keypath="By {username}"
                      dir="auto"
                      class="block truncate max-w-xs md:max-w-sm"
                    >
                      <template #username>
                        <span dir="ltr">{{
                          displayName(event.organizerActor)
                        }}</span>
                      </template>
                    </i18n-t>
                  </popover-actor-card>
                </div>
                <span v-else-if="event?.attributedTo">
                  <popover-actor-card
                    :actor="event.attributedTo"
                    :inline="true"
                  >
                    <i18n-t
                      keypath="By {group}"
                      dir="auto"
                      class="block truncate max-w-xs md:max-w-sm"
                    >
                      <template #group>
                        <router-link
                          :to="{
                            name: RouteName.GROUP,
                            params: {
                              preferredUsername: usernameWithDomain(
                                event.attributedTo
                              ),
                            },
                          }"
                          dir="ltr"
                          >{{ displayName(event.attributedTo) }}</router-link
                        >
                      </template>
                    </i18n-t>
                  </popover-actor-card>
                </span>
              </div>
              <div class="inline-flex items-center gap-1">
                <p v-if="event?.status !== EventStatus.CONFIRMED">
                  <tag
                    variant="warning"
                    v-if="event?.status === EventStatus.TENTATIVE"
                    >{{ t("Event to be confirmed") }}</tag
                  >
                  <tag
                    variant="danger"
                    v-if="event?.status === EventStatus.CANCELLED"
                    >{{ t("Event cancelled") }}</tag
                  >
                </p>
                <p class="flex gap-1 items-center" dir="auto">
                  <tag v-if="eventCategory" class="category" capitalize>{{
                    eventCategory
                  }}</tag>
                  <router-link
                    v-for="tag in event?.tags ?? []"
                    :key="tag.title"
                    :to="{ name: RouteName.TAG, params: { tag: tag.title } }"
                  >
                    <tag>{{ tag.title }}</tag>
                  </router-link>
                </p>
                <tag variant="warning" size="medium" v-if="event?.draft"
                  >{{ t("Draft") }}
                </tag>
              </div>
            </div>

            <div class="">
              <participation-section
                v-if="
                  event &&
                  currentActor &&
                  identities &&
                  anonymousParticipationConfig
                "
                :participation="participations[0]"
                :event="event"
                :anonymousParticipation="anonymousParticipation"
                :currentActor="currentActor"
                :identities="identities"
                :anonymousParticipationConfig="anonymousParticipationConfig"
                @join-event="joinEvent"
                @join-modal="isJoinModalActive = true"
                @join-event-with-confirmation="joinEventWithConfirmation"
                @confirm-leave="confirmLeave"
                @cancel-anonymous-participation="cancelAnonymousParticipation"
              />
              <div class="flex flex-col">
                <template v-if="!event?.draft">
                  <p
                    v-if="event?.visibility === EventVisibility.PUBLIC"
                    class="inline-flex gap-1"
                  >
                    <Earth />
                    {{ t("Public event") }}
                  </p>
                  <p
                    v-if="event?.visibility === EventVisibility.UNLISTED"
                    class="inline-flex gap-1"
                  >
                    <Link />
                    {{ t("Private event") }}
                  </p>
                </template>
                <template v-if="!event?.local && organizer?.domain">
                  <a :href="event?.url">
                    <tag>{{ organizer?.domain }}</tag>
                  </a>
                </template>
                <p class="inline-flex gap-1">
                  <TicketConfirmationOutline />
                  <router-link
                    class="participations-link"
                    v-if="canManageEvent && event?.draft === false"
                    :to="{
                      name: RouteName.PARTICIPATIONS,
                      params: { eventId: event.uuid },
                    }"
                  >
                    <!-- We retire one because of the event creator who is a
                    participant -->
                    <span v-if="maximumAttendeeCapacity">
                      {{
                        t(
                          "{available}/{capacity} available places",
                          {
                            available:
                              maximumAttendeeCapacity -
                              event.participantStats.participant,
                            capacity: maximumAttendeeCapacity,
                          },
                          maximumAttendeeCapacity -
                            event.participantStats.participant
                        )
                      }}
                    </span>
                    <span v-else>
                      {{
                        t(
                          "No one is participating|One person participating|{going} people participating",
                          {
                            going: event.participantStats.participant,
                          },
                          event.participantStats.participant
                        )
                      }}
                    </span>
                  </router-link>
                  <span v-else>
                    <span v-if="maximumAttendeeCapacity">
                      {{
                        t(
                          "{available}/{capacity} available places",
                          {
                            available:
                              maximumAttendeeCapacity -
                              (event?.participantStats.participant ?? 0),
                            capacity: maximumAttendeeCapacity,
                          },
                          maximumAttendeeCapacity -
                            (event?.participantStats.participant ?? 0)
                        )
                      }}
                    </span>
                    <span v-else>
                      {{
                        t(
                          "No one is participating|One person participating|{going} people participating",
                          {
                            going: event?.participantStats.participant,
                          },
                          event?.participantStats.participant ?? 0
                        )
                      }}
                    </span>
                  </span>
                  <VTooltip v-if="event?.local === false">
                    <HelpCircleOutline :size="16" />
                    <template #popper>
                      {{
                        t(
                          "The actual number of participants may differ, as this event is hosted on another instance."
                        )
                      }}
                    </template>
                  </VTooltip>
                </p>
                <o-dropdown>
                  <template #trigger>
                    <o-button icon-right="dots-horizontal">
                      {{ t("Actions") }}
                    </o-button>
                  </template>
                  <o-dropdown-item
                    aria-role="listitem"
                    has-link
                    v-if="canManageEvent || event?.draft"
                  >
                    <router-link
                      class="flex gap-1"
                      :to="{
                        name: RouteName.EDIT_EVENT,
                        params: { eventId: event?.uuid },
                      }"
                    >
                      <Pencil />
                      {{ t("Edit") }}
                    </router-link>
                  </o-dropdown-item>
                  <o-dropdown-item
                    aria-role="listitem"
                    has-link
                    v-if="canManageEvent || event?.draft"
                  >
                    <router-link
                      class="flex gap-1"
                      :to="{
                        name: RouteName.DUPLICATE_EVENT,
                        params: { eventId: event?.uuid },
                      }"
                    >
                      <ContentDuplicate />
                      {{ t("Duplicate") }}
                    </router-link>
                  </o-dropdown-item>
                  <o-dropdown-item
                    aria-role="listitem"
                    v-if="canManageEvent || event?.draft"
                    @click="openDeleteEventModal"
                    @keyup.enter="openDeleteEventModal"
                    ><span class="flex gap-1">
                      <Delete />
                      {{ t("Delete") }}
                    </span>
                  </o-dropdown-item>

                  <hr
                    role="presentation"
                    class="dropdown-divider"
                    aria-role="o-dropdown-item"
                    v-if="canManageEvent || event?.draft"
                  />
                  <o-dropdown-item
                    aria-role="listitem"
                    v-if="event?.draft === false"
                    @click="triggerShare()"
                    @keyup.enter="triggerShare()"
                    class="p-1"
                  >
                    <span class="flex gap-1">
                      <Share />
                      {{ t("Share this event") }}
                    </span>
                  </o-dropdown-item>
                  <o-dropdown-item
                    aria-role="listitem"
                    @click="downloadIcsEvent()"
                    @keyup.enter="downloadIcsEvent()"
                    v-if="event?.draft === false"
                  >
                    <span class="flex gap-1">
                      <CalendarPlus />
                      {{ t("Add to my calendar") }}
                    </span>
                  </o-dropdown-item>
                  <o-dropdown-item
                    aria-role="listitem"
                    v-if="ableToReport"
                    @click="isReportModalActive = true"
                    @keyup.enter="isReportModalActive = true"
                    class="p-1"
                  >
                    <span class="flex gap-1">
                      <Flag />
                      {{ t("Report") }}
                    </span>
                  </o-dropdown-item>
                </o-dropdown>
              </div>
            </div>
          </div>
        </section>
      </div>

      <div
        class="event-description-wrapper rounded-lg dark:border-violet-title flex flex-wrap flex-col md:flex-row-reverse"
      >
        <aside class="event-metadata rounded dark:bg-gray-600 shadow-md">
          <div class="sticky">
            <event-metadata-sidebar
              v-if="event"
              :event="event"
              :user="loggedUser"
              @showMapModal="showMap = true"
            />
          </div>
        </aside>
        <div class="event-description-comments">
          <section class="event-description">
            <h2 class="text-xl">{{ t("About this event") }}</h2>
            <p v-if="!event?.description">
              {{ t("The event organizer didn't add any description.") }}
            </p>
            <div v-else>
              <div
                :lang="event?.language"
                dir="auto"
                class="description-content"
                ref="eventDescriptionElement"
                v-html="event.description"
              />
            </div>
          </section>
          <section class="integration-wrappers">
            <component
              v-for="(metadata, integration) in integrations"
              :is="integration"
              :key="integration"
              :metadata="metadata"
            />
          </section>
          <section class="comments" ref="commentsObserver">
            <a href="#comments">
              <h2 class="text-xl" id="comments">{{ t("Comments") }}</h2>
            </a>
            <comment-tree v-if="event && loadComments" :event="event" />
          </section>
        </div>
      </div>

      <section class="" v-if="(event?.relatedEvents ?? []).length > 0">
        <h2 class="">
          {{ t("These events may interest you") }}
        </h2>
        <multi-card :events="event?.relatedEvents ?? []" />
      </section>
      <o-modal
        v-model:active="isReportModalActive"
        has-modal-card
        ref="reportModal"
        :close-button-aria-label="t('Close')"
      >
        <ReportModal
          :on-confirm="reportEvent"
          :title="t('Report this event')"
          :outside-domain="organizerDomain"
        />
      </o-modal>
      <o-modal
        :close-button-aria-label="t('Close')"
        v-model:active="isShareModalActive"
        has-modal-card
        ref="shareModal"
      >
        <share-event-modal
          v-if="event"
          :event="event"
          :eventCapacityOK="eventCapacityOK"
        />
      </o-modal>
      <o-modal
        v-model:active="isJoinModalActive"
        has-modal-card
        ref="participationModal"
        :close-button-aria-label="t('Close')"
      >
        <identity-picker v-if="identity" v-model="identity">
          <template #footer>
            <footer class="flex gap-2">
              <o-button
                outlined
                ref="cancelButton"
                @click="isJoinModalActive = false"
                @keyup.enter="isJoinModalActive = false"
              >
                {{ t("Cancel") }}
              </o-button>
              <o-button
                v-if="identity"
                variant="primary"
                ref="confirmButton"
                @click="
                  event?.joinOptions === EventJoinOptions.RESTRICTED
                    ? joinEventWithConfirmation(identity as IPerson)
                    : joinEvent(identity as IPerson)
                "
                @keyup.enter="
                  event?.joinOptions === EventJoinOptions.RESTRICTED
                    ? joinEventWithConfirmation(identity as IPerson)
                    : joinEvent(identity as IPerson)
                "
              >
                {{ t("Confirm my particpation") }}
              </o-button>
            </footer>
          </template>
        </identity-picker>
      </o-modal>
      <o-modal
        v-model:active="isJoinConfirmationModalActive"
        has-modal-card
        ref="joinConfirmationModal"
        :close-button-aria-label="t('Close')"
      >
        <div class="modal-card">
          <header class="modal-card-head">
            <p class="modal-card-title">
              {{ t("Participation confirmation") }}
            </p>
          </header>

          <section class="modal-card-body">
            <p>
              {{
                t(
                  "The event organiser has chosen to validate manually participations. Do you want to add a little note to explain why you want to participate to this event?"
                )
              }}
            </p>
            <form
              @submit.prevent="
                joinEvent(
                  actorForConfirmation as IPerson,
                  messageForConfirmation
                )
              "
            >
              <o-field :label="t('Message')">
                <o-input
                  type="textarea"
                  size="medium"
                  v-model="messageForConfirmation"
                  minlength="10"
                ></o-input>
              </o-field>
              <div class="buttons">
                <o-button
                  native-type="button"
                  class="button"
                  ref="cancelButton"
                  @click="isJoinConfirmationModalActive = false"
                  @keyup.enter="isJoinConfirmationModalActive = false"
                  >{{ t("Cancel") }}
                </o-button>
                <o-button variant="primary" native-type="submit">
                  {{ t("Confirm my participation") }}
                </o-button>
              </div>
            </form>
          </section>
        </div>
      </o-modal>
      <o-modal
        v-model:active="showMap"
        :close-button-aria-label="t('Close')"
        class="map-modal"
        v-if="event?.physicalAddress?.geom"
        has-modal-card
        full-screen
        :can-cancel="['escape', 'outside']"
      >
        <template #default="props">
          <event-map
            :routingType="routingType ?? RoutingType.OPENSTREETMAP"
            :address="event.physicalAddress"
            @close="props.close"
          />
        </template>
      </o-modal>
    </div>
  </div>
</template>

<script lang="ts" setup>
import {
  EventJoinOptions,
  EventStatus,
  EventVisibility,
  MemberRole,
  ParticipantRole,
  RoutingType,
} from "@/types/enums";
import {
  EVENT_PERSON_PARTICIPATION,
  // EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED,
  FETCH_EVENT,
  JOIN_EVENT,
  LEAVE_EVENT,
} from "@/graphql/event";
import { IEvent } from "@/types/event.model";
import {
  displayName,
  IActor,
  IPerson,
  usernameWithDomain,
} from "@/types/actor";
import { GRAPHQL_API_ENDPOINT } from "@/api/_entrypoint";
import DateCalendarIcon from "@/components/Event/DateCalendarIcon.vue";
import MultiCard from "@/components/Event/MultiCard.vue";
import ReportModal from "@/components/Report/ReportModal.vue";
import IdentityPicker from "@/views/Account/IdentityPicker.vue";
import ParticipationSection from "@/components/Participation/ParticipationSection.vue";
import RouteName from "@/router/name";
import CommentTree from "@/components/Comment/CommentTree.vue";
import "intersection-observer";
import {
  AnonymousParticipationNotFoundError,
  getLeaveTokenForParticipation,
  isParticipatingInThisEvent,
  removeAnonymousParticipation,
} from "@/services/AnonymousParticipationStorage";
import Tag from "@/components/TagElement.vue";
import EventMetadataSidebar from "@/components/Event/EventMetadataSidebar.vue";
import EventBanner from "@/components/Event/EventBanner.vue";
import EventMap from "@/components/Event/EventMap.vue";
import PopoverActorCard from "@/components/Account/PopoverActorCard.vue";
import { IParticipant } from "@/types/participant.model";
import { ApolloCache, FetchResult } from "@apollo/client/core";
import { IEventMetadataDescription } from "@/types/event-metadata";
import { eventMetaDataList } from "@/services/EventMetadata";
import { useDeleteEvent, useFetchEvent } from "@/composition/apollo/event";
import {
  computed,
  onMounted,
  ref,
  watch,
  defineAsyncComponent,
  inject,
} from "vue";
import { useRoute, useRouter } from "vue-router";
import Earth from "vue-material-design-icons/Earth.vue";
import Link from "vue-material-design-icons/Link.vue";
import Flag from "vue-material-design-icons/Flag.vue";
import CalendarPlus from "vue-material-design-icons/CalendarPlus.vue";
import ContentDuplicate from "vue-material-design-icons/ContentDuplicate.vue";
import Delete from "vue-material-design-icons/Delete.vue";
import Pencil from "vue-material-design-icons/Pencil.vue";
import HelpCircleOutline from "vue-material-design-icons/HelpCircleOutline.vue";
import TicketConfirmationOutline from "vue-material-design-icons/TicketConfirmationOutline.vue";
import {
  useCurrentActorClient,
  useCurrentUserIdentities,
  usePersonStatusGroup,
} from "@/composition/apollo/actor";
import { useLoggedUser } from "@/composition/apollo/user";
import { useMutation, useQuery } from "@vue/apollo-composable";
import {
  useAnonymousActorId,
  useAnonymousParticipationConfig,
  useAnonymousReportsConfig,
  useEventCategories,
  useRoutingType,
} from "@/composition/apollo/config";
import { useCreateReport } from "@/composition/apollo/report";
import Share from "vue-material-design-icons/Share.vue";
import { useI18n } from "vue-i18n";
import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier";
import { AbsintheGraphQLErrors } from "@/types/errors.model";
import { useHead } from "@vueuse/head";
import { Snackbar } from "@/plugins/snackbar";

const ShareEventModal = defineAsyncComponent(
  () => import("@/components/Event/ShareEventModal.vue")
);
/* eslint-disable @typescript-eslint/no-unused-vars */
const IntegrationTwitch = defineAsyncComponent(
  () => import("@/components/Event/Integrations/TwitchIntegration.vue")
);
const IntegrationPeertube = defineAsyncComponent(
  () => import("@/components/Event/Integrations/PeerTubeIntegration.vue")
);
const IntegrationYoutube = defineAsyncComponent(
  () => import("@/components/Event/Integrations/YouTubeIntegration.vue")
);
const IntegrationJitsiMeet = defineAsyncComponent(
  () => import("@/components/Event/Integrations/JitsiMeetIntegration.vue")
);
const IntegrationEtherpad = defineAsyncComponent(
  () => import("@/components/Event/Integrations/EtherpadIntegration.vue")
);
/* eslint-enable @typescript-eslint/no-unused-vars */

const props = defineProps<{
  uuid: string;
}>();

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

const {
  event,
  onError: onFetchEventError,
  loading: eventLoading,
} = useFetchEvent(props.uuid);
const eventId = computed(() => event.value?.id);
const { currentActor } = useCurrentActorClient();
const currentActorId = computed(() => currentActor.value?.id);
const { loggedUser } = useLoggedUser();
const {
  result: participationsResult,
  // subscribeToMore: subscribeToMoreParticipation,
} = useQuery<{ person: IPerson }>(
  EVENT_PERSON_PARTICIPATION,
  () => ({
    eventId: event.value?.id,
    actorId: currentActorId.value,
  }),
  () => ({
    enabled:
      currentActorId.value !== undefined &&
      currentActorId.value !== null &&
      eventId.value !== undefined,
  })
);

// subscribeToMoreParticipation(() => ({
//   document: EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED,
//   variables: {
//     eventId: eventId,
//     actorId: currentActorId,
//   },
// }));

const participations = computed(
  () => participationsResult.value?.person.participations?.elements ?? []
);

const { person } = usePersonStatusGroup(
  usernameWithDomain(event.value?.attributedTo)
);

const { anonymousReportsConfig } = useAnonymousReportsConfig();
const { anonymousActorId } = useAnonymousActorId();
const { eventCategories } = useEventCategories();

const { anonymousParticipationConfig } = useAnonymousParticipationConfig();
const { identities } = useCurrentUserIdentities();

// metaInfo() {
//   return {
//     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//     // @ts-ignore
//     title: this.eventTitle,
//     meta: [
//       // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//       // @ts-ignore
//       { name: "description", content: this.eventDescription },
//     ],
//   };
// },

const identity = ref<IPerson | undefined | null>(null);

const reportModal = ref();
const oldParticipationRole = ref<string | undefined>(undefined);
const isReportModalActive = ref(false);
const isShareModalActive = ref(false);
const isJoinModalActive = ref(false);
const isJoinConfirmationModalActive = ref(false);

const observer = ref<IntersectionObserver | null>(null);
const commentsObserver = ref<Element | null>(null);

const loadComments = ref(false);

const anonymousParticipation = ref<boolean | null>(null);
const actorForConfirmation = ref<IPerson | null>(null);
const messageForConfirmation = ref("");

const eventTitle = computed((): undefined | string => {
  return event.value?.title;
});

const eventDescription = computed((): undefined | string => {
  return event.value?.description;
});

const route = useRoute();
const router = useRouter();

const eventDescriptionElement = ref<HTMLElement | null>(null);

onMounted(async () => {
  identity.value = currentActor.value;
  if (route.hash.includes("#comment-")) {
    loadComments.value = true;
  }

  try {
    if (window.isSecureContext) {
      anonymousParticipation.value = await anonymousParticipationConfirmed();
    }
  } catch (e) {
    if (e instanceof AnonymousParticipationNotFoundError) {
      anonymousParticipation.value = null;
    } else {
      console.error(e);
    }
  }

  observer.value = new IntersectionObserver(
    (entries) => {
      // eslint-disable-next-line no-restricted-syntax
      for (const entry of entries) {
        if (entry) {
          loadComments.value = entry.isIntersecting || loadComments.value;
        }
      }
    },
    {
      rootMargin: "-50px 0px -50px",
    }
  );
  if (commentsObserver.value) {
    observer.value.observe(commentsObserver.value);
  }

  watch(eventDescription, () => {
    if (!eventDescription.value) return;
    if (!eventDescriptionElement.value) return;

    eventDescriptionElement.value.addEventListener("click", ($event) => {
      // TODO: Find the right type for target
      let { target }: { target: any } = $event;
      while (target && target.tagName !== "A") target = target.parentNode;
      // handle only links that occur inside the component and do not reference external resources
      if (target && target.matches(".hashtag") && target.href) {
        // some sanity checks taken from vue-router:
        // https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L106
        const { altKey, ctrlKey, metaKey, shiftKey, button, defaultPrevented } =
          $event;
        // don't handle with control keys
        if (metaKey || altKey || ctrlKey || shiftKey) return;
        // don't handle when preventDefault called
        if (defaultPrevented) return;
        // don't handle right clicks
        if (button !== undefined && button !== 0) return;
        // don't handle if `target="_blank"`
        if (target && target.getAttribute) {
          const linkTarget = target.getAttribute("target");
          if (/\b_blank\b/i.test(linkTarget)) return;
        }
        // don't handle same page links/anchors
        const url = new URL(target.href);
        const to = url.pathname;
        if (window.location.pathname !== to && $event.preventDefault) {
          $event.preventDefault();
          router.push(to);
        }
      }
    });
  });

  // this.$on("event-deleted", () => {
  //   return router.push({ name: RouteName.HOME });
  // });
});

const deleteEventMessage = computed(() => {
  const participantsLength = event.value?.participantStats.participant;
  const prefix = participantsLength
    ? t(
        "There are {participants} participants.",
        {
          participants: event.value.participantStats.participant,
        },
        event.value.participantStats.participant
      )
    : "";
  return `${prefix}
        ${t(
          "Are you sure you want to delete this event? This action cannot be reverted."
        )}
        <br><br>
        ${t('To confirm, type your event title "{eventTitle}"', {
          eventTitle: event.value?.title,
        })}`;
});

const notifier = inject<Notifier>("notifier");
const dialog = inject<Dialog>("dialog");

const {
  mutate: deleteEvent,
  onDone: onDeleteEventDone,
  onError: onDeleteEventError,
} = useDeleteEvent();

const escapeRegExp = (string: string) => {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
};

const openDeleteEventModal = () => {
  dialog?.prompt({
    title: t("Delete event"),
    message: deleteEventMessage.value,
    confirmText: t("Delete event"),
    cancelText: t("Cancel"),
    variant: "danger",
    hasIcon: true,
    hasInput: true,
    inputAttrs: {
      placeholder: event.value?.title,
      pattern: escapeRegExp(event.value?.title ?? ""),
    },
    onConfirm: (result: string) => {
      console.log("calling delete event", result);
      if (result.trim() === event.value?.title) {
        event.value?.id ? deleteEvent({ eventId: event.value?.id }) : null;
      }
    },
  });
};

onDeleteEventDone(() => {
  router.push({ name: RouteName.MY_EVENTS });
});

onDeleteEventError((error) => {
  console.error(error);
});

const {
  mutate: createReportMutation,
  onDone: onCreateReportDone,
  onError: onCreateReportError,
} = useCreateReport();

onCreateReportDone(() => {
  notifier?.success(t("Event {eventTitle} reported", { eventTitle }));
});

onCreateReportError((error) => {
  console.error(error);
});

const reportEvent = async (
  content: string,
  forward: boolean
): Promise<void> => {
  isReportModalActive.value = false;
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  reportModal.value.close();
  if (!organizer.value) return;

  createReportMutation({
    eventId: event.value?.id ?? "",
    reportedId: organizer.value?.id ?? "",
    content,
    forward,
  });
};

const joinEventWithConfirmation = (actor: IPerson): void => {
  isJoinConfirmationModalActive.value = true;
  actorForConfirmation.value = actor;
};

const {
  mutate: joinEventMutation,
  onDone: onJoinEventMutationDone,
  onError: onJoinEventMutationError,
} = useMutation<{
  joinEvent: IParticipant;
}>(JOIN_EVENT, () => ({
  update: (
    store: ApolloCache<{
      joinEvent: IParticipant;
    }>,
    { data }: FetchResult
  ) => {
    if (data == null) return;

    const participationCachedData = store.readQuery<{ person: IPerson }>({
      query: EVENT_PERSON_PARTICIPATION,
      variables: { eventId: event.value?.id, actorId: identity.value?.id },
    });

    if (participationCachedData?.person == undefined) {
      console.error(
        "Cannot update participation cache, because of null value."
      );
      return;
    }
    store.writeQuery({
      query: EVENT_PERSON_PARTICIPATION,
      variables: { eventId: event.value?.id, actorId: identity.value?.id },
      data: {
        person: {
          ...participationCachedData?.person,
          participations: {
            elements: [data.joinEvent],
            total: 1,
          },
        },
      },
    });

    const cachedData = store.readQuery<{ event: IEvent }>({
      query: FETCH_EVENT,
      variables: { uuid: event.value?.uuid },
    });
    if (cachedData == null) return;
    const { event: cachedEvent } = cachedData;
    if (cachedEvent === null) {
      console.error(
        "Cannot update event participant cache, because of null value."
      );
      return;
    }
    const participantStats = { ...cachedEvent.participantStats };

    if (data.joinEvent.role === ParticipantRole.NOT_APPROVED) {
      participantStats.notApproved += 1;
    } else {
      participantStats.going += 1;
      participantStats.participant += 1;
    }

    store.writeQuery({
      query: FETCH_EVENT,
      variables: { uuid: props.uuid },
      data: {
        event: {
          ...cachedEvent,
          participantStats,
        },
      },
    });
  },
}));

const joinEvent = async (
  identityForJoin: IPerson,
  message: string | null = null
): Promise<void> => {
  isJoinConfirmationModalActive.value = false;
  isJoinModalActive.value = false;
  joinEventMutation({
    eventId: event.value?.id,
    actorId: identityForJoin?.id,
    message,
  });
};

onJoinEventMutationDone(({ data }) => {
  if (data) {
    if (data.joinEvent.role === ParticipantRole.NOT_APPROVED) {
      participationRequestedMessage();
    } else {
      participationConfirmedMessage();
    }
  }
});

onJoinEventMutationError((error) => {
  console.error(error);
});

const confirmLeave = (): void => {
  dialog?.confirm({
    title: t('Leaving event "{title}"', {
      title: event.value?.title,
    }),
    message: t(
      'Are you sure you want to cancel your participation at event "{title}"?',
      {
        title: event.value?.title,
      }
    ),
    confirmText: t("Leave event"),
    cancelText: t("Cancel"),
    variant: "danger",
    hasIcon: true,
    onConfirm: () => {
      if (event.value && currentActor.value?.id) {
        console.log("calling leave event");
        leaveEvent(event.value, currentActor.value.id);
      }
    },
  });
};

watch(participations, () => {
  if (participations.value.length > 0) {
    if (
      oldParticipationRole.value &&
      participations.value[0].role !== ParticipantRole.NOT_APPROVED &&
      oldParticipationRole.value !== participations.value[0].role
    ) {
      switch (participations.value[0].role) {
        case ParticipantRole.PARTICIPANT:
          participationConfirmedMessage();
          break;
        case ParticipantRole.REJECTED:
          participationRejectedMessage();
          break;
        default:
          participationChangedMessage();
          break;
      }
    }
    oldParticipationRole.value = participations.value[0].role;
  }
});

const participationConfirmedMessage = () => {
  notifier?.success(t("Your participation has been confirmed"));
};

const participationRequestedMessage = () => {
  notifier?.success(t("Your participation has been requested"));
};

const participationRejectedMessage = () => {
  notifier?.error(t("Your participation has been rejected"));
};

const participationChangedMessage = () => {
  notifier?.info(t("Your participation status has been changed"));
};

const downloadIcsEvent = async (): Promise<void> => {
  const data = await (
    await fetch(`${GRAPHQL_API_ENDPOINT}/events/${props.uuid}/export/ics`)
  ).text();
  const blob = new Blob([data], { type: "text/calendar" });
  const link = document.createElement("a");
  link.href = window.URL.createObjectURL(blob);
  link.download = `${event.value?.title}.ics`;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
};

const triggerShare = (): void => {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore-start
  if (navigator.share) {
    navigator
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      .share({
        title: event.value?.title,
        url: event.value?.url,
      })
      .then(() => console.debug("Successful share"))
      .catch((error: any) => console.debug("Error sharing", error));
  } else {
    isShareModalActive.value = true;
    // send popup
  }
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore-end
};

const handleErrors = (errors: AbsintheGraphQLErrors): void => {
  if (
    errors.some((error) => error.status_code === 404) ||
    errors.some(({ message }) => message.includes("has invalid value $uuid"))
  ) {
    router.replace({ name: RouteName.PAGE_NOT_FOUND });
  }
};

onFetchEventError(({ graphQLErrors }) =>
  handleErrors(graphQLErrors as AbsintheGraphQLErrors)
);

// const actorIsParticipant = computed((): boolean => {
//   if (actorIsOrganizer.value) return true;

//   return (
//     participations.value.length > 0 &&
//     participations.value[0].role === ParticipantRole.PARTICIPANT
//   );
// });

const actorIsOrganizer = computed((): boolean => {
  return (
    participations.value.length > 0 &&
    participations.value[0].role === ParticipantRole.CREATOR
  );
});

const hasGroupPrivileges = computed((): boolean => {
  return (
    person.value?.memberships !== undefined &&
    person.value?.memberships?.total > 0 &&
    [MemberRole.MODERATOR, MemberRole.ADMINISTRATOR].includes(
      person.value?.memberships?.elements[0].role
    )
  );
});

const canManageEvent = computed((): boolean => {
  return actorIsOrganizer.value || hasGroupPrivileges.value;
});

// const endDate = computed((): string | undefined => {
//   return event.value?.endsOn && event.value.endsOn > event.value.beginsOn
//     ? event.value.endsOn
//     : event.value?.beginsOn;
// });

const maximumAttendeeCapacity = computed((): number | undefined => {
  return event.value?.options?.maximumAttendeeCapacity;
});

const eventCapacityOK = computed((): boolean => {
  if (event.value?.draft) return true;
  if (!maximumAttendeeCapacity.value) return true;
  return (
    event.value?.options?.maximumAttendeeCapacity !== undefined &&
    event.value.participantStats.participant !== undefined &&
    event.value?.options?.maximumAttendeeCapacity >
      event.value.participantStats.participant
  );
});

// const numberOfPlacesStillAvailable = computed((): number | undefined => {
//   if (event.value?.draft) return maximumAttendeeCapacity.value;
//   return (
//     (maximumAttendeeCapacity.value ?? 0) -
//     (event.value?.participantStats.participant ?? 0)
//   );
// });

const anonymousParticipationConfirmed = async (): Promise<boolean> => {
  return isParticipatingInThisEvent(props.uuid);
};

const cancelAnonymousParticipation = async (): Promise<void> => {
  if (!event.value || !anonymousActorId.value) return;
  const token = (await getLeaveTokenForParticipation(props.uuid)) as string;
  leaveEvent(event.value, anonymousActorId.value, token);
  await removeAnonymousParticipation(props.uuid);
  anonymousParticipation.value = null;
};

const ableToReport = computed((): boolean => {
  return (
    currentActor.value?.id != null ||
    anonymousReportsConfig.value?.allowed === true
  );
});

const organizer = computed((): IActor | null => {
  if (event.value?.attributedTo?.id) {
    return event.value.attributedTo;
  }
  if (event.value?.organizerActor) {
    return event.value.organizerActor;
  }
  return null;
});

const organizerDomain = computed((): string | undefined => {
  return organizer.value?.domain ?? undefined;
});

const metadataToComponent: Record<string, string> = {
  "mz:live:twitch:url": "IntegrationTwitch",
  "mz:live:peertube:url": "IntegrationPeertube",
  "mz:live:youtube:url": "IntegrationYoutube",
  "mz:visio:jitsi_meet": "IntegrationJitsiMeet",
  "mz:notes:etherpad:url": "IntegrationEtherpad",
};

const integrations = computed((): Record<string, IEventMetadataDescription> => {
  return (event.value?.metadata ?? [])
    .map((val) => {
      const def = eventMetaDataList.find((dat) => dat.key === val.key);
      return {
        ...def,
        ...val,
      };
    })
    .reduce((acc: Record<string, IEventMetadataDescription>, metadata) => {
      const component = metadataToComponent[metadata.key];
      if (component !== undefined) {
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        acc[component] = metadata;
      }
      return acc;
    }, {});
});

const showMap = ref(false);

const { routingType } = useRoutingType();

const eventCategory = computed((): string | undefined => {
  if (event.value?.category === "MEETING") {
    return undefined;
  }
  return (eventCategories.value ?? []).find((eventCategoryToFind) => {
    return eventCategoryToFind.id === event.value?.category;
  })?.label as string;
});

const {
  mutate: leaveEventMutation,
  onDone: onLeaveEventMutationDone,
  onError: onLeaveEventMutationError,
} = useMutation<{ leaveEvent: { actor: { id: string } } }>(LEAVE_EVENT, () => ({
  update: (
    store: ApolloCache<{
      leaveEvent: IParticipant;
    }>,
    { data }: FetchResult,
    { context, variables }
  ) => {
    if (data == null) return;
    let participation;

    const token = context?.token;
    const actorId = variables?.actorId;
    const localEventId = variables?.eventId;
    const eventUUID = context?.eventUUID;
    const isAnonymousParticipationConfirmed =
      context?.isAnonymousParticipationConfirmed;

    if (!token) {
      const participationCachedData = store.readQuery<{
        person: IPerson;
      }>({
        query: EVENT_PERSON_PARTICIPATION,
        variables: { eventId: localEventId, actorId },
      });
      if (participationCachedData == null) return;
      const { person: cachedPerson } = participationCachedData;
      [participation] = cachedPerson?.participations?.elements ?? [undefined];

      store.modify({
        id: `Person:${actorId}`,
        fields: {
          participations() {
            return {
              elements: [],
              total: 0,
            };
          },
        },
      });
    }

    const eventCachedData = store.readQuery<{ event: IEvent }>({
      query: FETCH_EVENT,
      variables: { uuid: eventUUID },
    });
    if (eventCachedData == null) return;
    const { event: eventCached } = eventCachedData;
    if (eventCached === null) {
      console.error("Cannot update event cache, because of null value.");
      return;
    }
    const participantStats = { ...eventCached.participantStats };
    if (participation && participation?.role === ParticipantRole.NOT_APPROVED) {
      participantStats.notApproved -= 1;
    } else if (isAnonymousParticipationConfirmed === false) {
      participantStats.notConfirmed -= 1;
    } else {
      participantStats.going -= 1;
      participantStats.participant -= 1;
    }
    store.writeQuery({
      query: FETCH_EVENT,
      variables: { uuid: eventUUID },
      data: {
        event: {
          ...eventCached,
          participantStats,
        },
      },
    });
  },
}));

const leaveEvent = (
  eventToLeave: IEvent,
  actorId: string,
  token: string | null = null,
  isAnonymousParticipationConfirmed: boolean | null = null
): void => {
  leaveEventMutation(
    {
      eventId: eventToLeave.id,
      actorId,
      token,
    },
    {
      context: {
        token,
        isAnonymousParticipationConfirmed,
        eventUUID: eventToLeave.uuid,
      },
    }
  );
};

onLeaveEventMutationDone(({ data }) => {
  if (data) {
    notifier?.success(t("You have cancelled your participation"));
  }
});

const snackbar = inject<Snackbar>("snackbar");

onLeaveEventMutationError((error) => {
  snackbar?.open({
    message: error.message,
    variant: "danger",
    position: "bottom",
  });
  console.error(error);
});

useHead({
  title: computed(() => eventTitle.value ?? ""),
  meta: [{ name: "description", content: eventDescription.value }],
});
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;
.section {
  padding: 1rem 2rem 4rem;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}

div.sidebar {
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;

  position: relative;

  &::before {
    content: "";
    background: #b3b3b2;
    position: absolute;
    bottom: 30px;
    top: 30px;
    left: 0;
    height: calc(100% - 60px);
    width: 1px;
  }

  div.organizer {
    display: inline-flex;
    padding-top: 10px;

    a {
      color: #4a4a4a;

      span {
        line-height: 2.7rem;
        @include padding-right(6px);
      }
    }
  }
}

.intro {
  // background: white;

  .is-3-tablet {
    width: initial;
  }

  p.tags {
    a {
      text-decoration: none;
    }

    span {
      &.tag {
        margin: 0 2px;
      }
    }
  }
}

.event-description-wrapper {
  padding: 0;

  aside.event-metadata {
    min-width: 20rem;
    flex: 1;

    .sticky {
      // position: sticky;
      // background: white;
      top: 50px;
      padding: 1rem;
    }
  }

  div.event-description-comments {
    min-width: 20rem;
    padding: 1rem;
    flex: 2;
    // background: white;
  }

  .description-content {
    :deep(h1) {
      font-size: 2rem;
    }

    :deep(h2) {
      font-size: 1.5rem;
    }

    :deep(h3) {
      font-size: 1.25rem;
    }

    :deep(ul) {
      list-style-type: disc;
    }

    :deep(li) {
      margin: 10px auto 10px 2rem;
    }

    :deep(blockquote) {
      border-left: 0.2em solid #333;
      display: block;
      @include padding-left(1rem);
    }

    :deep(p) {
      margin: 10px auto;

      a {
        display: inline-block;
        padding: 0.3rem;
        color: #111;

        &:empty {
          display: none;
        }
      }
    }
  }
}

.comments {
  padding-top: 3rem;

  a h3#comments {
    margin-bottom: 10px;
  }
}

.more-events {
  // background: white;
  padding: 1rem 1rem 4rem;

  & > .title {
    font-size: 1.5rem;
  }
}

// .dropdown .dropdown-trigger span {
//   cursor: pointer;
// }

// a.dropdown-item,
// .dropdown .dropdown-menu .has-link a,
// button.dropdown-item {
//   white-space: nowrap;
//   width: 100%;
//   @include padding-right(1rem);
//   text-align: right;
// }

a.participations-link {
  text-decoration: none;
}

.no-border {
  border: 0;
  cursor: auto;
}

.wrapper,
.intro-wrapper {
  display: flex;
  flex-direction: column;
}

.intro-wrapper {
  position: relative;
  padding: 0 16px 16px;
  // background: #fff;

  .date-calendar-icon-wrapper {
    margin-top: 16px;
    height: 0;
    display: flex;
    align-items: flex-end;
    align-self: flex-start;
    margin-bottom: 7px;
    @include margin-left(0);
  }
}
.title {
  margin: 0;
  font-size: 2rem;
}
</style>
