import { Node, ReactNodeViewRenderer } from '@tiptap/react';
import { Editor } from '@tiptap/core';
import { Plugin } from '@tiptap/pm/state';
import { EditorView } from '@tiptap/pm/view';
import { Dropzone } from '@features/block-editor/extensions/MediaUpload/view/Dropzone';

declare module '@tiptap/core' {
  interface Commands<ReturnType> {
    mediaSelect: {
      uploadMediaImage: (file?: File[]) => ReturnType;
      uploadMediaFile: (file?: File[]) => ReturnType;
      uploadMedia: (file?: File[]) => ReturnType;
    };
  }
}

// 画像のコピーペースト対応
const imagePastePlugin = (editor: Editor) =>
  new Plugin({
    props: {
      handlePaste(view: EditorView, event: ClipboardEvent): boolean {
        const files = event.clipboardData?.files;
        // 通常の image file の貼り付け
        if (files && files.length > 0) {
          const images = Array.from(files).filter((file) => file.type.startsWith('image/'));
          if (images.length === 0) return false;

          event.preventDefault(); // 通常の貼り付け処理を止める
          editor.commands.uploadMediaImage(images);
          return true;
        }
        return false;
      },
    },
  });

// エディタ本文への画像・ファイルのドラッグ&ドロップ対応（ブロックを選択していなくても画像/ファイルブロックを生成する）
const mediaDropPlugin = (editor: Editor) =>
  new Plugin({
    props: {
      handleDrop(view: EditorView, event: DragEvent): boolean {
        const files = event.dataTransfer?.files;
        if (!files || files.length === 0) {
          return false;
        }

        const coordinates = view.posAtCoords({ left: event.clientX, top: event.clientY });
        if (!coordinates) {
          return false;
        }

        // 既存の MediaUpload プレースホルダ上へのドロップは、その NodeView (Dropzone) 側の処理に委ねる
        const $pos = view.state.doc.resolve(coordinates.pos);
        for (let { depth } = $pos; depth > 0; depth -= 1) {
          if ($pos.node(depth).type.name === 'MediaUpload') {
            return false;
          }
        }

        // 画像・ファイルを区別せず 1 つのプレースホルダに渡す。挿入時に各メディアの media_type で振り分けられる
        event.preventDefault(); // 通常のドロップ処理を止める
        editor.chain().focus().setTextSelection(coordinates.pos).uploadMedia(Array.from(files)).run();
        return true;
      },
    },
  });

export const MediaUpload = Node.create({
  name: 'MediaUpload',

  isolating: true,

  defining: true,

  group: 'block',

  draggable: false,

  selectable: true,

  inline: false,

  addAttributes() {
    return {
      __pastedFiles: {
        default: [],
        parseHTML: () => [],
        renderHTML: () => null,
      },
      __mediaType: {
        default: 'image', // 'image' or 'file'
        parseHTML: (element) => element.getAttribute('data-media-type') || 'image',
        renderHTML: (attrs) => ({ 'data-media-type': attrs.__mediaType }),
      },
    };
  },

  parseHTML() {
    return [
      {
        tag: `div[data-type="${this.name}"]`,
        getAttrs: () => ({}),
      },
    ];
  },

  renderHTML({ HTMLAttributes }) {
    return ['div', { 'data-type': this.name, ...HTMLAttributes }];
  },

  addCommands() {
    return {
      uploadMediaImage:
        (files?: File[]) =>
        ({ commands }) =>
          commands.insertContent({
            type: this.name,
            attrs: {
              __pastedFiles: files ?? [],
              __mediaType: 'image',
            },
          }),

      uploadMediaFile:
        (files?: File[]) =>
        ({ commands }) =>
          commands.insertContent({
            type: this.name,
            attrs: {
              __pastedFiles: files ?? [],
              __mediaType: 'file',
            },
          }),

      uploadMedia:
        (files?: File[]) =>
        ({ commands }) =>
          commands.insertContent({
            type: this.name,
            attrs: {
              __pastedFiles: files ?? [],
              __mediaType: 'all',
            },
          }),
    };
  },

  addNodeView() {
    return ReactNodeViewRenderer(Dropzone);
  },

  addProseMirrorPlugins() {
    return [imagePastePlugin(this.editor), mediaDropPlugin(this.editor)];
  },
});

export default MediaUpload;
