import { type MaybeComputedElementRef, tryOnMounted, tryOnUnmounted, unrefElement, useElementSize } from '@vueuse/core';
import { createEditor, createToolbar } from '@wangeditor/editor';
import { getDefaultToolbarConfig } from './config';
import { getElement, getResources, isEditorInstance, register, upload } from './tools';
import type { EditorEmits, EditorImageElement, EditorInstance, EditorProps, EditorVideoElement, ToolbarInstance } from './types';

export type EmitsToProps<T extends Record<string, any>> = {
  [K in keyof T as `on${Capitalize<string & K>}`]?: T[K] extends [infer U] ? (event: U) => void : T[K] extends [...infer Args] ? (...args: Args) => void : never;
};

export function useEditor(target: MaybeComputedElementRef, props?: EditorProps, emits?: EmitsToProps<EditorEmits>) {
  register();

  const editor = shallowRef<EditorInstance>();
  const toolbar = shallowRef<ToolbarInstance>();

  const handle1 = useElementSize(target, { height: 0, width: 0 });
  const handle2 = watch(
    [editor, () => props?.defaultValue],
    ([editor, html]) => {
      if (!isEditorInstance(editor)) return;
      const value = toValue(html);
      if (value) editor.dangerouslyInsertHtml(value);
    },
    { immediate: true }
  );
  const handle3 = watch(
    [editor, () => props?.disable],
    ([editor, disable = false]) => {
      if (!isEditorInstance(editor)) return;
      if (disable) editor.disable();
      else editor.enable();
    },
    { immediate: true }
  );

  const stop = () => {
    handle1.stop();
    handle2.stop();
    handle3.stop();
  };

  const resources = new Set<string>();

  tryOnMounted(() => {
    const container = unrefElement(target);
    if (container) {
      const height = handle1.height.value;
      if (container && height > 0 && /* 全屏 */ height < window.innerHeight) container.style.setProperty('--height', `${height}px`);

      const querySelector = getElement.bind(null, container as HTMLElement);
      editor.value = createEditor({
        selector: querySelector('.content-container')!,
        html: toValue(props?.defaultValue ?? ''),
        config: {
          placeholder: toValue(props?.placeholder ?? '请输入内容...'),
          scroll: false,
          MENU_CONF: {
            insertImage: {
              onInsertedImage(node: EditorImageElement | null) {
                if (node?.src) resources.add(node.src);
              },
            },
            insertVideo: {
              onInsertedVideo(node: EditorVideoElement | null) {
                if (node?.src) resources.add(node.src);
              },
            },
            deleteVideo: {},
          },
          hoverbarKeys: {
            text: {
              menuKeys: ['headerSelect', '|', 'bold', 'italic', 'underline', 'through', 'sup', 'sub', 'color', 'bgColor', 'clearStyle'],
            },
            video: {
              menuKeys: ['enter', 'editVideoSize', 'deleteVideo'],
            },
          },
          onCreated(editor) {
            emits?.onCreated?.call(editor, editor);
          },
          onChange(editor) {
            const text = editor.getText();
            const html = text ? editor.getHtml() : '';
            emits?.onChange?.call(editor, editor, { html, text });
          },
          customAlert(message, type) {
            editor.value?.emit('message', { type, content: message });
          },
        },
      });
      toolbar.value = createToolbar({
        selector: querySelector('.toolbar-container')!,
        editor: editor.value,
        config: {
          toolbarKeys: toValue(props?.toolbar) ?? getDefaultToolbarConfig(),
          excludeKeys: [],
        },
      });

      if (props?.uploadPicture) Object.assign(editor.value.getMenuConfig('uploadImage'), upload.call(editor.value, props.uploadPicture));
      else toolbar.value?.getConfig().excludeKeys?.push('uploadImage');

      if (props?.uploadVideo) Object.assign(editor.value.getMenuConfig('uploadVideo'), upload.call(editor.value, props.uploadVideo));
      else toolbar.value?.getConfig().excludeKeys?.push('uploadVideo');

      if (!props?.previewable) toolbar.value?.getConfig().excludeKeys?.push('preview');
    }
  });
  tryOnUnmounted(() => {
    stop();
    editor.value?.destroy();
    toolbar.value = void 0;
    editor.value = void 0;
    resources.clear();
  });

  function save() {
    if (!editor.value) throw { message: `编辑器未初始化` };

    const text = editor.value.getText();
    if (!text?.trim().length) throw { message: `编辑器内容为空` };

    const lapsedResources: string[] = [];
    if (resources.size) {
      const value = getResources.call(editor.value);
      resources.forEach((r) => {
        if (!value.includes(r)) lapsedResources.push(r);
      });
    }
    const html = editor.value?.getHtml();
    return { text, html, lapsedResources };
  }

  return { editor, toolbar, save };
}
