<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed, useSlots } from 'vue'
import Quill from 'quill'
import ImageResize from 'quill-image-resize'
import MagicUrl from 'quill-magic-url'
import 'quill/dist/quill.snow.css'

const slots = useSlots()

const props = defineProps<{ 
  df: any; 
  record?: Record<string, any>; 
  canEdit?: boolean; 
  ctx?: any; 
  read_only?: boolean; 
  modelValue?: any 
}>()

const emit = defineEmits(['update:modelValue'])

const host = ref<HTMLDivElement | null>(null)
let quill: Quill | null = null
let syncTimer: any = null

// Label布局：上下结构(vertical) 或 左右结构(horizontal)
const labelLayout = computed(() => props.df.label_layout || 'vertical')

function registerQuillExtensions() {
  Quill.register('modules/imageResize', ImageResize)
  Quill.register('modules/magicUrl', MagicUrl)

  try {
    const CodeBlockContainer: any = (Quill as any).import('formats/code-block-container')
    CodeBlockContainer.tagName = 'PRE'
    ;(Quill as any).register(CodeBlockContainer, true)
  } catch {}

  try {
    const BackgroundStyle: any = (Quill as any).import('attributors/style/background')
    const ColorStyle: any = (Quill as any).import('attributors/style/color')
    const FontStyle: any = (Quill as any).import('attributors/style/font')
    const AlignStyle: any = (Quill as any).import('attributors/style/align')
    const DirectionStyle: any = (Quill as any).import('attributors/style/direction')
    ;(Quill as any).register(BackgroundStyle, true)
    ;(Quill as any).register(ColorStyle, true)
    ;(Quill as any).register(FontStyle, true)
    ;(Quill as any).register(AlignStyle, true)
    ;(Quill as any).register(DirectionStyle, true)
  } catch {}

  try {
    const DirectionClass: any = (Quill as any).import('attributors/class/direction')
    ;(Quill as any).register(DirectionClass, true)
  } catch {}

  try {
    const Uploader: any = (Quill as any).import('modules/uploader')
    if (Uploader?.DEFAULTS?.mimetypes && !Uploader.DEFAULTS.mimetypes.includes('image/gif')) {
      Uploader.DEFAULTS.mimetypes.push('image/gif')
    }
  } catch {}

  try {
    const Table: any = (Quill as any).import('formats/table-container')
    const superCreate = Table.create.bind(Table)
    Table.create = (value: any) => {
      const node = superCreate(value)
      node.classList.add('table')
      node.classList.add('table-bordered')
      return node
    }
    ;(Quill as any).register(Table, true)
  } catch {}

  try {
    const BaseLink: any = (Quill as any).import('formats/link')
    class InternalLink extends BaseLink {
      static create(value: string) {
        const node = super.create(value)
        const href = (this as any).sanitize(value)
        node.setAttribute('href', href)
        try {
          if (href?.startsWith('/') || href?.includes(window.location.host)) node.removeAttribute('target')
        } catch {}
        return node
      }
    }
    ;(Quill as any).register(InternalLink, true)
  } catch {}

  try {
    const Inline: any = (Quill as any).import('blots/inline')
    class CustomColor extends Inline {
      constructor(domNode: HTMLElement, value: any) {
        // @ts-ignore
        super(domNode, value)
        const closeFont = new RegExp('<\\/font>', 'g')
        domNode.outerHTML = domNode.outerHTML
          .replace(/<font/g, '<span')
          .replace(closeFont, '</span>')
      }
    }
    ;(CustomColor as any).blotName = 'customColor'
    ;(CustomColor as any).tagName = 'font'
    ;(Quill as any).register(CustomColor, true)
  } catch {}

  try {
    const Size: any = (Quill as any).import('attributors/style/size')
    Size.whitelist = [false,'8px','9px','10px','11px','12px','13px','14px','15px','16px','18px','20px','22px','24px','32px','36px','40px','48px','54px','64px','96px','128px']
    ;(Quill as any).register(Size, true)
  } catch {}
}

function init() {
  if (!host.value || quill) return
  registerQuillExtensions()
  const toolbar = [
    [{ header: [1, 2, 3, 4, 5, 6, false] }],
    [{ size: [false,'8px','9px','10px','11px','12px','13px','14px','15px','16px','18px','20px','22px','24px','32px','36px','40px','48px','54px','64px','96px','128px'] }],
    ['bold','italic','underline','strike','clean'],
    [{ color: [] }, { background: [] }],
    ['blockquote','code-block'],
    [{ direction: 'rtl' }],
    ['link','image'],
    [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
    [{ align: [] }],
    [{ indent: '-1' }, { indent: '+1' }],
    [
      {
        table: [
          'insert-table',
          'insert-row-above',
          'insert-row-below',
          'insert-column-right',
          'insert-column-left',
          'delete-row',
          'delete-column',
          'delete-table',
        ]
      }
    ]
  ]
  
  const readOnly = props.read_only || props.df?.read_only || !props.canEdit
  
  quill = new Quill(host.value, {
    theme: 'snow',
    readOnly,
    placeholder: props.df?.placeholder || '',
    modules: { toolbar, imageResize: {}, magicUrl: true, table: true }
  })

  // FormBuilder 模式：使用 modelValue
  if (slots.label) {
    setHtml(String(props.modelValue ?? ''))
    quill.on('text-change', (_d: any, _o: any, source: string) => {
      if (source === 'api') return
      if (syncTimer) clearTimeout(syncTimer)
      syncTimer = setTimeout(() => {
        emit('update:modelValue', getHtml())
      }, 200)
    })
  } else {
    // default_single.vue 模式：使用 record
    setHtml(String((props.record as any)?.[props.df.fieldname] ?? ''))
    quill.on('text-change', (_d: any, _o: any, source: string) => {
      if (source === 'api') return
      if (syncTimer) clearTimeout(syncTimer)
      syncTimer = setTimeout(() => {
        if (props.record) {
          (props.record as any)[props.df.fieldname] = getHtml()
        }
      }, 200)
    })
  }

  try {
    const toolbarModule: any = (quill as any).getModule('toolbar')
    toolbarModule?.addHandler('table', (action: string) => {
      const table = (quill as any).getModule('table')
      if (!table) return
      if (action === 'insert-table') table.insertTable(2, 2)
      else if (action === 'insert-row-above') table.insertRowAbove()
      else if (action === 'insert-row-below') table.insertRowBelow()
      else if (action === 'insert-column-left') table.insertColumnLeft()
      else if (action === 'insert-column-right') table.insertColumnRight()
      else if (action === 'delete-row') table.deleteRow()
      else if (action === 'delete-column') table.deleteColumn()
      else if (action === 'delete-table') table.deleteTable()
      if (action !== 'delete-row' && table?.balanceTables) table.balanceTables()
    })
  } catch {}

  try {
    setTimeout(() => {
      const root = host.value as HTMLElement
      const tableLabel = root?.querySelector?.('.ql-table .ql-picker-label') as HTMLElement | null
      if (tableLabel) tableLabel.setAttribute('data-label', String(props.df?.tableLabel || 'Table'))
    }, 0)
  } catch {}
}

function setHtml(html: string) {
  if (!quill) return
  if (!html) { quill.setText(''); return }
  const delta = (quill as any).clipboard.convert({ html, text: '' })
  quill.setContents(delta)
}

function getHtml() {
  if (!quill) return ''
  return (quill.root as HTMLElement).innerHTML || ''
}

onMounted(() => init())
onBeforeUnmount(() => {
  if (syncTimer) clearTimeout(syncTimer)
  quill = null
})

watch(() => (props.record as any)?.[props.df.fieldname], (v) => {
  if (!quill) return
  const current = getHtml()
  if (String(v ?? '') !== current) setHtml(String(v ?? ''))
})

watch(() => props.canEdit, (v) => { (quill as any)?.enable(!!v) })
</script>

<template>
  <div :class="['field-wrapper', `layout-${labelLayout}`, { 'form-builder-control': slots.label }]">
    <!-- FormBuilder 模式: 使用 slot 渲染 label -->
    <div v-if="slots.label" class="field-controls">
      <slot name="label" />
      <slot name="actions" />
    </div>
    <!-- default_single.vue 模式: 自己渲染 label -->
    <label v-else-if="props.df && props.ctx" class="field-label">
      {{ ctx.t(df.label || df.fieldname) }}
      <span v-if="df.reqd" class="required">*</span>
    </label>
    <div class="text-editor">
      <div ref="host"></div>
    </div>
  </div>
</template>

<style scoped>
.field-wrapper :deep(.text-editor) {
  flex: 1;
}

.text-editor :deep(.ql-container) { min-height: 240px; }
.text-editor :deep(.ql-toolbar) { border-top-left-radius: 6px; border-top-right-radius: 6px; }
.text-editor :deep(.ql-container) { border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; }

.text-editor :deep(.ql-snow .ql-picker.ql-size .ql-picker-item::before),
.text-editor :deep(.ql-snow .ql-picker.ql-size .ql-picker-label::before) {
  content: attr(data-value);
}

.text-editor :deep(.ql-snow .ql-picker.ql-size .ql-picker-label:not([data-value])::before),
.text-editor :deep(.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=""]::before) {
  content: 'Font Size';
}

.text-editor :deep(.ql-snow .ql-picker.ql-size .ql-picker-options) {
  max-height: 240px;
  overflow-y: auto;
}

.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-label::before) { content: attr(data-label); }

.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-label) {
  padding: 0 24px 0 8px;
  white-space: nowrap;
}


.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-label:not([data-label])::before),
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-label[data-label=""]::before) {
  content: 'Table';
}

.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-options) {
  min-width: 200px;
  max-height: 260px;
  overflow-y: auto;
  z-index: 1000;
}

.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item) {
  padding: 6px 12px;
  line-height: 16px;
  white-space: nowrap;
}

.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="insert-table"]::before) { content: 'Insert table'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="insert-row-above"]::before) { content: 'Insert row above'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="insert-row-below"]::before) { content: 'Insert row below'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="insert-column-left"]::before) { content: 'Insert column left'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="insert-column-right"]::before) { content: 'Insert column right'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="delete-row"]::before) { content: 'Delete row'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="delete-column"]::before) { content: 'Delete column'; }
.text-editor :deep(.ql-snow .ql-picker.ql-table .ql-picker-item[data-value="delete-table"]::before) { content: 'Delete table'; }
</style>


