import { defaultValueCtx, Editor, editorViewOptionsCtx, rootCtx, Ctx, createCmdKey } from '@milkdown/core';
import { clipboard } from '@milkdown/plugin-clipboard';
import { cursor } from '@milkdown/plugin-cursor';
// import { block } from '@milkdown/plugin-block';
import { diagram } from '@milkdown/plugin-diagram';
import { history } from '@milkdown/plugin-history';
import { indent } from '@milkdown/plugin-indent';
import { emoji } from '@milkdown/plugin-emoji';
import { listener, listenerCtx } from '@milkdown/plugin-listener';
import { Config } from '@milkdown/plugin-menu';
import { prism } from '@milkdown/plugin-prism';
import { slash } from '@milkdown/plugin-slash';
import { tooltip } from '@milkdown/plugin-tooltip';
import { trailing } from '@milkdown/plugin-trailing';
import { upload } from '@milkdown/plugin-upload';
import { gfm } from '@milkdown/preset-gfm';
import { codeFence as cmCodeFence, commonmark, image, link } from '@milkdown/preset-commonmark';
import { nord, nordLight } from '@milkdown/theme-nord';

import { math } from '@milkdown/plugin-math';
import { menu, menuPlugin } from '@milkdown/plugin-menu';
import { redo, undo } from '@milkdown/prose/history';
import { setBlockType, wrapIn } from '@milkdown/prose/commands';
import { EditorState, TextSelection } from '@milkdown/prose/state';
import { MarkType } from '@milkdown/prose/model';
import { liftListItem, sinkListItem } from '@milkdown/prose/schema-list';

const hasMark = (state: EditorState, type: MarkType | undefined): boolean => {
  if (!type) return false;
  const { from, $from, to, empty } = state.selection;
  if (empty) {
      return !!type.isInSet(state.storedMarks || $from.marks());
  }
  return state.doc.rangeHasMark(from, to, type);
};

export const SelectParent = createCmdKey('SelectParent');

export const createEditor = (
    root: HTMLElement | null,
    defaultValue: string,
    readOnly: boolean | undefined,
    onChange?: (markdown: string) => void,
    onFocus?: () => void,
    onBlur?: () => void,
) => {

  const customMenu = menu.configure(menuPlugin, {
    config: [
      [
          {
              type: 'button',
              icon: 'undo',
              key: 'Undo',
              disabled: (view) => {
                  return !undo(view.state);
              },
          },
          {
              type: 'button',
              icon: 'redo',
              key: 'Redo',
              disabled: (view) => {
                  return !redo(view.state);
              },
          },
      ],
      [
          {
              type: 'select',
              text: '标题',
              options: [
                  { id: '1', text: '标题 1' },
                  { id: '2', text: '标题 2' },
                  { id: '3', text: '标题 3' },
                  { id: '4', text: '标题 4' },
                  { id: '5', text: '标题 5' },
                  { id: '0', text: '正文' },
              ],
              disabled: (view) => {
                  const { state } = view;
                  const heading = state.schema.nodes['heading'];
                  if (!heading) return true;
                  const setToHeading = (level: number) => setBlockType(heading, { level })(state);
                  return (
                      !(view.state.selection instanceof TextSelection) ||
                      !(setToHeading(1) || setToHeading(2) || setToHeading(3))
                  );
              },
              onSelect: (id) => (Number(id) ? ['TurnIntoHeading', Number(id)] : ['TurnIntoText', null]),
          },
      ],
      [
          {
              type: 'button',
              icon: 'bold',
              key: 'ToggleBold',
              active: (view) => hasMark(view.state, view.state.schema.marks['strong']),
              disabled: (view) => !view.state.schema.marks['strong'],
          },
          {
              type: 'button',
              icon: 'italic',
              key: 'ToggleItalic',
              active: (view) => hasMark(view.state, view.state.schema.marks['em']),
              disabled: (view) => !view.state.schema.marks['em'],
          },
          {
              type: 'button',
              icon: 'strikeThrough',
              key: 'ToggleStrikeThrough',
              active: (view) => hasMark(view.state, view.state.schema.marks['strike_through']),
              disabled: (view) => !view.state.schema.marks['strike_through'],
          },
      ],
      [
          {
              type: 'button',
              icon: 'bulletList',
              key: 'WrapInBulletList',
              disabled: (view) => {
                  const { state } = view;
                  const node = state.schema.nodes['bullet_list'];
                  if (!node) return true;
                  return !wrapIn(node)(state);
              },
          },
          {
              type: 'button',
              icon: 'orderedList',
              key: 'WrapInOrderedList',
              disabled: (view) => {
                  const { state } = view;
                  const node = state.schema.nodes['ordered_list'];
                  if (!node) return true;
                  return !wrapIn(node)(state);
              },
          },
          {
              type: 'button',
              icon: 'taskList',
              key: 'TurnIntoTaskList',
              disabled: (view) => {
                  const { state } = view;
                  const node = state.schema.nodes['task_list_item'];
                  if (!node) return true;
                  return !wrapIn(node)(state);
              },
          },
          {
              type: 'button',
              icon: 'liftList',
              key: 'LiftListItem',
              disabled: (view) => {
                  const { state } = view;
                  const node = state.schema.nodes['list_item'];
                  if (!node) return true;
                  return !liftListItem(node)(state);
              },
          },
          {
              type: 'button',
              icon: 'sinkList',
              key: 'SinkListItem',
              disabled: (view) => {
                  const { state } = view;
                  const node = state.schema.nodes['list_item'];
                  if (!node) return true;
                  return !sinkListItem(node)(state);
              },
          },
      ],
      [
          {
              type: 'button',
              icon: 'link',
              key: 'ToggleLink',
              active: (view) => hasMark(view.state, view.state.schema.marks['link']),
          },
          {
              type: 'button',
              icon: 'image',
              key: 'InsertImage',
          },
          {
              type: 'button',
              icon: 'table',
              key: 'InsertTable',
          },
          {
              type: 'button',
              icon: 'code',
              key: 'TurnIntoCodeFence',
          },
      ],
      [
          {
              type: 'button',
              icon: 'quote',
              key: 'WrapInBlockquote',
          },
          {
              type: 'button',
              icon: 'divider',
              key: 'InsertHr',
          },
          // {
          //     type: 'button',
          //     icon: 'select',
          //     key: SelectParent,
          // },
      ],
    ]
  })

    const editor = Editor.make()
        .config((ctx) => {
            ctx.set(rootCtx, root);
            ctx.set(defaultValueCtx, defaultValue);
            ctx.update(editorViewOptionsCtx, (prev) => ({ ...prev, editable: () => !readOnly }));
            ctx.get(listenerCtx).markdownUpdated((_, markdown) => {
                onChange?.(markdown);
            })
            .blur((ctx: Ctx) => {
              onBlur?.()
            })
            .focus((ctx: Ctx) => {
              onFocus?.()
            })
        })
        .use(gfm)
        // .use(commonmark)
        .use(clipboard)
        .use(history)
        .use(cursor)
        .use(prism)
        // .use(block)
        .use(math)
        .use(indent)
        .use(upload)
        .use(diagram)
        .use(tooltip)
        .use(emoji)
        // .use(slash)
        .use(nordLight)
        .use(trailing)
        .use(customMenu)
        .use(listener)

    return editor;
};
