<template>
  <!-- eslint-disable vue/no-v-html -->
  <div
    class="module"
    v-if="module.id"
  >
    <div class="module__header">
      <h2
        class="module__meta-title"
        id="meta-title"
      >
        {{ module.metaTitle }}
      </h2>

      <div
        class="module__categoryindicators"
        v-if="$flavor.showModuleFilter"
      >
        <pill :text="module.level?.name"></pill>
        <pill :text="module.category?.name"></pill>
      </div>
    </div>

    <h1
      class="module__title"
      data-cy="module-title"
    >
      {{ module.title }}
    </h1>
    <div class="module__hero">
      <img
        :src="module.heroImage"
        alt=""
        class="module__hero-image"
      />
      <h5
        class="module__hero-source"
        v-if="module.heroSource"
      >
        Quelle: {{ module.heroSource }}
      </h5>
    </div>

    <div class="module__intro-wrapper">
      <bookmark-actions
        :bookmarked="!!module.bookmark"
        :note="note"
        :edit-mode="module.inEditMode"
        class="module__bookmark-actions"
        data-cy="module-bookmark-actions"
        @add-note="$emit('addNote')"
        @edit-note="$emit('editNote')"
        @bookmark="$emit('bookmark', !module.bookmark)"
      />
      <div
        class="module__intro intro"
        data-cy="module-intro"
        ref="introDiv"
        v-html="module.intro"
      />
    </div>

    <h3
      v-if="$flavor.showObjectivesTitle && module.objectiveGroups.length && showObjectives"
      id="objectives"
    >
      <span>Lernziele</span>
    </h3>

    <div
      class="module__objective-groups"
      v-if="module.objectiveGroups.length && showObjectives"
    >
      <objective-groups
        :groups="languageCommunicationObjectiveGroups"
        v-if="languageCommunicationObjectiveGroups.length"
      />

      <objective-groups
        :groups="societyObjectiveGroups"
        v-if="societyObjectiveGroups.length"
      />

      <objective-groups
        :groups="interdisciplinaryObjectiveGroups"
        v-if="interdisciplinaryObjectiveGroups.length"
      />
    </div>

    <chapter
      :chapter="chapter"
      :index="index"
      :edit-mode="module.inEditMode"
      v-for="(chapter, index) in module.chapters"
      :key="chapter.id"
    />
  </div>
</template>

<script setup lang="ts">
import ObjectiveGroups from '@/components/objective-groups/ObjectiveGroups.vue';
import Chapter from '@/components/Chapter.vue';
import BookmarkActions from '@/components/notes/BookmarkActions.vue';
import Pill from '@/components/ui/Pill.vue';
import {
  SelectionHandlerType,
  SelectionHandlerOptions,
  getSelectionHandler,
  createHighlightCurry,
  markHighlight,
} from '@/helpers/highlight';
import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
import { AddHighlightArgument, HighlightNode, ModuleNode } from '@/__generated__/graphql';
import { graphql } from '@/__generated__';
import highlightSidebar from '@/helpers/highlight-sidebar';
import { doUpdateHighlight } from '@/graphql/mutations';
import Mark from 'mark.js';
import { useRoute } from 'vue-router';

export interface Props {
  module: ModuleNode;
}
const route = useRoute();
const props = defineProps<Props>();
let selectionHandler: SelectionHandlerType;

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

const moduleHighlightsFragment = graphql(`
  fragment ModuleHighlightsFragment on ModuleNode {
    id
    __typename
    slug
    highlights {
      ...HighlightParts
    }
  }
`);

const createHighlight = createHighlightCurry({
  fragment: moduleHighlightsFragment,
  fragmentName: 'ModuleHighlightsFragment',
  cacheSignature: { slug: props.module.slug, __typename: 'ModuleNode' },
  isContentHighlight: false,
});

onMounted(() => {
  const element = introDiv.value;
  if (element !== null) {
    const options: SelectionHandlerOptions = {
      el: element,
      page: props.module,
      parentSelector: 'module__intro',
      onChangeColor: (newHighlight: AddHighlightArgument) => {
        createHighlight(newHighlight);
      },
      onCreateNote: async (newHighlight: AddHighlightArgument) => {
        // we also open the sidebar when clicking on the note icon
        const highlight = await createHighlight(newHighlight);
        highlightSidebar.open({
          highlight,
          onUpdateText: (text: string) => {
            doUpdateHighlight({
              input: {
                note: text,
                id: highlight.id,
              },
            });
          },
        });
      },
    };

    selectionHandler = getSelectionHandler(options);

    element.addEventListener('mouseup', selectionHandler);
  }

  markHighlights();
});

const markHighlights = () => {
  if (props.module.highlights && introDiv.value) {
    for (const highlight of props.module.highlights) {
      const highlightNode = highlight as HighlightNode;
      const element = introDiv.value.children[highlightNode.paragraphIndex] as HTMLElement;
      markHighlight(highlightNode, element, element);
    }
  }
};

onUnmounted(() => {
  const element = introDiv.value;

  if (element !== null) {
    element.removeEventListener('mouseup', selectionHandler);
  }
});

const unmark = () => {
  for (const paragraph of introDiv.value.children) {
    const instance = new Mark(paragraph);
    instance.unmark();
  }
};

const highlights = computed(() => {
  return props.module ? props.module.highlights : [];
});

watch(
  () => highlights.value?.filter((h) => h.color),
  () => {
    unmark();
    markHighlights();
  }
);

const note = computed(() => {
  if (!(props.module && props.module.bookmark)) {
    return;
  }
  return props.module.bookmark.note;
});

const filterObjectiveGroup = (title: string) => {
  return props.module.objectiveGroups
    ? props.module.objectiveGroups.filter((group) => group.title.toLowerCase() === title)
    : [];
};

const languageCommunicationObjectiveGroups = computed(() => {
  return filterObjectiveGroup('language_communication');
});
const societyObjectiveGroups = computed(() => {
  return filterObjectiveGroup('society');
});
const interdisciplinaryObjectiveGroups = computed(() => {
  return filterObjectiveGroup('interdisciplinary');
});
const showObjectives = computed(() => {
  return route && route.query['show-objectives'] !== undefined;
});
</script>

<style scoped lang="scss">
@import 'styles/helpers';

.module {
  display: flex;
  justify-self: center;
  max-width: 100vw;

  padding: $large-spacing 0;
  @include desktop {
    width: 800px;
    padding: $large-spacing 15px;
  }
  flex-direction: column;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;

  &__hero {
    margin-bottom: 35px;
  }

  &__hero-image {
    max-width: 100%;
    border-radius: 12px;
  }

  &__hero-source {
    @include tiny-text;
    line-height: 25px;
  }

  &__header {
    display: flex;
    justify-content: flex-start;
    align-items: stretch;
    margin-bottom: $small-spacing;
  }

  &__meta-title {
    @include meta-title;
    margin-right: $medium-spacing;
  }

  &__intro-wrapper {
    position: relative;
  }

  &__intro {
    > :deep(p) {
      margin-bottom: $large-spacing;
      @include lead-paragraph;

      &:last-child {
        margin-bottom: 0;
      }
    }

    > :deep(ul) {
      @include list-parent;

      > li {
        @include list-child;
        @include lead-paragraph;
      }
    }
  }

  &__bookmark-actions {
    margin-top: 3px;
  }

  &__objective-groups {
    margin-bottom: 2 * $large-spacing;
  }
}
</style>
