'use strict';
/**
 * Conversation export formatters — Markdown / JSON / HTML.
 *
 * Ported from upstream claude-code-history-viewer
 * src/services/export/{markdownExporter,jsonExporter,htmlExporter}.ts
 * and src/hooks/useExport.ts. See gap-reports/viewer-2026-04-23.md item C.
 *
 * All formatters are pure functions of (messages, meta) and do no I/O,
 * making them straightforward to test and safe to run on any thread.
 *
 * Input message shape (same as /api/session/messages returns):
 *   { role: 'user' | 'assistant', text: string, timestamp?: string,
 *     model?: string, ... }
 */

/**
 * Strip filesystem-invalid characters and clamp length so the result is
 * a safe filename on all three major OSes. Invalid on Windows: <>:"/\|?*
 * plus control chars 0x00-0x1f.
 */
function sanitizeFilename(name) {
  // eslint-disable-next-line no-control-regex
  const safe = String(name || 'conversation').replace(/[<>:"/\\|?*\x00-\x1f]/g, '_').trim();
  return safe.slice(0, 200) || 'conversation';
}

function _roleLabel(role) {
  if (role === 'user') return 'You';
  if (role === 'assistant') return 'Assistant';
  return String(role || '').trim() || 'Note';
}

/**
 * Render a single message to Markdown. Kept internal — call toMarkdown
 * for the header-framed export.
 */
function _messageMd(m) {
  const role = _roleLabel(m.role);
  const ts = m.timestamp ? ` _${m.timestamp}_` : '';
  const model = m.role === 'assistant' && m.model ? ` · \`${m.model}\`` : '';
  const body = (m.text || '').replace(/\r\n/g, '\n');
  return `\n### ${role}${ts}${model}\n\n${body}\n`;
}

function toMarkdown(messages, meta = {}) {
  const msgs = Array.isArray(messages) ? messages : [];
  const { sessionName = 'Conversation', projectPath = '', createdAt = '' } = meta;
  const lines = [];
  lines.push(`# ${sessionName}\n`);
  if (projectPath) lines.push(`**Project:** \`${projectPath}\`  `);
  if (createdAt) lines.push(`**Created:** ${createdAt}  `);
  lines.push(`**Messages:** ${msgs.length}\n`);
  lines.push('\n---\n');
  for (const m of msgs) lines.push(_messageMd(m));
  return lines.join('');
}

function toJson(messages, meta = {}) {
  return JSON.stringify({
    exported_at: new Date().toISOString(),
    format_version: 1,
    session: {
      name: meta.sessionName || '',
      id: meta.sessionId || '',
      project_path: meta.projectPath || '',
      created_at: meta.createdAt || '',
    },
    message_count: Array.isArray(messages) ? messages.length : 0,
    messages: Array.isArray(messages) ? messages : [],
  }, null, 2);
}

function _escapeHtml(s) {
  return String(s == null ? '' : s)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function toHtml(messages, meta = {}) {
  const msgs = Array.isArray(messages) ? messages : [];
  const title = _escapeHtml(meta.sessionName || 'Conversation');
  const metaLine = [
    meta.projectPath ? _escapeHtml(meta.projectPath) : '',
    meta.createdAt ? _escapeHtml(meta.createdAt) : '',
    `${msgs.length} messages`,
  ].filter(Boolean).join(' · ');
  const rows = msgs.map(m => {
    const role = _escapeHtml(_roleLabel(m.role));
    // Use white-space: pre-wrap in CSS instead of <br> so copy-paste
    // out of the exported HTML keeps the original line breaks intact.
    const body = _escapeHtml(m.text || '');
    const ts = m.timestamp ? `<time datetime="${_escapeHtml(m.timestamp)}">${_escapeHtml(m.timestamp)}</time>` : '';
    const model = m.role === 'assistant' && m.model ? ` <span class="model">${_escapeHtml(m.model)}</span>` : '';
    const cls = m.role === 'user' ? 'msg msg-user' : (m.role === 'assistant' ? 'msg msg-assistant' : 'msg');
    return `<article class="${cls}">\n  <header>${role}${model} ${ts}</header>\n  <div class="body">${body}</div>\n</article>`;
  }).join('\n');
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>${title}</title>
  <meta name="generator" content="ctm / claude-task-manager">
  <style>
    :root { color-scheme: light dark; }
    body { font: 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; max-width: 820px; margin: 2em auto; padding: 0 1em; }
    h1 { margin: 0 0 4px; font-size: 22px; }
    .meta { color: #666; font-size: 12px; margin-bottom: 1.5em; }
    article.msg { margin: 1em 0; padding: 10px 14px; border-radius: 8px; border-left: 3px solid transparent; }
    article.msg-user { background: rgba(124,147,255,0.10); border-color: #7c93ff; }
    article.msg-assistant { background: rgba(160,212,160,0.10); border-color: #a0d4a0; }
    article.msg header { font-size: 11px; font-weight: 600; color: #555; margin-bottom: 6px; }
    article.msg .body { white-space: pre-wrap; word-wrap: break-word; }
    article.msg time { font-weight: 400; opacity: 0.6; margin-left: 8px; }
    article.msg .model { font-weight: 400; color: #888; margin-left: 6px; }
    @media (prefers-color-scheme: dark) {
      body { background: #151519; color: #e0e0e0; }
      .meta { color: #999; }
      article.msg-user { background: rgba(124,147,255,0.12); }
      article.msg-assistant { background: rgba(160,212,160,0.08); }
      article.msg header { color: #aaa; }
    }
  </style>
</head>
<body>
  <h1>${title}</h1>
  <div class="meta">${metaLine}</div>
  ${rows}
</body>
</html>
`;
}

/** Returns { contentType, ext } for a given format key (md|markdown|json|html). */
function resolveFormat(formatKey) {
  const f = String(formatKey || '').toLowerCase();
  if (f === 'md' || f === 'markdown') return { contentType: 'text/markdown; charset=utf-8', ext: 'md' };
  if (f === 'json') return { contentType: 'application/json; charset=utf-8', ext: 'json' };
  if (f === 'html') return { contentType: 'text/html; charset=utf-8', ext: 'html' };
  return null;
}

module.exports = { toMarkdown, toJson, toHtml, sanitizeFilename, resolveFormat };
