<script setup>
import { computed, nextTick, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { NButton, NSelect, NSpace, NSpin, NTag, useMessage } from 'naive-ui'
import { saveAs } from 'file-saver'
import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import { renderAsync } from 'docx-preview'

import CommonPage from '@/components/page/CommonPage.vue'
import api from '@/api'

const route = useRoute()
const router = useRouter()
const message = useMessage()

const loading = ref(false)
const saving = ref(false)
const exporting = ref(false)
const editorRef = ref(null)
const htmlContent = ref('')
const detailData = ref(null)

const pageMeta = computed(() => ({
  valuationId: route.query.valuationId || route.query.id,
  templateId: route.query.templateId,
  title: route.query.title || '评估报告',
}))

const defaultHtml = `
  <div class="doc-header">
    <h1>非遗资产评估报告</h1>
    <p>本页为在线编辑预览，内容来自 Word 模板渲染结果。</p>
  </div>
  <div class="doc-block">
    <h2>一、资产概览</h2>
    <p>资产名称：<strong>${pageMeta.value.title}</strong></p>
    <p>生成时间：${new Date().toLocaleDateString()}</p>
  </div>
  <div class="doc-block">
    <h2>二、正文示例</h2>
    <p>在这里可以直接修改文字样式、对齐方式，也可以粘贴从 Word 模板转换的 HTML，实现 1:1 的样式复刻。</p>
    <p>正文段落采用 14px/1.8 行距，并限制版心宽度，便于接近 A4 排版。</p>
  </div>
`

const syncEditorContent = async () => {
  await nextTick()
  if (editorRef.value) {
    editorRef.value.innerHTML = htmlContent.value || defaultHtml
  }
}

const ensureDetailData = async () => {
  if (detailData.value || !pageMeta.value.valuationId) return
  try {
    const { data } = await api.getValuationById({ valuation_id: pageMeta.value.valuationId })
    detailData.value = data
  } catch (error) {
    console.error('获取详情数据失败', error)
  }
}

const renderDocxWithPreview = async () => {
  await ensureDetailData()
  try {
    const resp = await fetch('/report_template.docx')
    if (!resp.ok) throw new Error('模板文件读取失败')
    const arrayBuffer = await resp.arrayBuffer()
    const zip = new PizZip(arrayBuffer)
    const doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true,
      delimiters: { start: '${', end: '}' },
      nullGetter: (part) => (!part.module ? '${' + part.value + '}' : ''),
    })
    if (detailData.value) {
      doc.render(detailData.value)
    }

    const blob = doc.getZip().generate({
      type: 'blob',
      mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })

    if (editorRef.value) {
      editorRef.value.innerHTML = ''
      await renderAsync(blob, editorRef.value, null, {
        inWrapper: true,
        ignoreWidth: false,
        ignoreHeight: false,
        className: 'docx-preview',
      })
      // 允许直接编辑渲染结果
      editorRef.value.setAttribute('contenteditable', 'true')
      htmlContent.value = editorRef.value.innerHTML
    }
  } catch (error) {
    console.error('Word 模板渲染失败', error)
    message.error('Word 模板渲染失败，显示默认示例')
    await syncEditorContent()
  }
}

const renderFromBackendHtml = async () => {
  let rendered = false
  try {
    const res = await api.renderReportTemplate({
      valuation_id: pageMeta.value.valuationId,
      template_id: pageMeta.value.templateId,
    })
    const payload = res?.data ?? res
    const html = payload?.html ?? payload?.content ?? (typeof payload === 'string' ? payload : '')
    const css = payload?.css
    if (html && editorRef.value) {
      const combined = css ? `<style>${css}</style>${html}` : html
      editorRef.value.innerHTML = combined
      editorRef.value.setAttribute('contenteditable', 'true')
      htmlContent.value = combined
      rendered = true
    }
  } catch (error) {
    console.error('加载后端渲染模板失败', error)
  }

  if (!rendered) {
    await renderDocxWithPreview()
  }
}

const fetchTemplateHtml = async () => {
  loading.value = true
  try {
    await renderFromBackendHtml()
  } finally {
    loading.value = false
  }
}

const handleInput = () => {
  if (!editorRef.value) return
  htmlContent.value = editorRef.value.innerHTML
}

const focusEditor = () => {
  if (editorRef.value) {
    const target = editorRef.value.querySelector('[contenteditable]') || editorRef.value
    target.focus()
  }
}

const exec = (command, value) => {
  focusEditor()
  document.execCommand(command, false, value)
  handleInput()
}

const wrapSelection = (styles = {}) => {
  const sel = window.getSelection()
  if (!sel || !sel.rangeCount) return
  const range = sel.getRangeAt(0)
  if (range.collapsed) return
  const span = document.createElement('span')
  Object.entries(styles).forEach(([k, v]) => {
    span.style[k] = v
  })
  span.appendChild(range.extractContents())
  range.insertNode(span)
  sel.removeAllRanges()
  const newRange = document.createRange()
  newRange.selectNodeContents(span)
  sel.addRange(newRange)
  handleInput()
}

const fontSizeOptions = [
  { label: '12px', value: 12 },
  { label: '14px', value: 14 },
  { label: '16px', value: 16 },
  { label: '18px', value: 18 },
  { label: '20px', value: 20 },
  { label: '24px', value: 24 },
]

const lineHeightOptions = [
  { label: '1.2', value: '1.2' },
  { label: '1.5', value: '1.5' },
  { label: '1.8', value: '1.8' },
  { label: '2.0', value: '2' },
]

const applyFontSize = (size) => {
  if (!size) return
  focusEditor()
  wrapSelection({ fontSize: `${size}px` })
}

const applyLineHeight = (height) => {
  if (!height) return
  focusEditor()
  const sel = window.getSelection()
  if (!sel || !sel.anchorNode) return
  const block = sel.anchorNode.parentElement?.closest('p')
  if (block) {
    block.style.lineHeight = height
    handleInput()
  }
}

const applyTextColor = (event) => {
  const color = event?.target?.value
  if (!color) return
  focusEditor()
  exec('foreColor', color)
}

const applyBackColor = (event) => {
  const color = event?.target?.value
  if (!color) return
  focusEditor()
  exec('hiliteColor', color)
}

const handleSave = async () => {
  if (!htmlContent.value) {
    message.warning('暂无内容可保存')
    return
  }
  saving.value = true
  try {
    await api.saveReportDraft({
      valuation_id: pageMeta.value.valuationId,
      template_id: pageMeta.value.templateId,
      html: htmlContent.value,
    })
    message.success('草稿已保存')
  } catch (error) {
    console.error('保存失败', error)
    message.error(error?.message || '保存失败，请稍后重试')
  } finally {
    saving.value = false
  }
}

const handleExport = async () => {
  if (!pageMeta.value.valuationId) {
    message.error('缺少参数，无法导出')
    return
  }
  exporting.value = true
  try {
    const res = await api.exportReportTemplate({
      valuation_id: pageMeta.value.valuationId,
      template_id: pageMeta.value.templateId,
    })
    const blob = res instanceof Blob ? res : new Blob([res])
    const fileName = `${pageMeta.value.title || '报告'}.pdf`
    saveAs(blob, fileName)
    message.success('导出成功')
  } catch (error) {
    console.error('导出失败', error)
    message.error('导出失败，请检查后端接口')
  } finally {
    exporting.value = false
  }
}

const handleBack = () => {
  router.back()
}

onMounted(fetchTemplateHtml)
</script>

<template>
  <CommonPage title="报告在线编辑" :show-footer="false">
    <template #header>
      <div class="editor-page__header">
        <div class="title">
          <h2>{{ pageMeta.title || '报告在线编辑' }}</h2>
          <div class="meta">
            <NTag size="small" type="info">估值ID：{{ pageMeta.valuationId || '未知' }}</NTag>
            <NTag v-if="pageMeta.templateId" size="small" type="success">
              模板ID：{{ pageMeta.templateId }}
            </NTag>
          </div>
        </div>
        <NSpace>
          <NButton @click="handleBack">返回</NButton>
          <NButton secondary @click="handleSave" :loading="saving">保存草稿</NButton>
          <NButton type="primary" @click="handleExport" :loading="exporting">
            导出 PDF
          </NButton>
        </NSpace>
      </div>
    </template>

    <div class="editor-page">
      <div class="editor-toolbar">
        <NSpace wrap>
          <NButton size="small" @click="exec('bold')">加粗</NButton>
          <NButton size="small" @click="exec('italic')">斜体</NButton>
          <NButton size="small" @click="exec('underline')">下划线</NButton>
          <NButton size="small" @click="exec('justifyLeft')">左对齐</NButton>
          <NButton size="small" @click="exec('justifyCenter')">居中</NButton>
          <NButton size="small" @click="exec('justifyRight')">右对齐</NButton>
          <NButton size="small" @click="exec('insertUnorderedList')">无序列表</NButton>
          <NButton size="small" @click="exec('insertOrderedList')">有序列表</NButton>
          <NButton size="small" @click="exec('indent')">增加缩进</NButton>
          <NButton size="small" @click="exec('outdent')">减少缩进</NButton>
          <NButton size="small" @click="exec('removeFormat')">清除格式</NButton>
          <NButton size="small" @click="exec('undo')">撤销</NButton>
          <NButton size="small" @click="exec('redo')">重做</NButton>
        </NSpace>
        <NSpace wrap>
          <NSelect
            size="small"
            style="width: 120px"
            placeholder="字号"
            :options="fontSizeOptions"
            @update:value="applyFontSize"
          />
          <NSelect
            size="small"
            style="width: 120px"
            placeholder="行距"
            :options="lineHeightOptions"
            @update:value="applyLineHeight"
          />
          <label class="color-picker">
            文字色
            <input type="color" @input="applyTextColor" />
          </label>
          <label class="color-picker">
            背景色
            <input type="color" @input="applyBackColor" />
          </label>
        </NSpace>
      </div>

      <NSpin :show="loading">
        <div class="editor-wrapper">
          <div
            ref="editorRef"
            class="editor-canvas"
            contenteditable="true"
            spellcheck="false"
            @input="handleInput"
          ></div>
        </div>
      </NSpin>
    </div>
  </CommonPage>
</template>

<style scoped>
.editor-page__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.editor-page__header .title {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.editor-page__header h2 {
  margin: 0;
}

.meta {
  display: flex;
  align-items: center;
  gap: 8px;
}

.editor-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.editor-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #f7f7f9;
  border: 1px solid #e0e4eb;
  border-radius: 10px;
  padding: 10px 12px;
}

.color-picker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #555;
}

.color-picker input {
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: transparent;
}

.editor-wrapper {
  width: 100%;
  display: flex;
  justify-content: center;
  padding: 12px 0 24px;
}

.editor-canvas {
  width: 100%;
  min-height: 400px;
  padding: 0;
  background: transparent;
  outline: none;
}

.editor-canvas:focus {
  box-shadow: none;
}

.doc-header h1 {
  margin: 0 0 10px;
  font-size: 22px;
  text-align: center;
}

.doc-header p {
  margin: 0 0 18px;
  color: #555;
  text-align: center;
}

.doc-block h2 {
  margin: 18px 0 8px;
  font-size: 16px;
}

.doc-block p {
  margin: 8px 0;
}
</style>
