<template>
  <v-container>
    <div v-if="editor" class="menu-editor">
      <v-btn small @click="openImgModal()" plain>
        <v-icon>mdi-image </v-icon>
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleBold().run()"
        :class="{ 'is-active': editor.isActive('bold') }"
      >
        <v-icon> mdi-format-bold </v-icon>
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleItalic().run()"
        :class="{ 'is-active': editor.isActive('italic') }"
      >
        <v-icon> mdi-format-italic </v-icon>
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().setParagraph().run()"
        :class="{ 'is-active': editor.isActive('paragraph') }"
      >
        <v-icon> mdi-format-paragraph </v-icon>
      </v-btn>

      <v-btn small plain @click="addInstagram">
        <v-icon>mdi-instagram </v-icon>
      </v-btn>

      <v-btn small plain @click="addIframe">
        <v-icon></v-icon>
        iframe
      </v-btn>

      <v-btn small plain @click="openGalleryModalInsert">
        <v-icon></v-icon>
        Галерея
      </v-btn>

      <v-btn
        small
        plain
        @click="openLinkModal"
        :class="{ 'is-active': editor.isActive('link') }"
      >
        ссылка
      </v-btn>

      <v-btn
        small
        plain
        @click="setTooltip"
        :class="{ 'is-active': editor.isActive('tooltip') }"
      >
        подсказка
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
      >
        h1
      </v-btn>

      <v-btn
        small
        plain
        @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
      >
        h2
      </v-btn>

      <v-btn
        small
        plain
        @click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
      >
        h3
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
      >
        h4
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
      >
        ИФ
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
      >
        Выделить
      </v-btn>
      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleBulletList().run()"
        :class="{ 'is-active': editor.isActive('bulletList') }"
      >
        <v-icon>mdi-format-list-bulleted</v-icon>
      </v-btn>

      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleOrderedList().run()"
        :class="{ 'is-active': editor.isActive('orderedList') }"
      >
        <v-icon>mdi-format-list-numbered</v-icon>
      </v-btn>
      <v-btn
        plain
        small
        @click="editor.chain().focus().toggleBlockquote().run()"
        :class="{ 'is-active': editor.isActive('blockquote') }"
      >
        <v-icon>mdi-format-quote-close-outline</v-icon>
      </v-btn>

      <v-btn plain small @click="editor.chain().focus().clearNodes().unsetAllMarks().run()">
        <v-icon>mdi-format-clear</v-icon>
      </v-btn>

      <v-btn plain small @click="openHtmlModal">
        <v-icon>mdi-file-code-outline</v-icon>
      </v-btn>

      <v-btn plain small @click="editor.chain().focus().undo().run()">
        <v-icon>mdi-undo-variant</v-icon>
      </v-btn>

      <v-btn plain small @click="editor.chain().focus().redo().run()">
        <v-icon> mdi-redo-variant </v-icon>
      </v-btn>
    </div>

    <div class="editor" ref="editor">
      <editor-content @click.native="onClickEditor" :editor="editor" />
    </div>

    <modal name="link-modal" height="auto" v-if="editor">
      <v-text-field  v-model="link.href" label="Ссылка"></v-text-field>
      <v-checkbox class="mt-0" v-model="link.nofollow" label="No follow"></v-checkbox>
      <v-btn :disabled="!link.href" @click="setLink" text color="primary">Ок</v-btn>
      <v-btn @click="unsetLink" text color="red" v-if="editor.isActive('link')">Удалить</v-btn>
    </modal>

    <modal name="img-modal" @closed="image.messages = []">
      <v-file-input
        label="Картинка:"
        type="file"
        accept="image/*"
        ref="inputEditor"
        :error-messages="image.messages"
        v-model="image.file"
      ></v-file-input>

      <v-btn @click="addImage">Добавить изображение</v-btn>
    </modal>

    <modal
      height="auto"
      name="gallery-modal"
      @closed="gallery.messages = []"
      :edit-mode="false"
    >
      <div class="gallery-container" ref="galleryContainer">
        <draggable v-model="gallery.list">
          <transition-group type="transition" name="flip-list">
            <div
              class="gallery-item"
              v-for="(img, index) in gallery.list"
              :key="img.url"
            >
              <img :src="img.url" :alt="img.name" />

              <v-text-field
                prepend-icon="mdi-image-outline"
                placeholder="Название изображения"
                v-model="img.name"
              ></v-text-field>

              <div
                class="gallery-item_delete"
                @click="gallery.list.splice(index, 1)"
              >
                <v-icon> mdi-delete </v-icon>
              </div>
            </div>
          </transition-group>
        </draggable>
      </div>

      <v-file-input
        @change="addImageToGallery"
        small-chips
        multiple
        label="Картинка:"
        type="file"
        accept="image/*"
        ref="galleryFileInput"
        :error-messages="gallery.messages"
        v-model="gallery.files"
      ></v-file-input>

      <v-btn
        text
        color="primary"
        @click="insertGallery"
        v-if="!gallery.editMode"
        :disabled="!gallery.list.length"
      >
        Вставить галерею в редактор
      </v-btn>

      <v-btn
        text
        color="primary"
        @click="editGallery"
        v-else
        :disabled="!gallery.list.length"
      >
        Сохранить галерею
      </v-btn>
    </modal>

    <modal name="html-modal" width="80%" height="auto">
      <v-textarea v-model="content" rows="10" outlined label="Исходный код HTML"></v-textarea>
      <v-btn @click="saveHtml" text color="primary" class="mt-0">Сохранить</v-btn>
    </modal>
  </v-container>
</template>

<script>
import Iframe from "./iframe.ts";
import Tooltip from "./tooltip.ts";
import Link from "./link.ts";
import Gallery from "./gallery";
import Focus from "@tiptap/extension-focus";
import Image from "@tiptap/extension-image";
import { Editor, EditorContent } from "@tiptap/vue-2";
import StarterKit from "@tiptap/starter-kit";
import Typography from "@tiptap/extension-typography";
import draggable from "vuedraggable";

export default {
  components: {
    EditorContent,
    draggable,
  },

  data() {
    return {
      editor: null,
      content: this.value,

      link: {
        href: '',
        nofollow: false,
      },

      image: {
        file: null,
        messages: [],
      },

      gallery: {
        files: null,
        messages: [],
        list: [],
        editMode: false,
        editID: null,
      },

      galleries: [],
    };
  },

  props: {
    value: {
      type: String,
      default: "",
    },
  },

  mounted() {
    this.editor = new Editor({
      content: this.value,
      extensions: [
        Gallery,
        Iframe,
        Tooltip,
        StarterKit,
        Typography,
        Link,
        Focus.configure({
          className: "has-focus",
          mode: "all",
        }),
        Image.configure({
          inline: true,
        }),
        ,
      ],
      onUpdate: ({ editor }) => {
        this.$emit("input", editor.getHTML());
      },

      onCreate: () => {
        const galleriesDOM = this.$refs.editor.querySelectorAll(".editor-gallery");

        galleriesDOM.forEach((galleryDOM) => {
          const imagesDOM = galleryDOM.querySelectorAll("img[src]");

          const images = [...imagesDOM].map((img) => ({
            url: img.src,
            name: img.alt,
          }));

          const gallery = { id: galleryDOM.id, list: images };
          this.galleries.push(gallery);
        });
      },
    });

    this.editor.commands.setContent(this.value);
  },

  beforeDestroy() {
    if (this.editor) {
      this.editor.destroy();
    }
  },

  methods: {
    openHtmlModal() {
      this.content = this.editor.getHTML();
      this.$modal.show('html-modal');
    },

    saveHtml() {
      this.editor.commands.setContent(this.content);
      this.$modal.hide('html-modal');
    },

    openLinkModal() {
      this.link.href = '';
      this.link.nofollow = false;

      if (this.editor.isActive("link")) {
        const attrs = this.editor.getAttributes('link');
        this.link.href = attrs.href;
        this.link.nofollow = attrs.rel.includes('nofollow');
      }

      this.$modal.show('link-modal');
    },

    setLink() {
      let rel = this.link.nofollow ? 'noopener noreferrer nofollow' : 'noopener noreferrer';

      this.editor
        .chain()
        .focus()
        .extendMarkRange("link")
        .unsetLink()
        .setLink({ href: this.link.href, rel })
        .run();

      this.$modal.hide('link-modal');
    },

    unsetLink() {
      this.editor.chain().focus().unsetLink().run();
      this.$modal.hide('link-modal');
    },

    setTooltip() {
      let dataTitle = '', dataText = '';

      if (this.editor.isActive("tooltip")) {
        const attrs = this.editor.getAttributes('tooltip');
        dataTitle = attrs['data-title'];
        dataText = attrs['data-text'];
      }

      const title = window.prompt("Введите название подсказки", dataTitle);
      if(!title) return

      const text = window.prompt("Введите текст подсказки", dataText);
      if(!text) return;

      this.editor
        .chain()
        .focus()
        .extendMarkRange("tooltip")
        .unsetTooltip()
        .setTooltip({ "data-title": title, "data-text": text })
        .run();
    },

    addIframe() {
      // Метод добавляет не только фреймы, но и любой html
      const url = window.prompt("URL");
      if (url) this.editor.commands.insertContent(url);
    },

    addInstagram() {
      const url = window.prompt("URL");
      if (url){
        if(url.slice(-1) == '/') {
          const newsUrl = url.substring(0, url.length - 1);
          const text = '<iframe src="' + newsUrl + '/embed" class="iframe-instagram" width="320" frameborder="0" scrolling="auto"></iframe>';
          this.editor.commands.insertContent(text);
        }else{
          const text = '<iframe src="' + url + '/embed" class="iframe-instagram" width="320" frameborder="0" scrolling="auto"></iframe>';
          this.editor.commands.insertContent(text);
        }
      }
    },

    onClickEditor(event) {
      if (!event.target.classList.contains("button")) return;

      const gallery = event.target.closest(".editor-gallery");
      const galleryID = gallery.id;

      if (event.target.classList.contains("button-edit")) {
        this.openGalleryModalEdit(galleryID);
      }

      if (event.target.classList.contains("button-delete")) {
        this.galleries = this.galleries.filter((g) => g.id != galleryID);
        gallery.remove();
      }
    },

    addImage(file) {
      if (!this.image.file) return;
      this.image.messages = [];

      this.uploadImage(this.image.file)
        .then((url) => {
          this.editor.chain().focus().setImage({ src: url }).run();
          this.$modal.hide("img-modal");
        })
        .catch((err) => {
          this.image.messages.push(err);
        })
        .finally(() => {
          this.image.file = null;
        });
    },

    addImageToGallery() {
      if (!this.gallery.files) return;
      this.gallery.messages = [];

      const promises = this.gallery.files.map((file) => this.uploadImage(file));

      Promise.all(promises)
        .then((urls) => {
          urls.forEach((url) => this.gallery.list.push({ url, name: "" }));
        })
        .catch((err) => {
          this.gallery.messages.push(err);
        })
        .finally(() => {
          this.$refs.galleryFileInput.blur();
          this.gallery.files = null;
        });
    },

    openImgModal() {
      this.$modal.show("img-modal");
    },

    openGalleryModalEdit(id) {
      if (!id) return;
      this.gallery.editMode = true;
      this.gallery.editID = id;
      this.gallery.list = this.galleries.find((g) => g.id == id).list.slice();
      this.$modal.show("gallery-modal");
    },

    openGalleryModalInsert() {
      this.gallery.editMode = false;
      this.gallery.editID = null;
      this.gallery.list = [];
      this.$modal.show("gallery-modal");
    },

    editGallery() {
      const id = this.gallery.editID;
      const galleryInEditor = this.$refs.editor.querySelector(`#${id} .content`);

      galleryInEditor.innerHTML = this.imagesHTML;

      this.galleries.find((g) => g.id == id).list = this.gallery.list;
      this.$modal.hide("gallery-modal");
    },

    insertGallery() {
      const id = `gallery-${this.galleries.length}`;
      const gallery = { id, list: this.gallery.list };

      this.galleries.push(gallery);
      this.$modal.hide("gallery-modal");

      const content = `<div class="gallery" id="${id}">${this.imagesHTML}</div>`;
      this.editor.commands.insertContent(content);
    },

    uploadImage(file) {
      return new Promise((resolve, reject) => {
        let image = new window.Image();
        image.src = window.URL.createObjectURL(file);

        image.onload = () => {
          let width = image.width;
          let height = image.height;

          if (width < 200 || height < 200) {
            reject("Размеры изображения должны быть не менее 200x200");
          }
          if(width > 1300){
            let ratioPhoto;
            if(width > height){
              ratioPhoto = 1300 / width
            }else{
              ratioPhoto = 900 / height
            }
            width  = ratioPhoto * width;
            height = ratioPhoto * height;
          }

          let formData = new FormData();
          formData.append("file", file);
          formData.append("destination_width", width);
          formData.append("destination_height", height);
          formData.append("title", "example");
          this.$axios
            .$post("authorized/admin/files/upload/image/news/raw", formData, {
              headers: {
                "Content-Type": `multipart/form-data;`,
              },
            })
            .then((res) => {
              let url = "https://api.amic.ru/" + res.data.relative_path;
              resolve(url);
            })
            .catch((err) => {
              reject(err.errors.join("* "));
            });
        };
      });
    },
  },

  computed: {
    imagesHTML() {
      return this.gallery.list
        .map((img) => `<img src="${img.url}" alt="${img.name}"></img>`)
        .join("");
    },
  },
};
</script>

<style lang="less">
.flip-list-move {
  transition: transform 0.2s;
}

//Gallery in editor
.editor-gallery {
  .content {
    display: flex;
    background: #f3f1f1;
    padding: 10px;
    flex-wrap: wrap;
    border-radius: 20px;
    pointer-events: none;

    img {
      width: 10%;
      height: auto;
      margin: 0 10px;
      object-fit: contain;
    }

    img.ProseMirror-separator {
      display: none !important;
    }
  }

  .button {
    margin: 5px;
  }
}

//Gallery in modal
.gallery-container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  max-height: 600px;
  overflow: auto;
  img {
    max-width: 25%;
    margin: 0 5px;
  }
}

.gallery-item {
  cursor: move;
  position: relative;
  &_delete {
    cursor: pointer;
    position: absolute;
    top: 0;
    right: 5px;
  }
}

//Tooltip in editor
span.tooltip {
  background: #f3f1f1;
  border-bottom: 1px dashed black;
  html.dark-mode & {
    background: rgba(243, 241, 241, 0.2);
    border-bottom: 1px dashed white;
  }
}

.ProseMirror {
  min-height: 800px;
  max-height: 800px;
  overflow-y: scroll;
  outline: none;
  padding: 0px 15px;

  &::-webkit-scrollbar {
    width: 10px;

    &-track {
      background-color: rgba(0, 0, 0, 0.1);
    }

    &-thumb {
      background-color: black;
      border-radius: 15px;
    }
  }
}

.editor {
  margin: 10px auto 0;
  border: 1px solid rgba(0, 0, 0, 0.5);
  border-radius: 15px;
  min-height: 600px;
  overflow: hidden;

  p,
  h1,
  h2,
  h3,
  h4 {
    font: "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
      Helvetica, Ubuntu, Arial, sans-serif;
  }
  h1 {
    font: 36px/43px "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI",
      Roboto, Helvetica, Ubuntu, Arial, sans-serif;
    font-weight: 700;
  }

  p {
    margin-bottom: 20px;
    font: 18px/29px "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI",
      Roboto, Helvetica, Ubuntu, Arial, sans-serif;
    padding: 3px;
  }

  blockquote p {
    font-size: 28px;
    line-height: 140%;
  }
}
.menu-editor {
  width: 100%;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  margin: 0 auto;
  margin-top: 40px;
  button {
    margin-left: 10px;
    margin-bottom: 15px;
  }
}
.has-focus {
  outline: 1px solid #bdb4b4;
  border-radius: 2px;
}
.v-btn:not(.v-btn--round).v-size--small {
  min-width: 15px;
  padding: 5px 5px;
  &:hover {
    background: black;
    color: white;
  }
  margin-bottom: 0;
}
.no-wrap {
  flex-wrap: nowrap !important;
}
.editor {
  img {
    max-width: 100% !important;
  }
  a {
    pointer-events: none;
  }
}
.vm--modal {
  padding: 15px;
}
</style>
