'use strict';

const fs = require('node:fs');
const path = require('node:path');
const { slugify, escapeXml } = require('../core/util');
const { encodePng } = require('../core/png');
const { guideSlug, renderAllImages, stepBlocks, codeBlockText } = require('./common');

/**
 * Confluence storage-format export. Writes a single XHTML document plus a
 * sidecar attachments folder containing the rendered screenshots referenced
 * by the page.
 */

const DEFAULT_TEMPLATE = {
  includeImages: true,
};

const MACRO_FOR_LEVEL = {
  info: 'info',
  warn: 'warning',
  error: 'note',
  success: 'tip',
};

function anchorFor(step) {
  return `step-${step.number.replace(/\./g, '-')}`;
}

function stepLinkRewrite(html, ast) {
  return String(html || '').replace(/href="step:([^"]+)"/g, (m, id) => {
    const target = ast.steps.find((s) => s.stepId === id);
    return target ? `href="#${anchorFor(target)}"` : 'data-missing-step-link="true"';
  });
}

function cdata(text) {
  return `<![CDATA[${String(text || '').replace(/]]>/g, ']]]]><![CDATA[>')}]]>`;
}

function blockMacro(tb, ast) {
  const macro = MACRO_FOR_LEVEL[tb.level] || 'note';
  const title = tb.title ? `<ac:parameter ac:name="title">${escapeXml(tb.title)}</ac:parameter>` : '';
  const body = tb.descriptionHtml ? `<div>${stepLinkRewrite(tb.descriptionHtml, ast)}</div>` : '<p />';
  return `<ac:structured-macro ac:name="${macro}">${title}<ac:rich-text-body>${body}</ac:rich-text-body></ac:structured-macro>`;
}

function exportConfluence(ast, outDir, template = {}) {
  const tpl = { ...DEFAULT_TEMPLATE, ...template };
  fs.mkdirSync(outDir, { recursive: true });
  const images = tpl.includeImages ? renderAllImages(ast) : new Map();
  const attachmentDir = path.join(outDir, `${guideSlug(ast)}-attachments`);
  fs.mkdirSync(attachmentDir, { recursive: true });

  let attachmentCount = 0;
  const attachmentNames = new Map();
  for (const step of ast.steps) {
    const img = images.get(step.stepId);
    if (!img) continue;
    attachmentCount += 1;
    const fileName = `${String(attachmentCount).padStart(3, '0')}-${slugify(step.title || step.stepId, step.stepId)}.png`;
    fs.writeFileSync(path.join(attachmentDir, fileName), encodePng(img));
    attachmentNames.set(step.stepId, fileName);
  }

  const stepXml = ast.steps.map((step) => {
    const parts = [`<a id="${anchorFor(step)}"></a>`, `<h2>${escapeXml(step.number)}. ${escapeXml(step.title || 'Untitled step')}</h2>`];
    if (step.skipped) parts.push('<p><em>(skipped)</em></p>');

    for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'before-description')) {
      parts.push(blockMacro(tb, ast));
    }

    if (step.descriptionHtml) {
      parts.push(`<div>${stepLinkRewrite(step.descriptionHtml, ast)}</div>`);
    }

    const attachment = attachmentNames.get(step.stepId);
    if (attachment) {
      parts.push(`<p><ac:image><ri:attachment ri:filename="${escapeXml(attachment)}" /></ac:image></p>`);
    }

    for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
      if (block.kind === 'code') {
        const lang = block.language ? `<ac:parameter ac:name="language">${escapeXml(block.language)}</ac:parameter>` : '';
        parts.push(`<ac:structured-macro ac:name="code">${lang}<ac:plain-text-body>${cdata(codeBlockText(block))}</ac:plain-text-body></ac:structured-macro>`);
      } else if (block.kind === 'table') {
        if (!block.rows || !block.rows.length) continue;
        const width = Math.max(...block.rows.map((row) => row.length));
        const rows = block.rows.map((row, rowIndex) => (
          `<tr>${Array.from({ length: width }, (_, i) => {
            const cell = escapeXml(row[i] ?? '');
            return rowIndex === 0 ? `<th>${cell}</th>` : `<td>${cell}</td>`;
          }).join('')}</tr>`
        ));
        parts.push(`<table><tbody>${rows.join('')}</tbody></table>`);
      }
    }

    for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'after-description')) {
      parts.push(blockMacro(tb, ast));
    }
    for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'after-image')) {
      parts.push(blockMacro(tb, ast));
    }

    return `<div class="step">${parts.join('\n')}</div>`;
  }).join('\n');

  const html = `<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ac="http://atlassian.com/content"
      xmlns:ri="http://atlassian.com/resource/identifier">
<head>
  <title>${escapeXml(ast.guide.title)}</title>
</head>
<body>
  <h1>${escapeXml(ast.guide.title)}</h1>
  ${ast.guide.descriptionHtml ? `<div>${stepLinkRewrite(ast.guide.descriptionHtml, ast)}</div>` : ''}
  ${stepXml}
</body>
</html>
`;

  const file = path.join(outDir, `${guideSlug(ast)}.confluence.xml`);
  fs.writeFileSync(file, html);
  return { file, attachmentCount: images.size };
}

module.exports = { exportConfluence, DEFAULT_TEMPLATE };
