import zlib from 'node:zlib';

const PREVIEWABLE_MIME_TYPES = new Set([
  'text/html',
  'text/plain',
  'text/markdown',
  'text/csv',
  'application/pdf',
  'image/png',
  'image/jpeg',
  'image/webp',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
]);

const PREVIEW_SHELL_STYLE = `
html,body{margin:0;padding:0;background:#f5f0e5;color:#1f2937;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
body{padding:24px;box-sizing:border-box;line-height:1.6}
pre,code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
pre{white-space:pre-wrap;word-break:break-word;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:16px}
table{border-collapse:collapse;width:100%;background:#fff;border-radius:12px;overflow:hidden}
th,td{border:1px solid #e5e7eb;padding:8px 10px;text-align:left;font-size:14px}
th{background:#faf7ef}
.docx-preview h1{font-size:1.5rem;margin:0 0 16px}
.docx-preview p{margin:0 0 12px;text-indent:2em}
.docx-preview .meta{color:#6b7280;font-size:13px;margin-bottom:20px;text-indent:0}
.pdf-frame,.image-frame{display:block;width:100%;min-height:calc(100vh - 48px);border:0;border-radius:12px;background:#fff}
.image-frame{object-fit:contain;max-height:calc(100vh - 48px);width:auto;max-width:100%;margin:0 auto;cursor:zoom-in}
.image-viewer{padding:0}
.image-viewer h1,.image-viewer .meta{display:none}
.image-viewer .image-frame{min-height:100vh;max-height:100vh;margin:0;border-radius:0;background:#0b100e}
.image-lightbox[hidden]{display:none}
.image-lightbox{position:fixed;inset:0;z-index:9999;display:grid;place-items:center;padding:max(16px,env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));background:rgba(7,12,10,.9);cursor:zoom-out}
.image-lightbox img{display:block;max-width:min(100%,1600px);max-height:calc(100vh - 32px);object-fit:contain;border-radius:8px;box-shadow:0 24px 80px rgba(0,0,0,.35);cursor:default}
.image-lightbox-close{position:fixed;top:max(16px,env(safe-area-inset-top));right:max(16px,env(safe-area-inset-right));z-index:10000;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:999px;color:#fffaf0;background:rgba(24,33,29,.72);box-shadow:0 8px 24px rgba(0,0,0,.28);cursor:pointer}
.image-lightbox-close svg{display:block;width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none}
.image-lightbox-close:hover{background:rgba(24,33,29,.92)}
`;

const IMAGE_LIGHTBOX_CLOSE_ICON =
  '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>';

const IMAGE_LIGHTBOX_SCRIPT = `<script>
(function () {
  var trigger = document.querySelector('[data-image-lightbox-trigger]');
  var lightbox = document.querySelector('.image-lightbox');
  if (!trigger || !lightbox) return;
  var closeBtn = lightbox.querySelector('.image-lightbox-close');

  function openLightbox() {
    lightbox.hidden = false;
    document.body.style.overflow = 'hidden';
  }

  function closeLightbox() {
    lightbox.hidden = true;
    document.body.style.overflow = '';
  }

  trigger.addEventListener('click', function () {
    openLightbox();
  });
  closeBtn.addEventListener('click', function (event) {
    event.stopPropagation();
    closeLightbox();
  });
  lightbox.addEventListener('click', function (event) {
    if (event.target === lightbox) closeLightbox();
  });
  document.addEventListener('keydown', function (event) {
    if (event.key === 'Escape' && !lightbox.hidden) {
      event.preventDefault();
      closeLightbox();
    }
  });
})();
</script>`;

function escapeHtml(text) {
  return String(text ?? '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

function previewDocument(
  title,
  bodyHtml,
  { downloadUrl = null, extraHead = '', allowScripts = false, bodyClass = '' } = {},
) {
  const csp = [
    "default-src 'none'",
    "style-src 'unsafe-inline'",
    "img-src 'self' data: https:",
    "font-src 'self' data:",
    "frame-src 'self'",
    "object-src 'self'",
    "base-uri 'none'",
    "form-action 'none'",
    allowScripts ? "script-src 'unsafe-inline'" : "script-src 'none'",
  ].join('; ');
  const downloadLink = downloadUrl
    ? `<p class="meta"><a href="${escapeHtml(downloadUrl)}" target="_blank" rel="noopener noreferrer">下载原文件</a></p>`
    : '';
  const bodyAttrs = bodyClass ? ` class="${escapeHtml(bodyClass)}"` : '';
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta http-equiv="Content-Security-Policy" content="${csp}"><title>${escapeHtml(title)}</title><style>${PREVIEW_SHELL_STYLE}</style>${extraHead}</head><body${bodyAttrs}><h1>${escapeHtml(title)}</h1>${downloadLink}${bodyHtml}</body></html>`;
}

function renderImageLightboxMarkup({ downloadUrl, title, triggerClass = 'image-frame' }) {
  const safeUrl = escapeHtml(downloadUrl);
  const safeTitle = escapeHtml(title);
  return (
    `<img class="${escapeHtml(triggerClass)}" data-image-lightbox-trigger src="${safeUrl}" alt="${safeTitle}">` +
    `<div class="image-lightbox" hidden role="dialog" aria-modal="true" aria-label="图片预览">` +
    `<button type="button" class="image-lightbox-close" aria-label="关闭">${IMAGE_LIGHTBOX_CLOSE_ICON}</button>` +
    `<img src="${safeUrl}" alt="${safeTitle}">` +
    `</div>${IMAGE_LIGHTBOX_SCRIPT}`
  );
}

export function wantsInlineImageViewer(req) {
  if (req?.query?.viewer === '1') return true;
  if (req?.query?.viewer === '0') return false;
  const fetchDest = String(req?.get?.('sec-fetch-dest') ?? req?.headers?.['sec-fetch-dest'] ?? '').toLowerCase();
  if (fetchDest === 'image') return false;
  if (fetchDest === 'document' || fetchDest === 'iframe') return true;
  const accept = String(req?.get?.('accept') ?? req?.headers?.accept ?? '');
  return /text\/html/i.test(accept);
}

export function renderImageAssetViewerHtml({ asset, downloadUrl }) {
  const title = asset.displayName || asset.filename;
  return previewDocument(title, renderImageLightboxMarkup({ downloadUrl, title }), {
    downloadUrl,
    allowScripts: true,
    bodyClass: 'image-viewer',
  });
}

function renderInlineMarkdown(text) {
  let html = escapeHtml(text);
  html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
  html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
  html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
  html = html.replace(/\[([^\]]+)]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
  return html;
}

function renderMarkdownDocument(text) {
  const lines = String(text ?? '').split('\n');
  const blocks = [];
  let inCode = false;
  let code = [];
  for (const line of lines) {
    if (line.startsWith('```')) {
      if (inCode) {
        blocks.push(`<pre><code>${escapeHtml(code.join('\n'))}</code></pre>`);
        code = [];
        inCode = false;
      } else {
        inCode = true;
      }
      continue;
    }
    if (inCode) {
      code.push(line);
      continue;
    }
    if (!line.trim()) continue;
    const heading = line.match(/^(#{1,6})\s+(.+)$/);
    if (heading) {
      const level = heading[1].length;
      blocks.push(`<h${level}>${renderInlineMarkdown(heading[2])}</h${level}>`);
      continue;
    }
    blocks.push(`<p>${renderInlineMarkdown(line)}</p>`);
  }
  if (inCode && code.length) {
    blocks.push(`<pre><code>${escapeHtml(code.join('\n'))}</code></pre>`);
  }
  return blocks.join('\n');
}

function parseCsvRows(text) {
  const rows = [];
  let row = [];
  let cell = '';
  let inQuotes = false;
  for (let i = 0; i < text.length; i += 1) {
    const ch = text[i];
    const next = text[i + 1];
    if (inQuotes) {
      if (ch === '"' && next === '"') {
        cell += '"';
        i += 1;
      } else if (ch === '"') {
        inQuotes = false;
      } else {
        cell += ch;
      }
      continue;
    }
    if (ch === '"') {
      inQuotes = true;
      continue;
    }
    if (ch === ',') {
      row.push(cell);
      cell = '';
      continue;
    }
    if (ch === '\n') {
      row.push(cell);
      rows.push(row);
      row = [];
      cell = '';
      continue;
    }
    if (ch === '\r') continue;
    cell += ch;
  }
  row.push(cell);
  rows.push(row);
  return rows.filter((item) => item.some((value) => String(value ?? '').trim()));
}

function renderCsvPreview(text) {
  const rows = parseCsvRows(String(text ?? ''));
  if (rows.length === 0) return '<pre>（空文件）</pre>';
  const [head, ...body] = rows;
  const header = `<thead><tr>${head.map((cell) => `<th>${escapeHtml(cell)}</th>`).join('')}</tr></thead>`;
  const content = body
    .slice(0, 200)
    .map((line) => `<tr>${line.map((cell) => `<td>${escapeHtml(cell)}</td>`).join('')}</tr>`)
    .join('');
  const tail = body.length > 200 ? `<p class="meta">仅展示前 200 行</p>` : '';
  return `<div style="overflow:auto">${tail}<table>${header}<tbody>${content}</tbody></table></div>`;
}

function extractZipEntry(buffer, targetName) {
  let offset = 0;
  while (offset + 30 <= buffer.length) {
    if (buffer.subarray(offset, offset + 2).toString('ascii') !== 'PK') break;
    const compressionMethod = buffer.readUInt16LE(offset + 8);
    const compressedSize = buffer.readUInt32LE(offset + 18);
    const nameLength = buffer.readUInt16LE(offset + 26);
    const extraLength = buffer.readUInt16LE(offset + 28);
    const name = buffer.subarray(offset + 30, offset + 30 + nameLength).toString('utf8');
    const dataStart = offset + 30 + nameLength + extraLength;
    if (name === targetName) {
      const compressed = buffer.subarray(dataStart, dataStart + compressedSize);
      if (compressionMethod === 0) return compressed;
      if (compressionMethod === 8) return zlib.inflateRawSync(compressed);
      return null;
    }
    offset = dataStart + compressedSize;
  }
  return null;
}

function extractDocxText(buffer) {
  const xmlBuffer = extractZipEntry(buffer, 'word/document.xml');
  if (!xmlBuffer) return '';
  const xml = xmlBuffer.toString('utf8');
  const paragraphs = [];
  for (const block of xml.split('</w:p>')) {
    const texts = [...block.matchAll(/<w:t[^>]*>([\s\S]*?)<\/w:t>/g)].map((match) =>
      match[1]
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&')
        .replace(/&quot;/g, '"'),
    );
    const line = texts.join('');
    if (line.trim()) paragraphs.push(line.trim());
  }
  return paragraphs.join('\n\n');
}

export function canPreviewAsset(mimeType) {
  return PREVIEWABLE_MIME_TYPES.has(mimeType);
}

export function renderAssetPreviewHtml({ asset, buffer, downloadUrl }) {
  const title = asset.displayName || asset.filename;
  const mimeType = asset.mimeType;

  if (mimeType === 'text/html') {
    const csp =
      '<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; style-src \'unsafe-inline\' \'self\'; img-src data: https:; font-src \'self\' data:; base-uri \'none\'; form-action \'none\'; script-src \'none\'">';
    const html = buffer.toString('utf8');
    if (/<head[^>]*>/i.test(html)) {
      return html.replace(/<head([^>]*)>/i, `<head$1>${csp}`);
    }
    return `<!doctype html><html lang="zh-CN"><head>${csp}</head><body>${html}</body></html>`;
  }

  if (mimeType === 'application/pdf') {
    return previewDocument(
      title,
      `<iframe class="pdf-frame" src="${escapeHtml(downloadUrl)}" title="${escapeHtml(title)}"></iframe>`,
      { downloadUrl },
    );
  }

  if (mimeType.startsWith('image/')) {
    return previewDocument(title, renderImageLightboxMarkup({ downloadUrl, title }), {
      downloadUrl,
      allowScripts: true,
    });
  }

  if (mimeType === 'text/csv') {
    return previewDocument(title, renderCsvPreview(buffer.toString('utf8')), { downloadUrl });
  }

  if (mimeType === 'text/markdown') {
    return previewDocument(title, renderMarkdownDocument(buffer.toString('utf8')), { downloadUrl });
  }

  if (mimeType === 'text/plain') {
    return previewDocument(title, `<pre>${escapeHtml(buffer.toString('utf8'))}</pre>`, { downloadUrl });
  }

  if (mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
    const text = extractDocxText(buffer);
    const body = text
      ? `<article class="docx-preview">${text
          .split(/\n{2,}/)
          .map((paragraph) => `<p>${escapeHtml(paragraph)}</p>`)
          .join('')}</article>`
      : '<p class="meta">无法提取正文，请下载原文件查看。</p>';
    return previewDocument(title, body, { downloadUrl });
  }

  throw Object.assign(new Error('该资产不支持预览'), { code: 'preview_not_supported' });
}
