/**
 * pdf-generator.js - PDF 生成逻辑（平台无关）
 *
 * 修复记录：
 * v3: 修复引用块竖线偏下（起点改为文字顶部）
 *     修复用户消息内容为空（TEXT_NODE 被跳过，改为对顶层节点直接提取 innerText）
 *     统一字体为 normal，避免 bold 字形缺失乱码
 *     列表符号用 ASCII "-" / "N."
 */

(function () {
  'use strict';

  const CFG = {
    format: 'a4',
    unit: 'mm',
    margin: { top: 20, bottom: 20, left: 20, right: 20 },
    fontSize: {
      h1: 15, h2: 13, h3: 12, h4: 11, h5: 10, h6: 10,
      roleLabel: 11,
      body: 10,
      code: 9,
      footer: 9,
      exportTime: 9,
      title: 15,
    },
    color: {
      title:          [17,  24,  39],
      exportTime:     [107, 114, 128],
      divider:        [209, 213, 219],
      userRole:       [37,  99,  235],
      assistantRole:  [22,  163, 74],
      body:           [31,  41,  55],
      heading:        [17,  24,  39],
      quote:          [107, 114, 128],
      quoteBorder:    [180, 180, 180],
      codeFg:         [55,  65,  81],
      codeBg:         [246, 248, 250],
      footer:         [156, 163, 175],
      listBullet:     [107, 114, 128],
    },
    lineHeight: 1.55,
    codeLineHeight: 1.4,
    codePadding: 4,
    quoteIndent: 6,
    quoteBorderW: 0.8,
    listIndent: 5,
    fontName: 'SongtiSCBlack',
    fallbackFont: 'helvetica',
  };

  const pt2mm = (pt) => pt * 0.3528;

  // ── 主入口 ────────────────────────────────────────────────
  async function generatePDF({ title, messages }) {
    const { jsPDF } = window.jspdf;
    if (!jsPDF) throw new Error('jsPDF 未加载');

    const doc = new jsPDF({ orientation: 'portrait', unit: CFG.unit, format: CFG.format });
    _registerFont(doc);

    const pageW = doc.internal.pageSize.getWidth();
    const pageH = doc.internal.pageSize.getHeight();
    const cW    = pageW - CFG.margin.left - CFG.margin.right;
    const maxY  = pageH - CFG.margin.bottom - 8;

    const ctx = { doc, pageW, pageH, cW, maxY, y: CFG.margin.top };

    ctx.y = _drawDocTitle(ctx, title);
    ctx.y = _drawExportTime(ctx);
    ctx.y = _drawHRule(ctx, 0.4);
    ctx.y += 5;

    for (let i = 0; i < messages.length; i++) {
      const msg = messages[i];
      ctx.y = _ensureSpace(ctx, 10);
      ctx.y = _drawRoleLabel(ctx, msg.role);

      if (msg.contentNode && msg.contentNode.childNodes && msg.contentNode.childNodes.length > 0) {
        // 有子节点：走富文本渲染
        ctx.y = _drawContentNode(ctx, msg.contentNode, 0);
      } else {
        // 无子节点或纯文本节点：直接用 innerText/textContent
        const text = msg.content || (msg.contentNode ? (msg.contentNode.innerText || msg.contentNode.textContent || '') : '');
        if (text.trim()) {
          ctx.y = _drawText(ctx, text.trim(), CFG.fontSize.body, CFG.color.body, 0);
        }
      }

      if (i < messages.length - 1) {
        ctx.y += 3;
        ctx.y = _ensureSpace(ctx, 6);
        ctx.y = _drawDashedHRule(ctx);
        ctx.y += 3;
      }
    }

    _drawFooters(ctx);
    doc.save(_buildFilename(title));
  }

  // ── 字体 ─────────────────────────────────────────────────
  function _registerFont(doc) {
    try {
      if (typeof font !== 'undefined' && font) {
        doc.addFileToVFS('SongtiSCBlack-normal.ttf', font);
        doc.addFont('SongtiSCBlack-normal.ttf', CFG.fontName, 'normal');
      }
    } catch (e) {
      console.warn('[AI PDF Exporter] 中文字体注册失败:', e);
    }
  }

  function _setFont(doc, size) {
    try { doc.setFont(CFG.fontName, 'normal'); }
    catch (e) { doc.setFont(CFG.fallbackFont, 'normal'); }
    doc.setFontSize(size);
  }

  function _setCodeFont(doc, size) {
    try { doc.setFont('courier', 'normal'); }
    catch (e) { doc.setFont(CFG.fallbackFont, 'normal'); }
    doc.setFontSize(size);
  }

  // ── 空间检查 ──────────────────────────────────────────────
  function _ensureSpace(ctx, needed) {
    if (ctx.y + needed > ctx.maxY) {
      ctx.doc.addPage();
      ctx.y = CFG.margin.top;
    }
    return ctx.y;
  }

  // ── 封面 ──────────────────────────────────────────────────
  function _drawDocTitle(ctx, title) {
    const { doc, pageW } = ctx;
    _setFont(doc, CFG.fontSize.title);
    doc.setTextColor(...CFG.color.title);
    const lines = doc.splitTextToSize(title || 'AI Chat Export', ctx.cW);
    const lh = pt2mm(CFG.fontSize.title) * CFG.lineHeight;
    doc.text(lines, pageW / 2, ctx.y, { align: 'center' });
    return ctx.y + lines.length * lh + 3;
  }

  function _drawExportTime(ctx) {
    const { doc, pageW } = ctx;
    _setFont(doc, CFG.fontSize.exportTime);
    doc.setTextColor(...CFG.color.exportTime);
    const now = new Date();
    const s = `Export: ${now.getFullYear()}-${_pad(now.getMonth()+1)}-${_pad(now.getDate())} ${_pad(now.getHours())}:${_pad(now.getMinutes())}`;
    doc.text(s, pageW / 2, ctx.y, { align: 'center' });
    return ctx.y + pt2mm(CFG.fontSize.exportTime) * CFG.lineHeight + 3;
  }

  function _drawRoleLabel(ctx, role) {
    const { doc } = ctx;
    const label = role === 'user' ? 'You' : 'ChatGPT';
    const color = role === 'user' ? CFG.color.userRole : CFG.color.assistantRole;
    _setFont(doc, CFG.fontSize.roleLabel);
    doc.setTextColor(...color);
    doc.text(label, CFG.margin.left, ctx.y);
    return ctx.y + pt2mm(CFG.fontSize.roleLabel) * CFG.lineHeight + 1;
  }

  // ── 分隔线 ────────────────────────────────────────────────
  function _drawHRule(ctx, lw = 0.3) {
    const { doc } = ctx;
    doc.setDrawColor(...CFG.color.divider);
    doc.setLineWidth(lw);
    doc.line(CFG.margin.left, ctx.y, ctx.pageW - CFG.margin.right, ctx.y);
    return ctx.y + 3;
  }

  function _drawDashedHRule(ctx) {
    const { doc } = ctx;
    doc.setDrawColor(...CFG.color.divider);
    doc.setLineWidth(0.2);
    doc.setLineDashPattern([1.5, 1.5], 0);
    doc.line(CFG.margin.left, ctx.y, ctx.pageW - CFG.margin.right, ctx.y);
    doc.setLineDashPattern([], 0);
    return ctx.y + 2;
  }

  // ── 内容节点渲染入口 ──────────────────────────────────────
  /**
   * 渲染一个内容容器节点（遍历其直接子节点）
   * 专门处理 ChatGPT 的 .markdown.prose 容器
   */
  function _drawContentNode(ctx, containerNode, indent) {
    if (!containerNode) return ctx.y;

    // 如果容器本身就是文本节点（用户消息的 whitespace-pre-wrap）
    if (containerNode.nodeType === Node.TEXT_NODE) {
      const text = (containerNode.textContent || '').trim();
      if (text) ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
      return ctx.y;
    }

    // 检查是否是纯文本容器（用户消息：div.whitespace-pre-wrap，无块级子节点）
    const hasBlockChildren = Array.from(containerNode.childNodes).some(n => {
      if (n.nodeType !== Node.ELEMENT_NODE) return false;
      const t = (n.tagName || '').toLowerCase();
      return ['p','h1','h2','h3','h4','h5','h6','ul','ol','pre','blockquote','hr','table','div'].includes(t);
    });

    if (!hasBlockChildren) {
      // 纯文本容器：直接提取 innerText
      const text = (containerNode.innerText || containerNode.textContent || '').trim();
      if (text) ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
      return ctx.y;
    }

    // 有块级子节点：递归渲染每个子节点
    for (const child of containerNode.childNodes) {
      ctx.y = _drawNode(ctx, child, indent);
    }
    return ctx.y;
  }

  // ── 富文本节点渲染（递归）────────────────────────────────
  function _drawNode(ctx, node, indent) {
    if (!node) return ctx.y;

    // 文本节点：直接渲染文本
    if (node.nodeType === Node.TEXT_NODE) {
      const text = (node.textContent || '').trim();
      if (text) {
        ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
        ctx.y += 0.5;
      }
      return ctx.y;
    }

    if (node.nodeType !== Node.ELEMENT_NODE) return ctx.y;

    const tag = (node.tagName || '').toLowerCase();

    // 标题 h1-h6
    if (/^h[1-6]$/.test(tag)) {
      const level = parseInt(tag[1]);
      const sizeMap = {1:CFG.fontSize.h1, 2:CFG.fontSize.h2, 3:CFG.fontSize.h3,
                       4:CFG.fontSize.h4, 5:CFG.fontSize.h5, 6:CFG.fontSize.h6};
      const sz = sizeMap[level] || CFG.fontSize.body;
      ctx.y = _ensureSpace(ctx, pt2mm(sz) * CFG.lineHeight + 4);
      ctx.y += 2;
      ctx.y = _drawText(ctx, _nodeText(node), sz, CFG.color.heading, indent);
      ctx.y += 1;
      return ctx.y;
    }

    // 段落
    if (tag === 'p') {
      const text = _nodeText(node).trim();
      if (text) {
        ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
        ctx.y += 1;
      }
      return ctx.y;
    }

    // 代码块
    if (tag === 'pre') {
      const codeEl = node.querySelector('code') || node;
      const code = codeEl.innerText || codeEl.textContent || '';
      ctx.y = _drawCodeBlock(ctx, code, indent);
      ctx.y += 2;
      return ctx.y;
    }

    // 引用块
    if (tag === 'blockquote') {
      ctx.y = _drawBlockquote(ctx, node, indent);
      ctx.y += 2;
      return ctx.y;
    }

    // 列表
    if (tag === 'ul') {
      ctx.y = _drawList(ctx, node, indent, false, 1);
      ctx.y += 1;
      return ctx.y;
    }
    if (tag === 'ol') {
      const start = parseInt(node.getAttribute('start') || '1', 10);
      ctx.y = _drawList(ctx, node, indent, true, start);
      ctx.y += 1;
      return ctx.y;
    }

    // 水平线
    if (tag === 'hr') {
      ctx.y = _ensureSpace(ctx, 6);
      ctx.y += 2;
      ctx.y = _drawHRule(ctx, 0.2);
      ctx.y += 2;
      return ctx.y;
    }

    // 表格
    if (tag === 'table') {
      ctx.y = _drawTable(ctx, node, indent);
      ctx.y += 2;
      return ctx.y;
    }

    // br：换行
    if (tag === 'br') {
      ctx.y += pt2mm(CFG.fontSize.body) * CFG.lineHeight * 0.5;
      return ctx.y;
    }

    // 行内格式化元素（strong/em/code/span）：提取文本作为段落
    const inlineTags = new Set(['strong','em','b','i','code','span','a','mark','s','del','ins','sup','sub']);
    if (inlineTags.has(tag)) {
      const text = _nodeText(node).trim();
      if (text) {
        ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
        ctx.y += 0.5;
      }
      return ctx.y;
    }

    // 块级容器：递归子节点
    const blockTags = new Set(['div','section','article','main','header','footer',
                                'aside','nav','figure','figcaption','details','summary','li']);
    if (blockTags.has(tag)) {
      for (const child of node.childNodes) {
        ctx.y = _drawNode(ctx, child, indent);
      }
      return ctx.y;
    }

    // 兜底：提取文本
    const text = _nodeText(node).trim();
    if (text) {
      ctx.y = _drawText(ctx, text, CFG.fontSize.body, CFG.color.body, indent);
      ctx.y += 1;
    }
    return ctx.y;
  }

  // ── 纯文本渲染 ────────────────────────────────────────────
  function _drawText(ctx, text, fontSize, color, indent = 0) {
    const { doc, cW } = ctx;
    if (!text || !text.trim()) return ctx.y;

    _setFont(doc, fontSize);
    doc.setTextColor(...color);

    const availW = cW - indent;
    const lh = pt2mm(fontSize) * CFG.lineHeight;
    const lines = doc.splitTextToSize(text.trim(), availW);

    for (const line of lines) {
      ctx.y = _ensureSpace(ctx, lh);
      doc.text(line, CFG.margin.left + indent, ctx.y);
      ctx.y += lh;
    }
    return ctx.y;
  }

  // ── 代码块 ────────────────────────────────────────────────
  function _drawCodeBlock(ctx, code, indent = 0) {
    const { doc, cW } = ctx;
    if (!code.trim()) return ctx.y;

    _setCodeFont(doc, CFG.fontSize.code);
    doc.setTextColor(...CFG.color.codeFg);

    const availW = cW - indent - CFG.codePadding * 2;
    const lh = pt2mm(CFG.fontSize.code) * CFG.codeLineHeight;
    const lines = doc.splitTextToSize(code.trim(), availW);
    const blockH = lines.length * lh + CFG.codePadding * 2;

    if (ctx.y + blockH > ctx.maxY && ctx.y > CFG.margin.top + 10) {
      doc.addPage();
      ctx.y = CFG.margin.top;
    }

    const bx = CFG.margin.left + indent;
    const bw = cW - indent;

    doc.setFillColor(...CFG.color.codeBg);
    doc.setDrawColor(...CFG.color.divider);
    doc.setLineWidth(0.2);
    doc.roundedRect(bx, ctx.y, bw, blockH, 2, 2, 'FD');

    let ty = ctx.y + CFG.codePadding + lh * 0.85;
    for (const line of lines) {
      doc.text(line, bx + CFG.codePadding, ty);
      ty += lh;
    }

    return ctx.y + blockH;
  }

  // ── 引用块 ────────────────────────────────────────────────
  function _drawBlockquote(ctx, node, indent) {
    const text = _nodeText(node).trim();
    if (!text) return ctx.y;

    const { doc, cW } = ctx;
    const qIndent = indent + CFG.quoteIndent;
    const availW  = cW - qIndent;
    const lh      = pt2mm(CFG.fontSize.body) * CFG.lineHeight;

    _setFont(doc, CFG.fontSize.body);
    doc.setTextColor(...CFG.color.quote);

    const lines  = doc.splitTextToSize(text, availW);
    const blockH = lines.length * lh;

    ctx.y = _ensureSpace(ctx, blockH + 2);

    // 竖线：从文字顶部开始，与文字高度完全对齐
    // jsPDF 文字基线在 y 处，文字顶部约在 y - lh*0.75
    const lineX     = CFG.margin.left + indent + 1;
    const lineTop   = ctx.y - lh * 0.75;           // 第一行文字顶部
    const lineBottom = lineTop + blockH;            // 最后一行文字底部

    doc.setDrawColor(...CFG.color.quoteBorder);
    doc.setLineWidth(CFG.quoteBorderW);
    doc.line(lineX, lineTop, lineX, lineBottom);

    for (const line of lines) {
      ctx.y = _ensureSpace(ctx, lh);
      doc.text(line, CFG.margin.left + qIndent, ctx.y);
      ctx.y += lh;
    }
    return ctx.y;
  }

  // ── 列表 ─────────────────────────────────────────────────
  // 无序列表用 jsPDF circle 画实心圆点，有序列表用 "N."
  function _drawList(ctx, listNode, indent, ordered, startIndex) {
    let counter = startIndex;
    for (const child of listNode.children) {
      if (child.tagName.toLowerCase() !== 'li') continue;

      counter++;

      // 提取 li 直接文本（排除子列表）
      let liText = '';
      for (const n of child.childNodes) {
        if (n.nodeType === Node.TEXT_NODE) {
          liText += n.textContent;
        } else if (n.nodeType === Node.ELEMENT_NODE) {
          const t = n.tagName.toLowerCase();
          if (t !== 'ul' && t !== 'ol') liText += _nodeText(n);
        }
      }
      liText = liText.trim();

      if (liText) {
        const { doc, cW } = ctx;
        const lh      = pt2mm(CFG.fontSize.body) * CFG.lineHeight;
        const textX   = CFG.margin.left + indent + CFG.listIndent;
        const availW  = cW - indent - CFG.listIndent;

        ctx.y = _ensureSpace(ctx, lh);

        if (ordered) {
          // 有序列表：文字符号
          _setFont(doc, CFG.fontSize.body);
          doc.setTextColor(...CFG.color.listBullet);
          doc.text(`${counter - 1}.`, CFG.margin.left + indent + 1, ctx.y);
        } else {
          // 无序列表：画实心圆点（不依赖字体）
          // jsPDF 文字基线在 ctx.y，字符视觉中心约在基线上方 lh*0.28
          // 圆心对齐文字视觉中心
          const dotR = 0.7;  // 半径 mm（略小）
          const dotX = CFG.margin.left + indent + 2.5;
          const dotY = ctx.y - lh * 0.22;  // 文字视觉中心（基线上方约22%行高）
          doc.setFillColor(...CFG.color.listBullet);
          doc.circle(dotX, dotY, dotR, 'F');
        }

        doc.setTextColor(...CFG.color.body);
        _setFont(doc, CFG.fontSize.body);
        const lines = doc.splitTextToSize(liText, availW);
        for (let i = 0; i < lines.length; i++) {
          if (i > 0) ctx.y = _ensureSpace(ctx, lh);
          doc.text(lines[i], textX, ctx.y);
          ctx.y += lh;
        }
      }

      // 嵌套子列表
      for (const child2 of child.children) {
        const t2 = child2.tagName.toLowerCase();
        if (t2 === 'ul') ctx.y = _drawList(ctx, child2, indent + CFG.listIndent, false, 1);
        if (t2 === 'ol') {
          const s2 = parseInt(child2.getAttribute('start') || '1', 10);
          ctx.y = _drawList(ctx, child2, indent + CFG.listIndent, true, s2);
        }
      }
    }
    return ctx.y;
  }

  // ── 表格 ─────────────────────────────────────────────────
  function _drawTable(ctx, tableNode, indent) {
    const rows = tableNode.querySelectorAll('tr');
    rows.forEach((row, ri) => {
      const cells = row.querySelectorAll('th, td');
      const rowText = Array.from(cells)
        .map(c => (c.innerText || c.textContent || '').trim())
        .join('  |  ');
      const sz = ri === 0 ? CFG.fontSize.body + 0.5 : CFG.fontSize.body;
      ctx.y = _drawText(ctx, rowText, sz, CFG.color.body, indent);
      if (ri === 0) ctx.y = _drawHRule(ctx, 0.2);
    });
    return ctx.y;
  }

  // ── 页脚 ─────────────────────────────────────────────────
  function _drawFooters(ctx) {
    const { doc, pageW, pageH } = ctx;
    const total = doc.internal.getNumberOfPages();
    for (let i = 1; i <= total; i++) {
      doc.setPage(i);
      _setFont(doc, CFG.fontSize.footer);
      doc.setTextColor(...CFG.color.footer);
      doc.text(
        `Page ${i} / ${total}`,
        pageW / 2,
        pageH - CFG.margin.bottom + 6,
        { align: 'center' }
      );
    }
  }

  // ── 工具函数 ─────────────────────────────────────────────
  function _nodeText(node) {
    return node.innerText || node.textContent || '';
  }

  function _buildFilename(title) {
    const safe = (title || 'AI-Chat')
      .replace(/[\\/:*?"<>|]/g, '')
      .replace(/\s+/g, '-')
      .slice(0, 20);
    const now = new Date();
    return `chatgpt-${safe}-${now.getFullYear()}${_pad(now.getMonth()+1)}${_pad(now.getDate())}.pdf`;
  }

  function _pad(n) { return String(n).padStart(2, '0'); }

  window.generatePDF = generatePDF;
})();
