<script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue'

const props = defineProps({
  fileBlob: { type: Blob, required: true },
  fileName: { type: String, required: true },
  format: { type: String, required: true },
})

const docxContainer = ref(null)
const isRendering = ref(false)
const errorMessage = ref('')
let renderToken = 0

const isDocx = computed(() => props.format === 'docx')
const previewTitle = computed(() => {
  if (props.format === 'docx') return 'Word 文档预览'
  if (props.format === 'xlsx') return 'Excel 文件'
  if (props.format === 'pptx') return 'PowerPoint 文件'
  return 'Office 文件'
})

function clearDocxPreview() {
  if (docxContainer.value) {
    docxContainer.value.replaceChildren()
  }
}

function downloadCurrentFile() {
  if (!(props.fileBlob instanceof Blob)) return

  const url = URL.createObjectURL(props.fileBlob)
  const anchor = document.createElement('a')
  anchor.href = url
  anchor.download = props.fileName || 'document'
  document.body.appendChild(anchor)
  anchor.click()
  anchor.remove()
  setTimeout(() => URL.revokeObjectURL(url), 0)
}

async function renderPreview() {
  const currentToken = ++renderToken
  errorMessage.value = ''
  isRendering.value = isDocx.value
  clearDocxPreview()

  if (!isDocx.value) {
    isRendering.value = false
    return
  }

  try {
    const { renderAsync } = await import('docx-preview')
    if (currentToken !== renderToken || !docxContainer.value) return

    await renderAsync(props.fileBlob, docxContainer.value, undefined, {
      className: 'docx-preview-shell',
      ignoreWidth: false,
      ignoreHeight: false,
      inWrapper: false,
      breakPages: false,
      useBase64URL: true,
    })

    if (currentToken !== renderToken) {
      clearDocxPreview()
      return
    }
  } catch (error) {
    if (currentToken !== renderToken) return
    errorMessage.value = error instanceof Error && error.message
      ? error.message
      : '文档预览失败，请下载后查看原文件。'
    clearDocxPreview()
  } finally {
    if (currentToken === renderToken) {
      isRendering.value = false
    }
  }
}

watch(
  () => [props.fileBlob, props.fileName, props.format],
  () => {
    void renderPreview()
  },
  { immediate: true }
)

onBeforeUnmount(() => {
  renderToken += 1
  clearDocxPreview()
})
</script>

<template>
  <section class="office-preview-card">
    <header class="office-preview-header">
      <div>
        <h3>{{ previewTitle }}</h3>
        <p>{{ fileName }}</p>
      </div>
      <button type="button" class="office-preview-download" @click="downloadCurrentFile">下载原文件</button>
    </header>

    <div class="office-preview-body">
      <div v-if="isRendering" class="office-preview-state">
        <h4>正在加载预览</h4>
        <p>仅在当前文件需要时才按需加载预览模块，避免初始页面加载过重。</p>
      </div>

      <div v-else-if="errorMessage" class="office-preview-state office-preview-state-error">
        <h4>预览失败</h4>
        <p>{{ errorMessage }}</p>
      </div>

      <div v-else-if="isDocx" ref="docxContainer" class="docx-preview-host"></div>

      <div v-else class="office-preview-state">
        <h4>当前格式不再使用内置重型预览引擎</h4>
        <p>为了降低移动端包体积和运行负担，Excel 与 PowerPoint 文件改为下载后使用本地应用查看。</p>
      </div>
    </div>
  </section>
</template>

<style scoped>
.office-preview-card {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  background: var(--github-bg);
}

.office-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--github-border);
  background: rgba(248, 250, 252, 0.92);
  position: sticky;
  top: 0;
  z-index: 1;
}

[data-theme='dark'] .office-preview-header {
  background: rgba(13, 17, 23, 0.96);
}

.office-preview-header h3 {
  margin: 0;
  font-size: 16px;
}

.office-preview-header p {
  margin: 4px 0 0;
  color: var(--github-text-secondary);
  font-size: 13px;
  word-break: break-all;
}

.office-preview-download {
  flex: none;
  height: 36px;
  padding: 0 14px;
  border: 1px solid var(--github-border);
  border-radius: 8px;
  background: var(--github-bg);
  color: var(--app-text);
  cursor: pointer;
}

.office-preview-download:hover {
  background: var(--github-hover);
}

.office-preview-body {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 20px;
}

.office-preview-state {
  display: grid;
  gap: 10px;
  place-content: center;
  min-height: 320px;
  padding: 24px;
  border: 1px dashed var(--github-border);
  border-radius: 16px;
  background: var(--github-hover);
  text-align: center;
}

.office-preview-state h4,
.office-preview-state p {
  margin: 0;
}

.office-preview-state p {
  color: var(--github-text-secondary);
  line-height: 1.6;
}

.office-preview-state-error {
  border-style: solid;
}

.docx-preview-host {
  overflow: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
}

.docx-preview-host :deep(.docx-preview-shell) {
  max-width: 100%;
}

.docx-preview-host :deep(.docx-wrapper) {
  padding: 0;
  background: transparent;
}

.docx-preview-host :deep(section.docx) {
  width: min(100%, 960px) !important;
  margin: 0 auto 20px !important;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.08);
}

@media (max-width: 720px) {
  .office-preview-header {
    align-items: flex-start;
    flex-direction: column;
  }

  .office-preview-download {
    width: 100%;
  }

  .office-preview-body {
    padding: 12px;
  }
}
</style>
