import zlib from 'node:zlib';

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

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 extractXmlText(xml) {
  const paragraphs = [];
  for (const block of String(xml ?? '').split('</w:p>')) {
    const texts = [...block.matchAll(/<w:t[^>]*>([\s\S]*?)<\/w:t>/g)].map((match) =>
      escapeXml(match[1]),
    );
    const line = texts.join('').replace(/\s+/g, ' ').trim();
    if (line) paragraphs.push(line);
  }
  return paragraphs.join('\n\n');
}

function extractDocxText(buffer) {
  const xmlBuffer = extractZipEntry(buffer, 'word/document.xml');
  return xmlBuffer ? extractXmlText(xmlBuffer.toString('utf8')) : '';
}

function extractXlsxText(buffer) {
  const sharedStringsXml = extractZipEntry(buffer, 'xl/sharedStrings.xml');
  const sharedStrings = sharedStringsXml
    ? [...sharedStringsXml.toString('utf8').matchAll(/<t[^>]*>([\s\S]*?)<\/t>/g)].map((match) =>
        escapeXml(match[1]),
      )
    : [];
  const sheetEntries = [];
  let index = 1;
  while (true) {
    const sheet = extractZipEntry(buffer, `xl/worksheets/sheet${index}.xml`);
    if (!sheet) break;
    const rows = [];
    for (const rowBlock of sheet.toString('utf8').split('</row>')) {
      const cells = [];
      for (const cell of rowBlock.matchAll(/<c\b[^>]*?(?:t="([^"]+)")?[^>]*>(?:[\s\S]*?<v>([\s\S]*?)<\/v>)?/g)) {
        const type = cell[1] ?? '';
        const value = cell[2] ?? '';
        if (type === 's') {
          const sharedIndex = Number(value);
          cells.push(sharedStrings[sharedIndex] ?? value);
        } else {
          cells.push(escapeXml(value));
        }
      }
      if (cells.length) rows.push(cells.join('\t'));
    }
    if (rows.length) {
      sheetEntries.push(`Sheet ${index}\n${rows.join('\n')}`);
    }
    index += 1;
  }
  return sheetEntries.join('\n\n');
}

function extractPptxText(buffer) {
  const slides = [];
  let index = 1;
  while (true) {
    const slide = extractZipEntry(buffer, `ppt/slides/slide${index}.xml`);
    if (!slide) break;
    const texts = [...slide.toString('utf8').matchAll(/<a:t[^>]*>([\s\S]*?)<\/a:t>/g)].map((match) =>
      escapeXml(match[1]),
    );
    const text = texts.join(' ').replace(/\s+/g, ' ').trim();
    if (text) slides.push(`Slide ${index}\n${text}`);
    index += 1;
  }
  return slides.join('\n\n');
}

function extractPdfText(buffer) {
  const text = buffer.toString('latin1');
  const segments = [];
  for (const streamMatch of text.matchAll(/stream\r?\n([\s\S]*?)\r?\nendstream/g)) {
    const stream = streamMatch[1];
    for (const textMatch of stream.matchAll(/\(([^()\\]*(?:\\.[^()\\]*)*)\)\s*Tj/g)) {
      const raw = textMatch[1]
        .replace(/\\n/g, '\n')
        .replace(/\\r/g, '\r')
        .replace(/\\t/g, '\t')
        .replace(/\\\\/g, '\\')
        .replace(/\\\(/g, '(')
        .replace(/\\\)/g, ')');
      const cleaned = raw.replace(/\s+/g, ' ').trim();
      if (cleaned) segments.push(cleaned);
    }
  }
  return segments.join('\n');
}

export function extractAttachmentText(buffer, mimeType, filename = '') {
  const normalizedMimeType = String(mimeType ?? '');
  const extension = String(filename ?? '').toLowerCase();
  if (normalizedMimeType === 'application/pdf') {
    const text = extractPdfText(buffer);
    return { text, format: 'pdf', warnings: text ? [] : ['pdf_text_not_detected'] };
  }
  if (
    normalizedMimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
    extension.endsWith('.docx')
  ) {
    const text = extractDocxText(buffer);
    return { text, format: 'docx', warnings: text ? [] : ['docx_text_not_detected'] };
  }
  if (
    normalizedMimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
    extension.endsWith('.xlsx')
  ) {
    const text = extractXlsxText(buffer);
    return { text, format: 'xlsx', warnings: text ? [] : ['xlsx_text_not_detected'] };
  }
  if (
    normalizedMimeType === 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ||
    extension.endsWith('.pptx')
  ) {
    const text = extractPptxText(buffer);
    return { text, format: 'pptx', warnings: text ? [] : ['pptx_text_not_detected'] };
  }
  if (normalizedMimeType.startsWith('text/')) {
    return { text: buffer.toString('utf8'), format: 'text', warnings: [] };
  }
  return { text: '', format: 'unsupported', warnings: ['unsupported_attachment_type'] };
}

export const attachmentTextInternals = {
  extractZipEntry,
  extractDocxText,
  extractXlsxText,
  extractPptxText,
  extractPdfText,
};
