<script setup lang="ts">
import 'vditor/dist/index.css'
import { createPostBodyMarkdownEditorBridge, type CreateVditorLikeOptions } from './post-body-markdown-editor-vditor-bridge'
import { buildPostBodyMarkdownEditorVditorOptions } from './post-body-markdown-editor-vditor-config'
import { initializePostBodyMarkdownEditorVditor } from './post-body-markdown-editor-vditor-init'
import { handlePostBodyMarkdownEditorViewportSwitch } from './post-body-markdown-editor-vditor-viewport'

const props = defineProps<{
  modelValue: string
}>()

const emit = defineEmits<{
  'update:modelValue': [string]
}>()

const { fetchData } = useClientApi()
const toast = useToast()
const mountEl = ref<HTMLElement | null>(null)
const isMobileViewport = ref(false)
let viewportMql: MediaQueryList | null = null
let onViewportChange: ((event: MediaQueryListEvent) => void) | null = null

const bridge = createPostBodyMarkdownEditorBridge({
  getModelValue: () => props.modelValue,
  emitUpdate: (v) => emit('update:modelValue', v),
  createEditor: ({ element, value, onInput }: CreateVditorLikeOptions) => {
    const Vditor = vditorCtor.value
    if (!Vditor) {
      throw new Error('Vditor constructor is not ready')
    }
    return new Vditor(element, buildPostBodyMarkdownEditorVditorOptions({
      value,
      isMobile: isMobileViewport.value,
      onInput,
      uploadHandler: onUploadImg,
    }))
  },
})

const vditorCtor = shallowRef<null | (new (element: HTMLElement, options: Record<string, unknown>) => {
  getValue: () => string
  setValue: (value: string, render?: boolean) => void
  destroy: () => void
})>(null)
let unmounted = false

async function onUploadImg(files: File[]): Promise<string> {
  const form = new FormData()
  for (const file of files) {
    form.append('file', file)
  }
  try {
    const { files: uploaded } = await fetchData<{ files: { url: string }[] }>('/api/file/upload', {
      method: 'POST',
      body: form,
    })
    const succMap = Object.fromEntries(uploaded.map((item) => [item.url, item.url]))
    toast.add({ title: '图片已上传', color: 'success' })
    return JSON.stringify({
      msg: '',
      code: 0,
      data: {
        errFiles: [] as string[],
        succMap,
      },
    })
  }
  catch {
    toast.add({ title: '图片上传失败', color: 'warning' })
    return JSON.stringify({
      msg: 'upload failed',
      code: 1,
      data: {
        errFiles: [] as string[],
        succMap: {} as Record<string, string>,
      },
    })
  }
}

onMounted(async () => {
  if (!import.meta.client) {
    return
  }
  viewportMql = window.matchMedia('(max-width: 767px)')
  isMobileViewport.value = viewportMql.matches
  onViewportChange = (event: MediaQueryListEvent) => {
    const nextIsMobile = event.matches
    const changed = handlePostBodyMarkdownEditorViewportSwitch({
      currentIsMobile: isMobileViewport.value,
      nextIsMobile,
      hasMountedEditor: Boolean(mountEl.value && bridge.getEditor()),
      remountEditor: () => {
        if (!mountEl.value) {
          return
        }
        bridge.unmount()
        bridge.mount(mountEl.value)
      },
    })
    if (changed) {
      isMobileViewport.value = nextIsMobile
    }
  }
  viewportMql.addEventListener('change', onViewportChange)
  await initializePostBodyMarkdownEditorVditor({
    importVditor: () => import('vditor'),
    isUnmounted: () => unmounted,
    onReady: (ctor) => {
      vditorCtor.value = ctor
      if (mountEl.value) {
        bridge.mount(mountEl.value)
      }
    },
    onError: (error) => {
      console.error('Failed to initialize Vditor editor', error)
    },
  })
})

watch(() => props.modelValue, () => {
  bridge.syncFromProps()
})

onBeforeUnmount(() => {
  unmounted = true
  if (viewportMql && onViewportChange) {
    viewportMql.removeEventListener('change', onViewportChange)
  }
  onViewportChange = null
  viewportMql = null
  bridge.unmount()
})
</script>

<template>
  <ClientOnly>
    <div
      ref="mountEl"
      :style="{ height: 'min(72vh, 720px)' }"
      class="w-full rounded-lg overflow-hidden ring ring-default"
    />
    <template #fallback>
      <div class="text-muted text-sm py-12 text-center border border-default rounded-lg">
        编辑器加载中…
      </div>
    </template>
  </ClientOnly>
</template>
