/**
 * 报告 DOCX 导出 composable
 *
 * 与 useReportPdfExport 类似,但输出 .docx 文件:
 *  - 用 html2canvas 把 .body-wrap 截图成 canvas
 *  - canvas 转 PNG bytes
 *  - 用 docx 库把 PNG 嵌入 A4 大小的 docx 中
 *
 * 用法:
 *   const { exportReportDocx } = useReportDocxExport()
 *   await exportReportDocx(bodyWrapEl, 'report.docx')
 */

import html2canvas from 'html2canvas'
import {
  AlignmentType,
  Document,
  HeightRule,
  ImageRun,
  Packer,
  PageOrientation,
  Paragraph,
} from 'docx'
import { saveAs } from 'file-saver'
import { ElMessage } from 'element-plus'

// A4 尺寸(单位 twip,1 inch = 1440 twip, 1mm ≈ 56.6929 twip)
const A4_WIDTH_TWIP = 11906
const A4_HEIGHT_TWIP = 16838
const MARGIN_TWIP = 850 // 约 15mm

export interface ExportReportDocxOptions {
  /** 离屏容器宽度,默认 800 */
  width?: number
  /** 缩放比例,值越大越清晰,默认 2 */
  scale?: number
  /** 临时隐藏元素的选择器列表 */
  hideSelectors?: string[]
  /** 是否不显示内部 loading 提示 */
  silent?: boolean
}

/**
 * canvas → PNG ArrayBuffer (docx 库 ImageRun 需要 Uint8Array)
 */
function canvasToPngBuffer(canvas: HTMLCanvasElement): Promise<Uint8Array> {
  return new Promise((resolve, reject) => {
    canvas.toBlob(async (blob) => {
      if (!blob) {
        reject(new Error('canvas 转 Blob 失败'))
        return
      }
      try {
        const buf = await blob.arrayBuffer()
        resolve(new Uint8Array(buf))
      } catch (e) {
        reject(e)
      }
    }, 'image/png')
  })
}

export function useReportDocxExport() {
  /**
   * 将原 DOM 节点导出为 DOCX。
   */
  const exportReportDocx = async (
    sourceEl: HTMLElement,
    fileName: string,
    options: ExportReportDocxOptions = {},
  ): Promise<void> => {
    if (!sourceEl) {
      ElMessage.error('未找到可导出的报告内容')
      return
    }

    const {
      width = 800,
      scale = 2,
      hideSelectors = ['.body-header', '.el-loading-mask', '.el-loading-spinner'],
      silent = false,
    } = options

    // 1) 记录原父节点 + 下一个兄弟节点,截图完毕后还原
    const originalParent = sourceEl.parentNode as HTMLElement
    const originalNext = sourceEl.nextSibling as ChildNode | null

    // 2) 读取 sourceEl 的计算样式,移动后强制 inline 注入 padding
    const originalStyleAttr = sourceEl.getAttribute('style') || ''
    const computed = window.getComputedStyle(sourceEl)
    sourceEl.style.paddingTop = computed.paddingTop
    sourceEl.style.paddingRight = computed.paddingRight
    sourceEl.style.paddingBottom = computed.paddingBottom
    sourceEl.style.paddingLeft = computed.paddingLeft

    // 3) 临时隐藏指定元素
    const hiddenDisplays: Array<{ el: HTMLElement; prev: string }> = []
    hideSelectors.forEach((sel) => {
      sourceEl.querySelectorAll<HTMLElement>(sel).forEach((el) => {
        hiddenDisplays.push({ el, prev: el.style.display })
        el.style.display = 'none'
      })
    })

    // 4) 创建离屏容器
    const offScreen = document.createElement('div')
    offScreen.style.cssText = [
      'position: fixed',
      'left: -99999px',
      'top: 0',
      `width: ${width}px`,
      'background: #ffffff',
      'z-index: -1',
      'pointer-events: none',
    ].join(';')
    document.body.appendChild(offScreen)
    offScreen.appendChild(sourceEl)

    // 4.1) 强制 sourceEl 宽度,让 ECharts 按目标宽度重绘 canvas
    const prevWidth = sourceEl.style.width
    const prevMaxWidth = sourceEl.style.maxWidth
    const prevBoxSizing = sourceEl.style.boxSizing
    sourceEl.style.width = `${width}px`
    sourceEl.style.maxWidth = `${width}px`
    sourceEl.style.boxSizing = 'border-box'

    let loadingMsg: { close: () => void } | null = null
    if (!silent) {
      loadingMsg = ElMessage({
        message: '正在生成 Word…',
        type: 'info',
        duration: 0,
      })
    }

    try {
      // 给浏览器一帧时间让样式/layout 生效
      await new Promise((r) => requestAnimationFrame(() => r(null)))
      await new Promise((r) => requestAnimationFrame(() => r(null)))
      // 等 ECharts canvas 完成重绘
      await new Promise((r) => setTimeout(r, 1500))

      const canvas = await html2canvas(sourceEl, {
        scale,
        backgroundColor: '#ffffff',
        logging: false,
        useCORS: false,
        allowTaint: true,
        windowWidth: width,
        foreignObjectRendering: false,
      })

      // canvas → PNG bytes
      const pngBytes = await canvasToPngBuffer(canvas)

      // docx 库 IMediaTransformation.width/height 单位是 **像素(px)**
      // A4 可用宽度: 210mm - 2*15mm(边距) = 180mm
      // 按 96 DPI: 180mm = 7.09 inch = 680 px
      const usableWidthPx = 680
      const ratio = canvas.height / canvas.width
      const imgHeightPx = Math.round(usableWidthPx * ratio)

      // 构造 docx 文档
      const doc = new Document({
        creator: 'CSM Report',
        title: fileName.replace(/\.docx$/i, ''),
        styles: {
          default: {
            document: {
              run: {
                font: 'Microsoft YaHei', // 中文字体
              },
            },
          },
        },
        sections: [
          {
            properties: {
              page: {
                size: {
                  orientation: PageOrientation.PORTRAIT,
                  width: A4_WIDTH_TWIP,
                  height: A4_HEIGHT_TWIP,
                },
                margin: {
                  top: MARGIN_TWIP,
                  right: MARGIN_TWIP,
                  bottom: MARGIN_TWIP,
                  left: MARGIN_TWIP,
                },
              },
            },
            children: [
              new Paragraph({
                alignment: AlignmentType.CENTER,
                spacing: {
                  after: 0,
                  line: 240,
                  lineRule: HeightRule.AUTO,
                },
                children: [
                  new ImageRun({
                    type: 'png', // ← 必填,指定图片类型,否则 Word/WPS 解析失败
                    data: pngBytes,
                    transformation: {
                      width: usableWidthPx, // ← 像素
                      height: imgHeightPx, // ← 像素
                    },
                  }),
                ],
              }),
            ],
          },
        ],
      })

      // 生成 .docx blob 并下载
      const blob = await Packer.toBlob(doc)
      saveAs(blob, fileName || 'report.docx')

      if (!silent) ElMessage.success('Word 下载成功')
    } catch (err) {
      console.error('[useReportDocxExport] Word 生成失败:', err)
      if (!silent) ElMessage.error((err as Error)?.message || 'Word 生成失败')
      throw err
    } finally {
      loadingMsg?.close()
      // 恢复隐藏元素的 display
      hiddenDisplays.forEach(({ el, prev }) => {
        el.style.display = prev
      })
      // 把原 DOM 移回原位置
      if (offScreen.parentNode) {
        offScreen.parentNode.removeChild(offScreen)
      }
      if (originalNext && originalNext.parentNode === originalParent) {
        originalParent.insertBefore(sourceEl, originalNext)
      } else {
        originalParent.appendChild(sourceEl)
      }
      // 恢复 sourceEl 原 inline style
      sourceEl.setAttribute('style', originalStyleAttr)
      sourceEl.style.width = prevWidth
      sourceEl.style.maxWidth = prevMaxWidth
      sourceEl.style.boxSizing = prevBoxSizing
    }
  }

  return { exportReportDocx }
}
