<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import loader from '@monaco-editor/loader'
import { marked } from 'marked'

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

const labelLayout = props.df.label_layout || 'vertical'

const editorHostEdit = ref<HTMLDivElement | null>(null)
const editorHostSplit = ref<HTMLDivElement | null>(null)
const previewHostEdit = ref<HTMLDivElement | null>(null)
const previewHostSplit = ref<HTMLDivElement | null>(null)
const containerRef = ref<HTMLDivElement | null>(null)
let monaco: any = null
let editor: any = null

const expanded = ref(false)

const layoutMode = ref<'split' | 'edit' | 'preview'>('split')

const splitRatio = ref(50)
const isDragging = ref(false)

let mermaidCounter = 0
let mermaidInstance: any = null

async function getMermaid() {
  if (!mermaidInstance) {
    const mod = await import('mermaid')
    mermaidInstance = mod.default
    mermaidInstance.initialize({ startOnLoad: false, theme: 'default' })
  }
  return mermaidInstance
}

async function renderMarkdown() {
  const target = layoutMode.value === 'preview' || layoutMode.value === 'edit'
    ? previewHostEdit.value
    : previewHostSplit.value
  if (!target) return

  let html: string
  try {
    html = marked.parse(String(props.record[props.df.fieldname] || '')) as string
  } catch {
    html = String(props.record[props.df.fieldname] || '')
  }

  const mm = await getMermaid()
  mermaidCounter = 0

  const offscreen = document.createElement('div')
  offscreen.innerHTML = html

  const codeBlocks = offscreen.querySelectorAll('pre code.language-mermaid')
  for (const block of Array.from(codeBlocks)) {
    const pre = block.parentElement
    if (!pre) continue
    const code = block.textContent || ''
    const id = `md-mermaid-${++mermaidCounter}`
    try {
      const { svg } = await mm.render(id, code)
      pre.outerHTML = `<div class="mermaid-diagram">${svg}</div>`
    } catch (err) {
      console.error('[MarkdownEditor] Mermaid render error:', err)
    }
  }

  const paragraphs = offscreen.querySelectorAll('p')
  for (const p of Array.from(paragraphs)) {
    const text = p.textContent || ''
    const trimmed = text.trim()
    if (/^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|requirementDiagram|gitGraph|journey)\s/i.test(trimmed)) {
      const id = `md-mermaid-${++mermaidCounter}`
      try {
        const { svg } = await mm.render(id, trimmed)
        p.outerHTML = `<div class="mermaid-diagram">${svg}</div>`
      } catch (err) {
        console.error('[MarkdownEditor] Mermaid render error:', err)
      }
    }
  }

  target.innerHTML = offscreen.innerHTML
}

async function mountEditor(target: HTMLDivElement | null) {
  if (!target) return
  if (!monaco) monaco = await loader.init()
  disposeEditor()
  editor = monaco.editor.create(target, {
    value: String(props.record[props.df.fieldname] || ''),
    language: 'markdown',
    automaticLayout: true,
    folding: true,
    minimap: { enabled: false },
    readOnly: !props.canEdit,
    wordWrap: 'on',
    wordWrapColumn: 120,
    wrappingStrategy: 'advanced',
    scrollBeyondLastLine: false,
    scrollBeyondLastColumn: 0,
    scrollbar: { horizontal: 'hidden', vertical: 'auto', horizontalScrollbarSize: 6, verticalScrollbarSize: 6, alwaysConsumeMouseWheel: false },
  })
  const refreshBars = () => {
    if (!editor || !target) return
    const info = editor.getLayoutInfo()
    const hasV = editor.getScrollHeight() > info.height + 1
    const hasH = editor.getScrollWidth() > info.width + 1
    target.classList.toggle('no-scroll-y', !hasV)
    target.classList.toggle('no-scroll-x', !hasH)
  }
  refreshBars()
  editor.onDidContentSizeChange(() => refreshBars())
  editor.onDidScrollChange(() => refreshBars())
  editor.onDidLayoutChange(() => refreshBars())
  editor.onDidChangeModelContent(() => {
    if (!editor) return
    props.record[props.df.fieldname] = editor.getValue()
    refreshBars()
  })
}

function disposeEditor() {
  try { editor?.dispose() } catch {}
  editor = null
}

function toggleExpand() {
  expanded.value = !expanded.value
  nextTick(() => editor?.layout())
}

function onDividerMouseDown(e: MouseEvent) {
  e.preventDefault()
  isDragging.value = true
  document.addEventListener('mousemove', onDividerMouseMove)
  document.addEventListener('mouseup', onDividerMouseUp)
}

function onDividerMouseMove(e: MouseEvent) {
  if (!isDragging.value || !containerRef.value) return
  const rect = containerRef.value.getBoundingClientRect()
  const minRatio = 20
  const maxRatio = 80
  let ratio = ((e.clientX - rect.left) / rect.width) * 100
  ratio = Math.max(minRatio, Math.min(maxRatio, ratio))
  splitRatio.value = ratio
  nextTick(() => editor?.layout())
}

function onDividerMouseUp() {
  isDragging.value = false
  document.removeEventListener('mousemove', onDividerMouseMove)
  document.removeEventListener('mouseup', onDividerMouseUp)
}

function onDividerTouchStart(e: TouchEvent) {
  e.preventDefault()
  isDragging.value = true
  document.addEventListener('touchmove', onDividerTouchMove, { passive: false })
  document.addEventListener('touchend', onDividerTouchEnd)
}

function onDividerTouchMove(e: TouchEvent) {
  if (!isDragging.value || !containerRef.value || !e.touches[0]) return
  e.preventDefault()
  const rect = containerRef.value.getBoundingClientRect()
  const minRatio = 20
  const maxRatio = 80
  let ratio = ((e.touches[0].clientX - rect.left) / rect.width) * 100
  ratio = Math.max(minRatio, Math.min(maxRatio, ratio))
  splitRatio.value = ratio
  nextTick(() => editor?.layout())
}

function onDividerTouchEnd() {
  isDragging.value = false
  document.removeEventListener('touchmove', onDividerTouchMove)
  document.removeEventListener('touchend', onDividerTouchEnd)
}

watch(() => props.canEdit, (v) => {
  editor?.updateOptions({ readOnly: !v })
})

watch(() => props.record[props.df.fieldname], async (v) => {
  if (editor && v !== editor.getValue()) editor.setValue(String(v || ''))
  await nextTick()
  await renderMarkdown()
}, { flush: 'post' })

onMounted(async () => {
  await nextTick()
  await mountEditor(editorHostSplit.value)
  await nextTick()
  await renderMarkdown()
})

watch(layoutMode, async (m) => {
  if (m === 'edit') {
    await nextTick()
    await mountEditor(editorHostEdit.value)
  } else if (m === 'split') {
    await nextTick()
    await mountEditor(editorHostSplit.value)
    await nextTick()
    await renderMarkdown()
  } else {
    await nextTick()
    await renderMarkdown()
  }
})

onBeforeUnmount(() => {
  document.removeEventListener('mousemove', onDividerMouseMove)
  document.removeEventListener('mouseup', onDividerMouseUp)
  document.removeEventListener('touchmove', onDividerTouchMove)
  document.removeEventListener('touchend', onDividerTouchEnd)
  disposeEditor()
})
</script>

<template>
  <div :class="['field-wrapper', `layout-${labelLayout}`]">
    <label class="field-label">
      {{ ctx?.t ? ctx.t(df.label || df.fieldname) : (df.label || df.fieldname) }}
      <span v-if="df.reqd" class="required">*</span>
    </label>
    <div class="md-editor">
      <div class="md-editor__toolbar">
        <n-button
          v-if="layoutMode === 'edit'"
          size="tiny"
          quaternary
          type="default"
          @click="layoutMode = 'split'"
        >{{ props.ctx?.t ? props.ctx.t('Split') : 'Split' }}</n-button>
        <n-button
          v-else-if="layoutMode === 'split'"
          size="tiny"
          quaternary
          type="default"
          @click="layoutMode = 'edit'"
        >{{ props.ctx?.t ? props.ctx.t('Edit') : 'Edit' }}</n-button>
        <n-button
          v-else
          size="tiny"
          quaternary
          type="default"
          @click="layoutMode = 'split'"
        >{{ props.ctx?.t ? props.ctx.t('Split') : 'Split' }}</n-button>

        <n-button
          v-if="layoutMode === 'preview'"
          size="tiny"
          quaternary
          type="default"
          @click="layoutMode = 'split'"
        >{{ props.ctx?.t ? props.ctx.t('Edit') : 'Edit' }}</n-button>
        <n-button
          v-if="layoutMode === 'edit' || layoutMode === 'split'"
          size="tiny"
          quaternary
          type="default"
          @click="layoutMode = 'preview'"
        >{{ props.ctx?.t ? props.ctx.t('Preview') : 'Preview' }}</n-button>

        <n-button
          v-if="layoutMode === 'edit' || layoutMode === 'split'"
          size="tiny"
          quaternary
          type="default"
          @click="toggleExpand"
        >{{ expanded ? (props.ctx?.t ? props.ctx.t('Collapse') : 'Collapse') : (props.ctx?.t ? props.ctx.t('Expand') : 'Expand') }}</n-button>
      </div>

      <!-- single-panel: edit only -->
      <div v-if="layoutMode === 'edit'" class="md-editor__single">
        <div ref="editorHostEdit" class="md-editor__monaco" :class="{ collapsed: !expanded }"></div>
      </div>

      <!-- single-panel: preview only -->
      <div v-else-if="layoutMode === 'preview'" class="md-editor__single">
        <div ref="previewHostEdit" class="md-editor__preview"></div>
      </div>

      <!-- split-pane: editor + preview side by side -->
      <div v-else ref="containerRef" class="md-editor__split">
        <div class="md-editor__split-left" :style="{ width: splitRatio + '%' }">
          <div ref="editorHostSplit" class="md-editor__monaco in-split"></div>
        </div>
        <div
          class="md-editor__divider"
          :class="{ dragging: isDragging }"
          @mousedown="onDividerMouseDown"
          @touchstart="onDividerTouchStart"
        >
          <div class="md-editor__divider-handle"><span></span><span></span><span></span></div>
        </div>
        <div class="md-editor__split-right" :style="{ width: (100 - splitRatio) + '%' }">
          <div ref="previewHostSplit" class="md-editor__preview"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.field-wrapper { width: 100%; }

.md-editor {
  width: 100%;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 6px;
  min-height: 400px;
}

.md-editor__toolbar {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.md-editor__single {
  height: 100%;
  min-height: 400px;
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
  overflow: hidden;
}

.md-editor__split {
  display: flex;
  height: 100%;
  min-height: 500px;
  overflow: hidden;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  background: #fff;
}

.md-editor__split-left {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.md-editor__split-right {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.md-editor__divider {
  width: 8px;
  cursor: col-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f1f5f9;
  border-left: 1px solid #e2e8f0;
  border-right: 1px solid #e2e8f0;
  transition: background 0.15s;
  user-select: none;
  flex-shrink: 0;
}

.md-editor__divider:hover,
.md-editor__divider.dragging {
  background: #e2e8f0;
}

.md-editor__divider-handle {
  display: flex;
  flex-direction: column;
  gap: 3px;
  pointer-events: none;
}

.md-editor__divider-handle span {
  display: block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #94a3b8;
}

.md-editor__divider:hover .md-editor__divider-handle span,
.md-editor__divider.dragging .md-editor__divider-handle span {
  background: #475569;
}

.md-editor__monaco {
  flex: 1;
  width: 100%;
  min-height: 0;
  overflow: hidden;
}

.md-editor__single .md-editor__monaco {
  flex: 1;
  min-height: 400px;
}

.md-editor__monaco.collapsed { height: 500px; }
.md-editor__monaco:not(.collapsed) { height: 800px; }

:deep(.md-editor__split-left .md-editor__monaco) {
  min-height: unset;
  height: 100%;
  max-height: unset;
  border: none;
  border-radius: 0;
}

.md-editor__preview {
  flex: 1;
  height: 100%;
  background: transparent;
  padding: 12px;
  overflow-y: auto;
  overflow-x: hidden;
}

:deep(.monaco-editor .minimap) { display: none; }
:deep(.monaco-scrollable-element > .scrollbar) { background: transparent !important; opacity: 0.6; }
:deep(.monaco-scrollable-element > .scrollbar:hover) { opacity: 0.9; }
:deep(.monaco-scrollable-element > .scrollbar.vertical) { width: 6px !important; }
:deep(.monaco-scrollable-element > .scrollbar.vertical .slider) { width: 6px !important; background: #cbd5e1 !important; border-radius: 4px !important; }
:deep(.monaco-scrollable-element > .scrollbar.horizontal) { height: 6px !important; }
:deep(.monaco-scrollable-element > .scrollbar.horizontal .slider) { height: 6px !important; background: #cbd5e1 !important; border-radius: 4px !important; }

.no-scroll-y :deep(.monaco-scrollable-element > .scrollbar.vertical) { display: none !important; }
.no-scroll-x :deep(.monaco-scrollable-element > .scrollbar.horizontal) { display: none !important; }

.md-editor__preview :deep(.mermaid-diagram) {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 16px;
  margin: 12px 0;
  overflow-x: auto;
}
.md-editor__preview :deep(.mermaid-diagram svg) {
  max-width: 100%;
  height: auto;
}

@media (max-width: 768px) {
  .md-editor__split {
    flex-direction: column;
  }
  .md-editor__split-left,
  .md-editor__split-right {
    width: 100% !important;
    height: 300px;
  }
  .md-editor__divider {
    width: 100%;
    height: 8px;
    cursor: row-resize;
    border-left: none;
    border-right: none;
    border-top: 1px solid #e2e8f0;
    border-bottom: 1px solid #e2e8f0;
  }
  .md-editor__divider-handle {
    flex-direction: row;
  }
  .md-editor__divider-handle span {
    width: 4px;
    height: 4px;
  }
  .md-editor__monaco.collapsed { max-height: 300px; }
  .md-editor__monaco:not(.collapsed) { max-height: 500px; }
}
</style>
