import { execFileSync } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";
import { expect } from "e2e";
import { test, WEB } from "./t3.ts";

// Synthetic OOXML equivalent to a heading, sentence, 300 populated five-column rows,
// then a closing heading and paragraph. No external programs or private data required.
const fixture = String.raw`
import io, zipfile, sys
w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'
def p(text,style=''):
 return '<w:p>'+('<w:pPr><w:pStyle w:val="'+style+'"/></w:pPr>' if style else '')+'<w:r><w:t>'+text+'</w:t></w:r></w:p>'
body=p('Quarterly inventory','Heading1')+p('This report contains all 300 inventory rows.')
body+='<w:tbl><w:tblPr><w:tblW w:w="9000" w:type="dxa"/><w:tblBorders>'+''.join('<w:'+s+' w:val="single" w:sz="4" w:color="999999"/>' for s in ['top','left','bottom','right','insideH','insideV'])+'</w:tblBorders></w:tblPr><w:tblGrid>'+('<w:gridCol w:w="1800"/>'*5)+'</w:tblGrid>'
for r in range(1,301):
 body+='<w:tr>'+''.join('<w:tc><w:tcPr><w:tcW w:w="1800" w:type="dxa"/></w:tcPr>'+p('Row %03d / %d'%(r,c))+'</w:tc>' for c in range(1,6))+'</w:tr>'
body+='</w:tbl>'+p('Closing section','Heading1')+p('All 300 rows are present. End of inventory report.')+'<w:sectPr><w:pgSz w:w="12240" w:h="15840"/><w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720"/></w:sectPr>'
large=len(sys.argv)>1
if large:
 body=p('Long document format sample','S1499')
 for r in range(1,6000):
  body+='<w:p><w:pPr><w:pStyle w:val="S1499"/>'+('<w:numPr><w:ilvl w:val="0"/><w:numId w:val="60"/></w:numPr>' if r<4 else '')+'</w:pPr><w:r><w:t>'+('Numbered item '+str(r) if r<4 else ('Paragraph 6000: END OF LONG DOCUMENT' if r==5999 else 'Paragraph %04d: retained content.'%(r+1)))+'</w:t></w:r></w:p>'
 body+='<w:sectPr><w:pgSz w:w="12240" w:h="15840"/><w:pgMar w:top="720" w:right="720" w:bottom="720" w:left="720"/></w:sectPr>'
out=io.BytesIO()
with zipfile.ZipFile(out,'w',zipfile.ZIP_DEFLATED) as z:
 z.writestr('[Content_Types].xml','<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/></Types>')
 z.writestr('_rels/.rels','<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>')
 z.writestr('word/document.xml','<w:document xmlns:w="'+w+'"><w:body>'+body+'</w:body></w:document>')
 if not large: z.writestr('word/_rels/document.xml.rels','<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>')
 if not large: z.writestr('word/styles.xml','<w:styles xmlns:w="'+w+'"><w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/><w:rPr><w:b/><w:color w:val="245A81"/><w:sz w:val="32"/></w:rPr></w:style></w:styles>')
 if large:
  z.writestr('word/styles.xml','<w:styles xmlns:w="'+w+'">'+''.join('<w:style w:type="paragraph" w:styleId="S'+str(i)+'"><w:name w:val="Style '+str(i)+'"/><w:rPr><w:b/><w:color w:val="245A81"/><w:sz w:val="24"/></w:rPr></w:style>' for i in range(1500))+'</w:styles>')
  z.writestr('word/numbering.xml','<w:numbering xmlns:w="'+w+'">'+''.join('<w:abstractNum w:abstractNumId="'+str(i)+'">'+''.join('<w:lvl w:ilvl="'+str(l)+'"><w:start w:val="1"/><w:numFmt w:val="decimal"/><w:lvlText w:val="Item %'+str(l+1)+')"/><w:pPr><w:ind w:left="1440" w:hanging="720"/></w:pPr></w:lvl>' for l in range(9))+'</w:abstractNum><w:num w:numId="'+str(i)+'"><w:abstractNumId w:val="'+str(i)+'"/></w:num>' for i in range(1,61))+'</w:numbering>')
  z.writestr('word/_rels/document.xml.rels','<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/></Relationships>')
sys.stdout.buffer.write(out.getvalue())
`;

test('Word preview retains the 300-row table and its closing section', WEB, async ({ t3, app, screen, browser }) => {
  const capture = async (label: string) => {
    let previous = await app.screenshot(`${label}-settling`);
    for (let attempt = 0; attempt < 12; attempt++) {
      await new Promise((resolve) => setTimeout(resolve, 500));
      const current = await app.screenshot(label);
      if ((await readFile(previous)).equals(await readFile(current))) return;
      previous = current;
    }
    throw new Error(`The ${label} capture did not settle`);
  };
  const project = await t3.project('Word preview evidence');
  await writeFile(`${project}/inventory.docx`, execFileSync('python3', ['-c', fixture]));
  await writeFile(`${project}/long-document.docx`, execFileSync('python3', ['-c', fixture, 'large']));
  await t3.thread({ project, title: 'Inventory report', messages: ['Review the inventory report.'] });
  await t3.open();
  await screen.getByTestId('sidebar-row-card').first().tap();
  await screen.getByRole('button', 'Toggle right panel', { exact: true }).tap();
  await screen.getByRole('button', 'Files F', { exact: true }).tap();
  await screen.getByRole('treeitem', 'inventory.docx', { exact: true }).tap();
  await screen.getByRole('button', 'Maximize panel', { exact: true }).tap();
  await browser.locator('[data-office-pages] canvas').first().waitFor({ state: 'visible', timeout: 120000 });
  await screen.getByText(/^\d+ pages · Fit to width$/).first().waitFor({ timeout: 120000 });
  await capture('table-opening');
  await browser.evaluate(() => { const el = document.querySelector('[data-office-pages]')!; const last = Array.from(el.querySelectorAll('[role=group]')).at(-1)!; el.scrollTop = (last as HTMLElement).offsetTop - 24; });
  await browser.locator('[data-office-pages] [role=group] canvas[width]:not([width="0"])').last().waitFor({ timeout: 120000 });
  await expect.soft(screen.getByText('All 300 rows are present. End of inventory report.', { exact: false })).toBeVisible({ timeout: 30000 });
  await capture('table-final-section');
  await expect.soft(screen.getByText('Preview limited', { exact: false })).toBeHidden();
  await screen.getByRole('treeitem', 'long-document.docx', { exact: true }).tap();
  await browser.locator('[data-office-pages] canvas').first().waitFor({ state: 'visible', timeout: 120000 });
  await screen.getByText(/^\d+ pages · Fit to width$/).first().waitFor({ timeout: 120000 });
  await browser.locator('[data-office-pages] span').filter({ hasText: 'Long document format sample' }).first().waitFor({ timeout: 120000 });
  await capture('long-document-styles-numbering');
  await browser.evaluate(() => { const el = document.querySelector('[data-office-pages]')!; const last = Array.from(el.querySelectorAll('[role=group]')).at(-1)!; el.scrollTop = (last as HTMLElement).offsetTop - 24; });
  await browser.locator('[data-office-pages] [role=group] canvas[width]:not([width="0"])').last().waitFor({ timeout: 120000 });
  await expect.soft(screen.getByText('Paragraph 6000: END OF LONG DOCUMENT', { exact: false })).toBeVisible({ timeout: 30000 });
  await capture('long-document-final-section');
});
