// exporters.jsx — Build XLSX (MQM report) and DOCX (corrected translation) in the browser.

// ── XLSX ──────────────────────────────────────────────────────────────────
// Four sheets, matching the structure of the user's reference file but in English:
//   1. Summary — title, metrics, dimension distribution, verdict, methodology
//   2. Segments — section, source, original target
//   3. MQM — error markup with category, severity, penalty, description, suggestion
//   4. Edit Distance — Levenshtein MT → PE for segments with proposed corrections

function buildMqmWorkbook(data, meta) {
  const XLSX = window.XLSX;
  const wb = XLSX.utils.book_new();
  const m = data.metrics || {};

  // ── Sheet 1: Summary ──
  const s1 = [
    [`MQM Assessment Report`],
    [meta.projectName || 'Untitled translation'],
    [`Pair: ${data.source_language || '?'} \u2192 ${data.target_language || '?'}. Domain: ${data.domain || '\u2014'}.`],
    [`Generated ${new Date().toISOString().slice(0, 19).replace('T', ' ')} UTC by APE (claude-opus-4-7).`],
    [],
    ['Metric', 'Value'],
    ['Source word count', m.source_word_count || 0],
    ['Total segments', (data.segments || []).length],
    ['Total errors', (data.errors || []).length],
    ['Critical errors (\u00d7 10)', m.counts?.critical || 0],
    ['Major errors (\u00d7 5)',    m.counts?.major || 0],
    ['Minor errors (\u00d7 1)',    m.counts?.minor || 0],
    ['Total penalty', m.penalty || 0],
    ['Quality Score (MQM)', `${(m.quality_score || 0).toFixed(2)} %`],
    [],
    ['Distribution by dimension'],
    ['Dimension', 'Errors'],
  ];
  Object.entries(m.dimension_counts || {}).forEach(([k, v]) => s1.push([k, v]));
  s1.push([]);
  s1.push(['Verdict']);
  s1.push([data.verdict || '']);
  s1.push([]);
  s1.push(['Methodology']);
  s1.push(['Standard: MQM-Core 2.0 (Multidimensional Quality Metrics).']);
  s1.push(['Severity weights: minor = 1, major = 5, critical = 10.']);
  s1.push(['Quality Score = (1 \u2212 total penalty / source word count) \u00d7 100 %.']);
  s1.push(['Segmentation: sentence-level pairs grouped by document section.']);
  s1.push(['Edit distance: Levenshtein between original MT and post-edited (PE) target. Normalized = distance / max(len(MT), len(PE)).']);

  const ws1 = XLSX.utils.aoa_to_sheet(s1);
  ws1['!cols'] = [{ wch: 40 }, { wch: 30 }];
  // Bold the section headers (row 0, 5, 15, 18+offset) — best-effort
  for (const cell of ['A1', 'A6', 'A16', 'A19']) {
    if (ws1[cell]) ws1[cell].s = { font: { bold: true } };
  }
  XLSX.utils.book_append_sheet(wb, ws1, 'Summary');

  // ── Sheet 2: Segments ──
  const s2 = [['#', 'Section', 'Source', 'Target']];
  for (const seg of data.segments || []) {
    s2.push([seg.id, seg.section || '', seg.source || '', seg.target || '']);
  }
  const ws2 = XLSX.utils.aoa_to_sheet(s2);
  ws2['!cols'] = [{ wch: 6 }, { wch: 20 }, { wch: 60 }, { wch: 60 }];
  ws2['!freeze'] = { xSplit: 0, ySplit: 1 };
  XLSX.utils.book_append_sheet(wb, ws2, 'Segments');

  // ── Sheet 3: MQM markup ──
  const s3 = [
    ['MQM error markup'],
    [`Weights: minor = 1, major = 5, critical = 10. Quality Score = (1 \u2212 penalty / ${m.source_word_count} words) \u00d7 100 %.`],
    [],
    ['Seg #', 'Category', 'Subcategory', 'Severity', 'Penalty', 'Description', 'Source span', 'Target span', 'Suggestion'],
  ];
  const errs = (data.errors || []).slice().sort((a, b) => (a.segment_id || 0) - (b.segment_id || 0));
  for (const e of errs) {
    const w = window.SEVERITY_WEIGHTS[(e.severity || '').toLowerCase()] || 0;
    s3.push([
      e.segment_id || '',
      e.category || '',
      e.subcategory || '',
      (e.severity || '').toLowerCase(),
      w,
      e.description || '',
      e.source_span || '',
      e.target_span || '',
      e.suggestion || '',
    ]);
  }
  s3.push([]);
  s3.push(['TOTAL penalty', '', '', '', m.penalty || 0]);
  s3.push(['Source word count', '', '', '', m.source_word_count || 0]);
  s3.push(['Quality Score (MQM)', '', '', '', `${(m.quality_score || 0).toFixed(2)} %`]);
  s3.push([]);
  s3.push(['Distribution by severity']);
  s3.push(['Critical', m.counts?.critical || 0, '\u00d7 10', (m.counts?.critical || 0) * 10]);
  s3.push(['Major',    m.counts?.major    || 0, '\u00d7 5',  (m.counts?.major    || 0) * 5]);
  s3.push(['Minor',    m.counts?.minor    || 0, '\u00d7 1',  (m.counts?.minor    || 0) * 1]);

  const ws3 = XLSX.utils.aoa_to_sheet(s3);
  ws3['!cols'] = [
    { wch: 8 }, { wch: 18 }, { wch: 18 }, { wch: 10 }, { wch: 10 },
    { wch: 50 }, { wch: 32 }, { wch: 32 }, { wch: 40 },
  ];
  XLSX.utils.book_append_sheet(wb, ws3, 'MQM');

  // ── Sheet 4: Edit Distance ──
  const s4 = [
    ['Levenshtein distance: MT \u2192 PE (post-editing)'],
    ['Only for segments with a proposed post-edit. Normalized distance = distance / max(len(MT), len(PE)).'],
    [],
    ['Seg #', 'MT (current)', 'PE (proposed)', 'Lev. dist.', 'Norm. dist.'],
  ];
  const edited = (data.segments || []).filter((s) => s.lev_distance > 0);
  for (const seg of edited) {
    s4.push([
      seg.id, seg.target || '', seg.corrected || '',
      seg.lev_distance,
      (seg.lev_norm || 0).toFixed(3),
    ]);
  }
  s4.push([]);
  const avgDist = edited.length ? edited.reduce((s, e) => s + e.lev_distance, 0) / edited.length : 0;
  const avgNorm = edited.length ? edited.reduce((s, e) => s + e.lev_norm, 0) / edited.length : 0;
  s4.push(['Average', '', '', avgDist.toFixed(1), avgNorm.toFixed(3)]);
  s4.push(['Segments edited', edited.length, `of ${(data.segments || []).length} (${((data.segments || []).length ? (edited.length / data.segments.length * 100) : 0).toFixed(0)} %)`]);

  const ws4 = XLSX.utils.aoa_to_sheet(s4);
  ws4['!cols'] = [{ wch: 8 }, { wch: 50 }, { wch: 50 }, { wch: 12 }, { wch: 12 }];
  XLSX.utils.book_append_sheet(wb, ws4, 'Edit Distance');

  return wb;
}

async function exportXlsx(data, meta) {
  const wb = buildMqmWorkbook(data, meta);
  const blob = wbToBlob(wb);
  const fname = `${(meta.projectName || 'translation').replace(/[^\w\-]+/g, '_')}__MQM.xlsx`;
  window.saveAs(blob, fname);
}

function wbToBlob(wb) {
  const out = window.XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
  return new Blob([out], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
}

// ── DOCX ──────────────────────────────────────────────────────────────────
async function exportDocx(data, meta) {
  const d = window.docx;
  const correctedText = data.corrected_full_text
    || (data.segments || []).map((s) => s.corrected || s.target || '').join('\n\n');

  const paragraphs = [];

  // Title
  paragraphs.push(new d.Paragraph({
    text: meta.projectName || 'Corrected translation',
    heading: d.HeadingLevel.TITLE,
    spacing: { after: 240 },
  }));

  // Meta line
  paragraphs.push(new d.Paragraph({
    children: [new d.TextRun({
      text: `${data.source_language || '?'} \u2192 ${data.target_language || '?'}  \u00b7  MQM Quality Score ${(data.metrics?.quality_score || 0).toFixed(2)}%  \u00b7  Post-edited by APE (claude-opus-4-7)`,
      italics: true, color: '666666', size: 18,
    })],
    spacing: { after: 360 },
  }));

  // Body — split corrected text by blank-line paragraphs
  const blocks = correctedText.split(/\n{2,}/);
  for (const block of blocks) {
    paragraphs.push(new d.Paragraph({
      children: [new d.TextRun({ text: block, size: 22 })],
      spacing: { after: 180 },
    }));
  }

  const doc = new d.Document({
    creator: 'APE \u2014 Automatic Post-Editing',
    description: 'Corrected translation generated from MQM assessment',
    title: meta.projectName || 'Corrected translation',
    sections: [{ children: paragraphs }],
  });

  const blob = await d.Packer.toBlob(doc);
  const fname = `${(meta.projectName || 'translation').replace(/[^\w\-]+/g, '_')}__corrected.docx`;
  window.saveAs(blob, fname);
}

Object.assign(window, { exportXlsx, exportDocx });
