import fs from 'node:fs';
import PDFDocument from 'pdfkit';
import {
  AlignmentType,
  Document,
  Footer,
  Header,
  HeadingLevel,
  Packer,
  PageNumber,
  Paragraph,
  TextRun,
} from 'docx';
import type { BuiltExportPayload } from '../../../api/src/features/tenant-content/exports.js';
import { config } from '../config.js';

interface RenderResult {
  body: Buffer;
  mimeType: string;
  extension: 'pdf' | 'docx';
}

type ExportQuestion = BuiltExportPayload['questions'][number];

const PDF_FONT_CANDIDATES = [
  config.exportPdfFontPath,
  'C:\\Windows\\Fonts\\NotoSansSC-VF.ttf',
  'C:\\Windows\\Fonts\\msyh.ttc',
  'C:\\Windows\\Fonts\\simhei.ttf',
  '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc',
  '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc',
  '/usr/share/fonts/truetype/wqy/wqy-microhei.ttc',
].filter(Boolean);

function plainText(value: unknown) {
  return String(value ?? '')
    .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, ' [图片: $1 $2] ')
    .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/(p|div|li|tr|h[1-6])>/gi, '\n')
    .replace(/<[^>]+>/g, '')
    .replace(/&nbsp;/g, ' ')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&amp;/g, '&')
    .replace(/\r\n/g, '\n')
    .replace(/\n{3,}/g, '\n\n')
    .trim();
}

function optionText(option: unknown, index: number) {
  const prefix = `${String.fromCharCode(65 + index)}. `;
  if (option && typeof option === 'object' && !Array.isArray(option)) {
    const object = option as Record<string, unknown>;
    return prefix + plainText(object.text ?? object.label ?? object.content ?? JSON.stringify(object));
  }
  return prefix + plainText(option);
}

function answerText(question: ExportQuestion) {
  const answers: string[] = [];
  if (typeof question.correctOptionIndex === 'number') {
    answers.push(String.fromCharCode(65 + question.correctOptionIndex));
  }
  if (Array.isArray(question.correctOptionIndices) && question.correctOptionIndices.length) {
    answers.push(question.correctOptionIndices.map(item => String.fromCharCode(65 + Number(item))).join(', '));
  }
  if (typeof question.answerText === 'string' && question.answerText.trim()) {
    answers.push(plainText(question.answerText));
  }
  return Array.from(new Set(answers.filter(Boolean))).join('；');
}

function subQuestionLines(question: ExportQuestion, includeAnswers: boolean, includeExplanations: boolean) {
  const subQuestions = Array.isArray(question.subQuestions) ? question.subQuestions : [];
  const lines: string[] = [];
  subQuestions.forEach((item, index) => {
    if (!item || typeof item !== 'object' || Array.isArray(item)) return;
    const object = item as Record<string, unknown>;
    lines.push(`(${index + 1}) ${plainText(object.content ?? object.stem ?? object.title ?? '')}`);
    const options = Array.isArray(object.options) ? object.options : [];
    options.forEach((option, optionIndex) => {
      lines.push(`    ${optionText(option, optionIndex)}`);
    });
    if (includeAnswers) {
      const answer = plainText(object.answerText ?? object.answer_text ?? object.answer ?? object.correctAnswer ?? object.referenceAnswer ?? '');
      const optionAnswer = Array.isArray(object.correctOptionIndices)
        ? object.correctOptionIndices.map(value => String.fromCharCode(65 + Number(value))).join(', ')
        : typeof object.correctOptionIndex === 'number'
          ? String.fromCharCode(65 + object.correctOptionIndex)
          : '';
      if (answer || optionAnswer) lines.push(`    答案：${answer || optionAnswer}`);
    }
    if (includeExplanations) {
      const explanation = plainText(object.explanation ?? object.analysis ?? '');
      if (explanation) lines.push(`    解析：${explanation}`);
    }
  });
  return lines;
}

function boolOption(payload: BuiltExportPayload, key: string) {
  return payload.options && typeof payload.options[key] === 'boolean' ? payload.options[key] === true : false;
}

function watermarkText(payload: BuiltExportPayload) {
  const raw = payload.options?.watermarkText;
  return typeof raw === 'string' && raw.trim() ? raw.trim().slice(0, 80) : '';
}

function isDailyPractice(payload: BuiltExportPayload) {
  return payload.exportType === 'daily_practice';
}

function brandName(payload: BuiltExportPayload) {
  return payload.dailyPractice?.brand?.name || '恭学教育';
}

function findPdfFont() {
  return PDF_FONT_CANDIDATES.find(candidate => fs.existsSync(candidate));
}

function collectPdf(doc: PDFKit.PDFDocument) {
  return new Promise<Buffer>((resolve, reject) => {
    const chunks: Buffer[] = [];
    doc.on('data', chunk => chunks.push(Buffer.from(chunk)));
    doc.on('end', () => resolve(Buffer.concat(chunks)));
    doc.on('error', reject);
  });
}

function renderPdfWatermark(doc: PDFKit.PDFDocument, text: string) {
  if (!text) return;
  const width = doc.page.width;
  const height = doc.page.height;
  doc.save();
  doc.rotate(-35, { origin: [width / 2, height / 2] });
  doc.opacity(0.08);
  doc.fillColor('#64748b');
  doc.fontSize(36);
  doc.text(text, 40, height / 2 - 40, { align: 'center', width: width - 80 });
  doc.opacity(1);
  doc.restore();
  doc.fillColor('#111827');
}

function writePdfQuestion(doc: PDFKit.PDFDocument, question: ExportQuestion, index: number, payload: BuiltExportPayload) {
  const includeAnswers = boolOption(payload, 'includeAnswers');
  const includeExplanations = boolOption(payload, 'includeExplanations');
  const typeLabel = typeof question.typeLabel === 'string' && question.typeLabel ? ` [${question.typeLabel}]` : '';
  const score = typeof question.score === 'number' ? ` (${question.score}分)` : '';
  doc.moveDown(0.4);
  doc.fontSize(10).fillColor('#111827').text(`${index + 1}. ${plainText(question.content)}${typeLabel}${score}`, {
    lineGap: 3,
  });
  const options = Array.isArray(question.options) ? question.options : [];
  options.forEach((option, optionIndex) => {
    doc.fontSize(9).fillColor('#374151').text(optionText(option, optionIndex), { indent: 16, lineGap: 2 });
  });
  for (const line of subQuestionLines(question, includeAnswers, includeExplanations)) {
    doc.fontSize(9).fillColor('#374151').text(line, { indent: line.startsWith('(') ? 16 : 26, lineGap: 2 });
  }
  const answer = includeAnswers ? answerText(question) : '';
  if (answer) {
    doc.fontSize(9).fillColor('#0f766e').text(`答案：${answer}`, { indent: 16, lineGap: 2 });
  }
  if (includeExplanations && typeof question.explanation === 'string' && question.explanation.trim()) {
    doc.fontSize(9).fillColor('#475569').text(`解析：${plainText(question.explanation)}`, { indent: 16, lineGap: 2 });
  }
}

function fitText(
  doc: PDFKit.PDFDocument,
  text: string,
  x: number,
  y: number,
  width: number,
  height: number,
  fontSize = 10,
  options: PDFKit.Mixins.TextOptions = {},
) {
  const content = plainText(text) || ' ';
  let nextFontSize = fontSize;
  while (nextFontSize > 6) {
    doc.fontSize(nextFontSize);
    const measured = doc.heightOfString(content, { ...options, width });
    if (measured <= height) break;
    nextFontSize -= 0.6;
  }
  doc.fontSize(nextFontSize).text(content, x, y, { ...options, width, height });
}

function writeDailyPracticePdfCard(
  doc: PDFKit.PDFDocument,
  question: ExportQuestion,
  index: number,
  x: number,
  y: number,
  width: number,
  height: number,
  payload: BuiltExportPayload,
) {
  const includeAnswers = boolOption(payload, 'includeAnswers');
  const includeExplanations = boolOption(payload, 'includeExplanations');
  doc.save();
  doc.roundedRect(x, y, width, height, 8).fillAndStroke('#ffffff', '#d7dee8');
  doc.fillColor('#0f172a').fontSize(8).text(`${String(index + 1).padStart(2, '0')}  ${question.typeLabel || question.type || '题目'}`, x + 10, y + 10, {
    width: width - 20,
  });
  doc.moveTo(x + 10, y + 25).lineTo(x + width - 10, y + 25).strokeColor('#e2e8f0').stroke();
  fitText(doc, String(question.content || ''), x + 10, y + 33, width - 20, height * 0.44, 10, { lineGap: 2 });
  const options = Array.isArray(question.options) ? question.options.slice(0, 4) : [];
  let optionY = y + height * 0.56;
  options.forEach((option, optionIndex) => {
    fitText(doc, optionText(option, optionIndex), x + 12, optionY, width - 24, 16, 9, { lineGap: 1 });
    optionY += 18;
  });
  const answer = includeAnswers ? answerText(question) : '';
  if (answer) {
    doc.fillColor('#0f766e').fontSize(7.5).text(`答案：${answer}`, x + 10, y + height - 31, { width: width - 20 });
  }
  if (includeExplanations && typeof question.explanation === 'string' && question.explanation.trim()) {
    doc.fillColor('#64748b').fontSize(7).text(`解析：${plainText(question.explanation).slice(0, 70)}`, x + 10, y + height - 18, { width: width - 20 });
  }
  doc.restore();
}

async function renderDailyPracticePdf(payload: BuiltExportPayload): Promise<RenderResult> {
  const doc = new PDFDocument({ size: 'A4', margin: 36, bufferPages: true, autoFirstPage: true });
  const done = collectPdf(doc);
  const fontPath = findPdfFont();
  if (fontPath) {
    doc.registerFont('TikuCjk', fontPath);
    doc.font('TikuCjk');
  }
  const watermark = watermarkText(payload) || brandName(payload);
  renderPdfWatermark(doc, watermark);

  const daily = payload.dailyPractice;
  const title = daily?.issue || payload.title || '每日一练';
  doc.rect(0, 0, doc.page.width, doc.page.height).fill('#f6f8fb');
  renderPdfWatermark(doc, watermark);
  doc.fillColor('#0f172a').fontSize(22).text(title, 36, 34, { width: doc.page.width - 72, align: 'center' });
  doc.fillColor('#64748b').fontSize(9).text(
    `${daily?.date || payload.exportedAt.slice(0, 10)} · ${daily?.brand?.slogan || '专注高职升本'} · ${daily?.brand?.english || 'GONGXUE EDU'}`,
    36,
    64,
    { width: doc.page.width - 72, align: 'center' },
  );

  const gap = 10;
  const gridX = 44;
  const gridY = 96;
  const cardW = (doc.page.width - gridX * 2 - gap * 2) / 3;
  const cardH = (doc.page.height - gridY - 82 - gap * 2) / 3;
  const questionSlots = [0, 1, 2, 3, 5, 6, 7, 8];
  payload.questions.slice(0, 8).forEach((question, index) => {
    const slot = questionSlots[index];
    const row = Math.floor(slot / 3);
    const col = slot % 3;
    writeDailyPracticePdfCard(
      doc,
      question,
      index,
      gridX + col * (cardW + gap),
      gridY + row * (cardH + gap),
      cardW,
      cardH,
      payload,
    );
  });

  const centerX = gridX + (cardW + gap);
  const centerY = gridY + (cardH + gap);
  doc.save();
  doc.roundedRect(centerX, centerY, cardW, cardH, 8).fillAndStroke('#0f172a', '#0f172a');
  doc.fillColor('#ffffff').fontSize(17).text(daily?.centerSlot.title || brandName(payload), centerX + 10, centerY + 35, {
    width: cardW - 20,
    align: 'center',
  });
  doc.fillColor('#cbd5e1').fontSize(9).text(daily?.centerSlot.subtitle || daily?.brand.ctaLine || '每日一练', centerX + 10, centerY + 72, {
    width: cardW - 20,
    align: 'center',
  });
  doc.restore();

  doc.fillColor('#64748b').fontSize(8).text(
    `${daily?.brand?.name || '恭学教育'} · ${daily?.brand?.ctaLine || '每日一练 · 精选八题 · 稳步上岸'}`,
    36,
    doc.page.height - 44,
    { width: doc.page.width - 72, align: 'center' },
  );
  doc.end();
  return {
    body: await done,
    mimeType: 'application/pdf',
    extension: 'pdf',
  };
}

export async function renderExportPdf(payload: BuiltExportPayload): Promise<RenderResult> {
  if (isDailyPractice(payload)) {
    return renderDailyPracticePdf(payload);
  }

  const doc = new PDFDocument({ size: 'A4', margin: 48, bufferPages: true, autoFirstPage: true });
  const done = collectPdf(doc);
  const fontPath = findPdfFont();
  if (fontPath) {
    doc.registerFont('TikuCjk', fontPath);
    doc.font('TikuCjk');
  }

  const watermark = watermarkText(payload);
  renderPdfWatermark(doc, watermark);
  doc.on('pageAdded', () => {
    if (fontPath) doc.font('TikuCjk');
    renderPdfWatermark(doc, watermark);
  });

  doc.fontSize(18).fillColor('#0f172a').text(payload.title || '题库导出', { align: 'center' });
  doc.moveDown(0.4);
  doc.fontSize(9).fillColor('#64748b').text(
    `导出时间：${payload.exportedAt}    题量：${payload.summary.questionCount}    总分：${payload.summary.totalScore}`,
    { align: 'center' },
  );
  if (watermark) {
    doc.moveDown(0.3);
    doc.fontSize(8).fillColor('#94a3b8').text(`水印：${watermark}`, { align: 'center' });
  }

  let questionIndex = 0;
  for (const section of payload.sections) {
    doc.moveDown(1);
    doc.fontSize(13).fillColor('#1e3a8a').text(section.title || section.key, { underline: true });
    for (const question of section.questions) {
      writePdfQuestion(doc, question, questionIndex, payload);
      questionIndex += 1;
    }
  }

  const pages = doc.bufferedPageRange();
  for (let i = 0; i < pages.count; i += 1) {
    doc.switchToPage(i);
    if (fontPath) doc.font('TikuCjk');
    doc.fontSize(8).fillColor('#94a3b8').text(
      `${payload.title || '题库导出'} · 第 ${i + 1} / ${pages.count} 页`,
      48,
      doc.page.height - 34,
      { align: 'center', width: doc.page.width - 96 },
    );
  }
  doc.end();
  return {
    body: await done,
    mimeType: 'application/pdf',
    extension: 'pdf',
  };
}

function docxParagraph(text: string, options: { heading?: (typeof HeadingLevel)[keyof typeof HeadingLevel]; bold?: boolean } = {}) {
  return new Paragraph({
    heading: options.heading,
    spacing: { after: 140 },
    children: [
      new TextRun({
        text,
        bold: options.bold,
        font: 'Microsoft YaHei',
        size: options.heading ? 28 : 21,
      }),
    ],
  });
}

function docxQuestionParagraphs(question: ExportQuestion, index: number, payload: BuiltExportPayload) {
  const includeAnswers = boolOption(payload, 'includeAnswers');
  const includeExplanations = boolOption(payload, 'includeExplanations');
  const paragraphs: Paragraph[] = [];
  const typeLabel = typeof question.typeLabel === 'string' && question.typeLabel ? ` [${question.typeLabel}]` : '';
  const score = typeof question.score === 'number' ? ` (${question.score}分)` : '';
  paragraphs.push(docxParagraph(`${index + 1}. ${plainText(question.content)}${typeLabel}${score}`, { bold: true }));
  const options = Array.isArray(question.options) ? question.options : [];
  options.forEach((option, optionIndex) => {
    paragraphs.push(docxParagraph(optionText(option, optionIndex)));
  });
  for (const line of subQuestionLines(question, includeAnswers, includeExplanations)) {
    paragraphs.push(docxParagraph(line));
  }
  const answer = includeAnswers ? answerText(question) : '';
  if (answer) paragraphs.push(docxParagraph(`答案：${answer}`));
  if (includeExplanations && typeof question.explanation === 'string' && question.explanation.trim()) {
    paragraphs.push(docxParagraph(`解析：${plainText(question.explanation)}`));
  }
  return paragraphs;
}

export async function renderExportDocx(payload: BuiltExportPayload): Promise<RenderResult> {
  if (isDailyPractice(payload)) {
    return renderDailyPracticeDocx(payload);
  }

  const watermark = watermarkText(payload);
  const children: Paragraph[] = [
    new Paragraph({
      heading: HeadingLevel.TITLE,
      alignment: AlignmentType.CENTER,
      spacing: { after: 180 },
      children: [new TextRun({ text: payload.title || '题库导出', bold: true, font: 'Microsoft YaHei', size: 36 })],
    }),
    new Paragraph({
      alignment: AlignmentType.CENTER,
      spacing: { after: 180 },
      children: [
        new TextRun({
          text: `导出时间：${payload.exportedAt}    题量：${payload.summary.questionCount}    总分：${payload.summary.totalScore}`,
          color: '64748B',
          font: 'Microsoft YaHei',
          size: 19,
        }),
      ],
    }),
  ];
  if (watermark) {
    children.push(new Paragraph({
      alignment: AlignmentType.CENTER,
      spacing: { after: 180 },
      children: [new TextRun({ text: `水印：${watermark}`, color: '94A3B8', font: 'Microsoft YaHei', size: 18 })],
    }));
  }
  let questionIndex = 0;
  for (const section of payload.sections) {
    children.push(docxParagraph(section.title || section.key, { heading: HeadingLevel.HEADING_2, bold: true }));
    for (const question of section.questions) {
      children.push(...docxQuestionParagraphs(question, questionIndex, payload));
      questionIndex += 1;
    }
  }

  const doc = new Document({
    creator: 'Tiku SaaS Export Worker',
    title: payload.title || '题库导出',
    description: 'Generated by Tiku SaaS export worker',
    sections: [
      {
        headers: {
          default: new Header({
            children: [
              new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [
                  new TextRun({
                    text: watermark || payload.title || '题库导出',
                    color: 'CBD5E1',
                    font: 'Microsoft YaHei',
                    size: 18,
                  }),
                ],
              }),
            ],
          }),
        },
        footers: {
          default: new Footer({
            children: [
              new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [
                  new TextRun({ children: ['第 ', PageNumber.CURRENT, ' / ', PageNumber.TOTAL_PAGES, ' 页'], font: 'Microsoft YaHei', size: 18 }),
                ],
              }),
            ],
          }),
        },
        children,
      },
    ],
  });

  return {
    body: await Packer.toBuffer(doc),
    mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    extension: 'docx',
  };
}

async function renderDailyPracticeDocx(payload: BuiltExportPayload): Promise<RenderResult> {
  const daily = payload.dailyPractice;
  const title = daily?.issue || payload.title || '每日一练';
  const children: Paragraph[] = [
    new Paragraph({
      heading: HeadingLevel.TITLE,
      alignment: AlignmentType.CENTER,
      spacing: { after: 160 },
      children: [new TextRun({ text: title, bold: true, font: 'Microsoft YaHei', size: 36 })],
    }),
    new Paragraph({
      alignment: AlignmentType.CENTER,
      spacing: { after: 220 },
      children: [new TextRun({
        text: `${daily?.date || payload.exportedAt.slice(0, 10)} · ${daily?.brand.slogan || '专注高职升本'} · ${daily?.brand.ctaLine || '每日一练'}`,
        color: '64748B',
        font: 'Microsoft YaHei',
        size: 19,
      })],
    }),
  ];
  payload.questions.slice(0, 8).forEach((question, index) => {
    children.push(...docxQuestionParagraphs(question, index, payload));
  });
  children.push(new Paragraph({
    alignment: AlignmentType.CENTER,
    spacing: { before: 220 },
    children: [new TextRun({
      text: daily?.brand.name || '恭学教育',
      bold: true,
      color: '0F172A',
      font: 'Microsoft YaHei',
      size: 24,
    })],
  }));

  const doc = new Document({
    creator: 'Tiku SaaS Export Worker',
    title,
    description: 'Generated by Tiku SaaS daily practice export worker',
    sections: [
      {
        headers: {
          default: new Header({
            children: [
              new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [new TextRun({ text: watermarkText(payload) || brandName(payload), color: 'CBD5E1', font: 'Microsoft YaHei', size: 18 })],
              }),
            ],
          }),
        },
        footers: {
          default: new Footer({
            children: [
              new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [new TextRun({ children: ['第 ', PageNumber.CURRENT, ' / ', PageNumber.TOTAL_PAGES, ' 页'], font: 'Microsoft YaHei', size: 18 })],
              }),
            ],
          }),
        },
        children,
      },
    ],
  });

  return {
    body: await Packer.toBuffer(doc),
    mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    extension: 'docx',
  };
}
