import type { IEditorConfig, MathliveModule, Range, ToolbarOptions } from '@opentiny/fluent-editor'
import type OSS from 'ali-oss'
import type { GetEditorConfigParams, MyToolbarOption, UpFile } from './types'
import FluentEditor, { generateToolbarTip } from '@opentiny/fluent-editor'
import katex from 'katex'
import QuillToolbarTip, { type QuillToolbarTipOptions } from 'quill-toolbar-tip'
import { ref } from 'vue'
import { getAliOssTokenAxios } from '@/service/api/upload'
import { browserPathJoin } from '@/utils/date'
import { base64ToFile } from '@/utils/file'
import { initOSSClient, uploadFileToOSS } from '@/utils/oss'
import { getSnowflake } from '@/utils/rest'

/** 自定义打开iframe的按钮的key */
export const MY_OPEN_PROCESSON_IFRAME = 'my-open-processon-iframe' as const
export const upFileList = ref<UpFile[]>([])

/**
 * 初始化编辑器
 */
export function initEditor(container: HTMLElement, params: IEditorConfig): FluentEditor {
  // 需要依赖 katex
  window.katex = katex as any

  // 处理图片给每个图片添加类名和最大宽度
  const ImageBlot: any = FluentEditor.import('formats/image')
  class CustomImageBlot extends ImageBlot {
    static create(value: any) {
      const node: HTMLImageElement = super.create(value)
      editImageAttribute(node, false)
      return node
    }
  }
  FluentEditor.register(CustomImageBlot, true)

  // 处理视频给每个视频添加类名和最大宽度
  const VideoBlot: any = FluentEditor.import('formats/video')
  class CustomVideoBlot extends VideoBlot {
    static create(value: any) {
      const node: HTMLVideoElement = super.create(value)
      editVideoAttribute(node, false)
      return node
    }
  }
  FluentEditor.register(CustomVideoBlot, true)

  // 注册工具栏提示模块
  FluentEditor.register({ 'modules/toolbar-tip': generateToolbarTip(QuillToolbarTip) }, true)
  const Parchment = FluentEditor.import('parchment')
  addToolbarIcon()

  // 居中问题
  const config = { scope: Parchment.Scope.BLOCK, whitelist: ['right', 'center', 'justify'] }
  const Align = new Parchment.StyleAttributor('align', 'text-align', config)
  FluentEditor.register(Align, true)

  // 工具栏配置
  return new FluentEditor(container, params)
}

/**
 * 得到编辑器的配置
 */
export async function getEditorConfig(params: GetEditorConfigParams): Promise<IEditorConfig> {
  const editorOptions: IEditorConfig = {
    theme: 'snow',
    modules: {
      // 是否开启数学公式模块
      'mathlive': true,
      // 开启字数统计
      // counter: {
      //   count: 2000,
      // },
      'toolbar': {
        // 工具栏显示那些按钮
        container: await getToolbarOption(params.toolbarOption || {}),
        handlers: {
          formula() {
            // 点击数学公式按钮时打开弹出框
            const mathlive = this.quill.getModule('mathlive') as MathliveModule
            mathlive.createDialog('')
          },
          // 自定义打开iframe的按钮的点击事件的回调
          [`${MY_OPEN_PROCESSON_IFRAME}`](_value: boolean) {
            params[MY_OPEN_PROCESSON_IFRAME]() // 执行回调
          },
        },
      },
      'uploader': {
        // only allow image
        mimetypes: ['*', 'video/*', 'audio/*'],
        async handler(_range: Range, files: File[]): Promise<(string | false)[]> {
          try {
            const paths = await Promise.all(files.map((file, index) => upFileAxios(file, `${Date.now()}_${index}`)))
            return paths
          }
          catch (error: any) {
            window.$message?.error(`上传失败: ${error.message}`)
            // eslint-disable-next-line no-console
            console.log('error====', error)
            return files.map(() => false)
          }
        },
      },
      'clipboard': {
        matchers: [
          [
            'img',
            (node: HTMLImageElement, delta: any) => {
              editImageAttribute(node, false)
              return delta
            },
          ],
          [
            'video',
            (node: HTMLVideoElement, delta: any) => {
              editVideoAttribute(node, false)
              return delta
            },
          ],
        ],
      },
      // 工具栏提示配置
      'toolbar-tip': {
        defaultTooltipOptions: {
          tipHoverable: false,
        },
        tipTextMap: {
          [MY_OPEN_PROCESSON_IFRAME]: '跳转第三方公式编辑器\n可在第三方编辑器编辑好公式后复制过来\n[注意]:需要复制到公式输入框内才能生效',
        },
      } satisfies Partial<QuillToolbarTipOptions>,
      // 国际化配置
      'i18n': { lang: 'zh-CN' },
    },
  }
  return editorOptions
}

/**
 * 得到工具栏配置(显示那些按钮)
 */
function getToolbarOption(params: MyToolbarOption): Promise<ToolbarOptions['container']> {
  const { show, showUpFile, showUpVideo, customToolbar } = params

  if (show === false) {
    return Promise.resolve([] satisfies ToolbarOptions['container'])
  }

  if (customToolbar !== undefined) {
    return Promise.resolve(customToolbar)
  }
  const va = ['image']
  showUpVideo !== false && va.push('video') // 只要不等于false就添加 (undefined也添加)
  showUpFile !== false && va.push('file') // 只要不等于false就添加 (undefined也添加)
  // 工具栏配置
  const TOOLBAR_CONFIG: ToolbarOptions['container'] = [
    ['undo', 'redo', 'clean', 'format-painter'],
    [
      { header: [1, 2, 3, 4, 5, 6, false] },
      { size: [false, '12px', '14px', '16px', '18px', '20px', '24px', '32px', '36px', '48px', '72px'] },
      { 'line-height': [false, '1.2', '1.5', '1.75', '2', '3', '4', '5'] },
      { font: [false, 'serif', 'sans-serif', 'monospace', 'cursive', 'fantasy', '宋体', '黑体', '微软雅黑', '楷体', '仿宋', '等线'] },
    ],
    ['bold', 'italic', 'strike', 'underline', 'divider'],
    [{ color: [] }, { background: [] }],
    [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }], // 这里可以为字符串
    [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
    [{ script: 'sub' }, { script: 'super' }],
    [{ indent: '-1' }, { indent: '+1' }],
    ['link', 'blockquote'],
    [...va],
    ['fullscreen', 'formula', MY_OPEN_PROCESSON_IFRAME],
  ]
  return Promise.resolve(TOOLBAR_CONFIG)
}

/**
 * 添加打开iframe的弹出框的自定义按钮
 */
function addToolbarIcon() {
  // 这里时增加按钮
  const myOpenProcessonIframeIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="currentColor" d="M6.5 1a1.5 1.5 0 0 0-1.415 1H4.5A1.5 1.5 0 0 0 3 3.5v10A1.5 1.5 0 0 0 4.5 15h1.651a1.5 1.5 0 0 1-.111-1H4.5a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h.585A1.5 1.5 0 0 0 6.5 4h3a1.5 1.5 0 0 0 1.415-1h.585a.5.5 0 0 1 .5.5v1.57q.346.079.681.247.178.09.319.217V3.5A1.5 1.5 0 0 0 11.5 2h-.585A1.5 1.5 0 0 0 9.5 1zM6 2.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5m3.451 5.277a1.916 1.916 0 0 1 2.778-1.568.5.5 0 0 1-.452.892.916.916 0 0 0-1.328.75l-.087 1.172H11.5a.5.5 0 0 1 0 1h-1.212l-.237 3.2a1.916 1.916 0 0 1-2.777 1.567.5.5 0 1 1 .452-.892.916.916 0 0 0 1.328-.75l.231-3.125H8.5a.5.5 0 1 1 0-1h.86zm5.403 4.077a.5.5 0 0 0-.708-.708l-1.057 1.057-.457-.734a.994.994 0 0 0-1.424-.281.5.5 0 0 0 .579.815l.576.926-1.217 1.217a.5.5 0 0 0 .708.708l1.052-1.053.457.735a.99.99 0 0 0 1.442.264.5.5 0 0 0-.599-.801l-.574-.924z"/></svg>`
  const icons = FluentEditor.import('ui/icons') as Record<string, string>
  icons[MY_OPEN_PROCESSON_IFRAME] = myOpenProcessonIframeIcon
}

/**
 * 上传文件
 */
async function upFileAxios(file: File, id: string): Promise<string | false> {
  const item: UpFile = { id, name: file.name, progress: 0 }
  try {
    upFileList.value.push(item)
    const { data: tokenData } = await getAliOssTokenAxios()
    if (!tokenData) {
      return false
    }
    // 初始化OSS客户端
    const client = initOSSClient(tokenData)
    const path = browserPathJoin(`temp/${Date.now()}`, file!.name)
    // 上传文件
    const res: OSS.MultipartUploadResult = await uploadFileToOSS(client, file, path, (progress) => {
      item.progress = Math.floor(progress * 10000) / 100
    })
    const url = browserPathJoin(import.meta.env.VITE_BASE_OSS_URL, res.name)
    upFileList.value.splice(upFileList.value.indexOf(item), 1)
    return Promise.resolve(url)
    // oxlint-disable-next-line no-unused-vars
  }
  catch (error: any) {
    upFileList.value.splice(upFileList.value.indexOf(item), 1)
    window.$message?.error(`上传失败: ${error.message}`)
    return Promise.resolve(false)
  }
}

/** 编辑视频属性 */
export function editVideoAttribute(node: HTMLVideoElement, isReplace = true) {
  node.setAttribute('style', 'max-width: 100%;') // 强制内联样式
  node.classList.add('my-ql-video')
  isReplace && replaceFileUrl(node)
}

/** 编辑图片属性 */
export function editImageAttribute(node: HTMLImageElement, isReplace = true) {
  node.setAttribute('style', 'max-width: 100%;') // 强制内联样式
  node.classList.add('my-ql-image')
  isReplace && replaceFileUrl(node)
}

/**
 *  base64替换为url地址
 * @param node
 */
export function replaceFileUrl(node: HTMLImageElement | HTMLVideoElement) {
  const ex = getExtensionFromDataUrl(node.src)
  if (ex) {
    const file = base64ToFile(node.src, `${Date.now()}_${getSnowflake()}.${ex}`)
    upFileAxios(file, file.name)
  }
}

const mimeToExtension: Record<string, string> = {
  'image/png': 'png',
  'image/jpeg': 'jpg',
  'image/jpg': 'jpg',
  'image/gif': 'gif',
  'image/webp': 'webp',
  'image/svg': 'webp',
  'video/mp4': 'mp4',
  'audio/mpeg': 'mp3',
  'application/pdf': 'pdf',
  'text/plain': 'txt',
}

/**
 * 从 base64 获取文件扩展名
 * @param dataUrl
 * @param mimeMap
 */
function getExtensionFromDataUrl(dataUrl: string, mimeMap = mimeToExtension): string | null {
  const base64Pattern = /^data:(image|video)\/([a-zA-Z]+);base64,([A-Za-z0-9+/]+={0,2})$/
  if (base64Pattern.test(dataUrl)) {
    const mimeType = dataUrl.split(',')?.[0]?.split(':')?.[1]?.split(';')[0]
    return mimeType && mimeType in mimeMap ? mimeMap[mimeType]! : null
  }
  else {
    return null
  }
}
