<template>
  <div class="content-block-form content-list__parent">
    <div class="content-block-form__content">
      <h1
        class="heading-1 content-block-form__heading"
        data-cy="content-block-form-heading"
      >
        {{ title }}
      </h1>

      <!-- Assignment toggle / checkbox -->
      <toggle
        :bordered="false"
        :checked="localContentBlock.isAssignment"
        class="content-block-form__task-toggle"
        label="Inhaltsblock als Auftrag formatieren"
        v-if="hasDefaultFeatures"
        @input="localContentBlock.isAssignment = $event"
      />

      <!-- Form for title of content block -->
      <content-form-section
        data-cy="content-form-title-section"
        title="Titel (Pflichtfeld)"
      >
        <input-with-label
          :value="localContentBlock.title"
          data-cy="content-block-title"
          placeholder="z.B. Auftrag 3"
          @input="localContentBlock.title = $event"
        />
      </content-form-section>

      <!-- Add content at top of content block -->
      <add-content-link @click="addBlock(-1)" />

      <!-- Loop for outer contents layer -->
      <div
        class="content-list content-list--creator content-block-form__segment"
        v-for="(block, outer) in localContentBlock.contents"
        :key="block.id"
      >
        <!-- If the block is a content list -->
        <div
          class="content-block-form__segment"
          data-cy="content-list"
          v-if="block.type === 'content_list_item'"
        >
          <content-element-actions
            class="content-block-form__actions"
            :actions="{ extended: true, up: outer > 0, down: outer < localContentBlock.contents.length }"
            @remove="remove(outer)"
            @move-up="up(outer)"
            @move-down="down(outer)"
            @move-top="top(outer)"
            @move-bottom="bottom(outer)"
          />
          <ol
            class="content-list__item"
            data-cy="content-list-item"
          >
            <li
              class="content-block-form__segment"
              v-for="(content, index) in block.contents"
              :key="content.id"
            >
              <content-element
                :first-element="index === 0"
                :last-element="index === block.contents.length - 1"
                :element="content"
                class="content-block-form__segment"
                :top-level="false"
                @update="update(index, $event, outer)"
                @remove="remove(outer, index, $event)"
                @up="up(outer, index)"
                @down="down(outer, index)"
                @top="top(outer, index)"
                @bottom="bottom(outer, index)"
              />

              <add-content-link
                class="content-block-form__add-button"
                @click="addBlock(outer, index)"
              />
            </li>
          </ol>
        </div>

        <!-- If the block is a single element -->
        <content-element
          :element="block"
          class="content-block-form__segment"
          :top-level="true"
          :first-element="outer === 0"
          :last-element="outer === localContentBlock.contents.length - 1"
          v-else
          @update="update(outer, $event)"
          @remove="remove(outer, undefined, $event)"
          @up="up(outer)"
          @down="down(outer)"
          @top="top(outer)"
          @bottom="bottom(outer)"
        />

        <!-- Add element after the looped item -->
        <add-content-link @click="addBlock(outer)" />
      </div>
    </div>
    <!-- -->
    <!-- Save and Cancel buttons -->
    <footer class="content-block-form__footer">
      <div class="content-block-form__buttons">
        <button
          :disabled="!isValid && !isSaving"
          class="button button--primary"
          data-cy="save-button"
          @click="save(localContentBlock)"
        >
          Speichern
        </button>
        <a
          class="button"
          @click="$emit('back')"
          >Abbrechen</a
        >
      </div>
    </footer>
  </div>
</template>

<script setup lang="ts">
import Toggle from '@/components/ui/Toggle.vue';
import ContentFormSection from '@/components/content-block-form/ContentFormSection.vue';
import InputWithLabel from '@/components/ui/InputWithLabel.vue';
import AddContentLink from '@/components/content-block-form/AddContentLink.vue';
import ContentElement from '@/components/content-block-form/ContentElement.vue';
import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue';
import { DEFAULT_FEATURE_SET } from '@/consts/features.consts';
import { computed, inject, provide, ref } from 'vue';
import { ContentBlock, numberOrUndefined } from '@/@types';
import { CHOOSER, transformInnerContents } from '@/components/content-block-form/helpers.js';
import {
  insertAtIndex,
  moveToIndex,
  removeAtIndex,
  replaceAtIndex,
  swapElements,
} from '@/graphql/immutable-operations';
import { Modal } from '@/plugins/modal.types';

export interface Props {
  title?: String;
  contentBlock: ContentBlock;
  features?: string;
  isSaving?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  title: '',
  features: DEFAULT_FEATURE_SET,
  isSaving: false,
});

const emit = defineEmits(['save']);
const modal = inject('modal') as Modal;

const localContentBlock = ref(
  Object.assign(
    {},
    {
      title: props.contentBlock.title,
      contents: transformInnerContents([...props.contentBlock.contents]),
      id: props.contentBlock.id || undefined,
      isAssignment: props.contentBlock.type && props.contentBlock.type.toLowerCase() === 'task',
    }
  )
);

const hasDefaultFeatures = computed(() => {
  return props.features === DEFAULT_FEATURE_SET;
});
const isValid = computed(() => {
  return localContentBlock.value.title > '';
});

const save = (contentBlock: ContentBlock) => {
  if (!props.isSaving) {
    emit('save', contentBlock);
  }
};
const executeRemoval = (outer: number, inner: numberOrUndefined = undefined) => {
  if (inner === undefined) {
    // not a list item container, just remove the element from the outer array
    localContentBlock.value.contents = removeAtIndex(localContentBlock.value.contents, outer);
  } else {
    let prevInnerContents = localContentBlock.value.contents[outer].contents;
    let innerContents = removeAtIndex(prevInnerContents, inner);

    if (innerContents.length) {
      /*
              there is still an element inside the outer element after removal,
              so we replace the previous element in the outer array with the new one with fewer contents
             */
      let element = {
        ...localContentBlock.value.contents[outer],
        contents: innerContents,
      };
      localContentBlock.value.contents = replaceAtIndex(localContentBlock.value.contents, outer, element);
    } else {
      // inner contents is now empty, remove the whole element from the outer array
      localContentBlock.value.contents = removeAtIndex(localContentBlock.value.contents, outer);
    }
  }
};
const update = (index: number, element: any, parent?: number) => {
  if (parent === undefined) {
    // element is top level
    localContentBlock.value.contents = replaceAtIndex(localContentBlock.value.contents, index, element);
  } else {
    const parentBlock = localContentBlock.value.contents[parent];

    const newElementContents = replaceAtIndex(parentBlock.contents, index, element);
    const newBlock = {
      ...parentBlock,
      contents: newElementContents,
    };
    localContentBlock.value.contents = replaceAtIndex(localContentBlock.value.contents, parent, newBlock);
  }
};
const addBlock = (afterOuterIndex: number, innerIndex?: number) => {
  if (innerIndex !== undefined) {
    const block = localContentBlock.value.contents[afterOuterIndex];
    const element = {
      ...block,
      contents: insertAtIndex(block.contents, innerIndex + 1, {
        id: -1,
        type: CHOOSER,
      }),
    };

    localContentBlock.value.contents = replaceAtIndex(localContentBlock.value.contents, afterOuterIndex, element);
  } else {
    const element = {
      id: -1,
      type: CHOOSER,
      includeListOption: true,
    };

    localContentBlock.value.contents = insertAtIndex(localContentBlock.value.contents, afterOuterIndex + 1, element);
  }
};

const remove = (outer: number, inner?: number, askForConfirmation = true) => {
  if (askForConfirmation) {
    modal
      .open('confirm')
      .then(() => {
        executeRemoval(outer, inner);
      })
      .catch(() => {});
  } else {
    executeRemoval(outer, inner);
  }
};
const shift = (outer: number, inner: numberOrUndefined = undefined, distance: number) => {
  if (inner === undefined) {
    localContentBlock.value.contents = swapElements(localContentBlock.value.contents, outer, outer + distance);
  } else {
    const { contents } = localContentBlock.value;
    const outerElement = contents[outer];
    const newOuterElement = {
      ...outerElement,
      contents: swapElements(outerElement.contents, inner, inner + distance),
    };
    localContentBlock.value.contents = replaceAtIndex(contents, outer, newOuterElement);
  }
};
const top = (outer: number, inner: numberOrUndefined = undefined) => {
  if (inner === undefined) {
    localContentBlock.value.contents = moveToIndex(localContentBlock.value.contents, outer, 0);
  } else {
    const { contents } = localContentBlock.value;
    const outerElement = contents[outer];
    const newOuterElement = {
      ...outerElement,
      contents: moveToIndex(outerElement.contents, inner, 0),
    };
    localContentBlock.value.contents = replaceAtIndex(contents, outer, newOuterElement);
  }
};
const up = (outer: number, inner: numberOrUndefined = undefined) => {
  shift(outer, inner, -1);
};

const down = (outer: number, inner: numberOrUndefined = undefined) => {
  shift(outer, inner, 1);
};
const bottom = (outer: number, inner: numberOrUndefined = undefined) => {
  if (inner === undefined) {
    const maxIndex = localContentBlock.value.contents.length - 1;
    localContentBlock.value.contents = moveToIndex(localContentBlock.value.contents, outer, maxIndex);
  } else {
    const { contents } = localContentBlock.value;
    const outerElement = contents[outer];
    const maxIndex = outerElement.contents.length - 1;
    const newOuterElement = {
      ...outerElement,
      contents: moveToIndex(outerElement.contents, inner, maxIndex),
    };
    localContentBlock.value.contents = replaceAtIndex(contents, outer, newOuterElement);
  }
};

provide('features', props.features);
</script>

<style lang="scss">
@import 'styles/helpers';
// override parent page properties
.layout--no-scroll {
  padding-bottom: 20px;
  height: 100vh;
  overflow-y: hidden;
  box-sizing: border-box;

  .module-page {
    height: 100vh;
    grid-template-rows: 60px 1fr;
    padding-bottom: $small-spacing;
    box-sizing: border-box;
  }
}
</style>

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

.content-block-form {
  max-width: 100%;
  display: grid;
  grid-template-columns: 100vw;
  grid-template-rows: 85vh 80px;
  height: 100vh;
  grid-template-areas:
    'content'
    'footer';

  @media (-webkit-min-device-pixel-ratio: 1.25) {
    grid-template-rows: 80vh auto;
    height: auto;
  }

  &__heading {
    @include heading-1;
  }

  &__task-toggle {
    margin-bottom: $large-spacing;
  }

  &__add-button {
  }

  &__segment {
    display: flex;
    flex-direction: column;
    align-items: stretch;

    margin-bottom: $large-spacing;

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

  &__actions {
    align-self: flex-end;
  }

  &__content {
    grid-area: content;
    overflow-x: visible;
    overflow-y: auto;
    padding: 10px;
    align-items: center;
    display: flex;
    flex-direction: column;

    & > * {
      // we make an exception and use a wildcard here
      width: 800px;
      max-width: 100vw;
      box-sizing: border-box;
    }
  }

  &__footer {
    padding: $medium-spacing 0;
    border-top: 1px solid $color-silver;
    margin-top: auto;
    grid-area: footer;
    justify-content: center;
    display: flex;
  }

  &__buttons {
    width: 800px;
  }
}
</style>
