import { describe, it, expect } from 'vitest';
import { writeFileSync, mkdtempSync, existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { chromium } from 'playwright';
import { runPageActions, executeTool, setSessionManager } from './browser.js';

// Real-browser screenshot behavior. Requires a launchable Chromium.
// CONTAINER=1 disables the Chromium sandbox (bwrap absent in this environment).
const CONTAINER = process.env['CONTAINER'] === '1';

/** Parse width/height (px) from a PNG file's IHDR chunk. */
function pngSize(path: string): { width: number; height: number } {
  const buf = readFileSync(path);
  // 8-byte signature, then IHDR: len(4) + "IHDR"(4) + width(4 BE) + height(4 BE)
  return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
}

function writeTallPage(dir: string, heightPx: number): string {
  const file = join(dir, 'tall.html');
  writeFileSync(
    file,
    `<html><body style="margin:0"><div style="height:${heightPx}px;` +
      `background:linear-gradient(#f00,#00f)"></div></body></html>`,
  );
  return file;
}

describe.skipIf(!CONTAINER)('BrowseWeb segmented screenshots (requires CONTAINER=1)', () => {
  it('splits a tall page into multiple viewport-sized images by default', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-'));
    const file = writeTallPage(dir, 6000);
    const ctx = { workspacePath: dir, toolsConfig: {} } as any;

    const r = await runPageActions(
      `file://${file}`,
      [{ type: 'screenshot', value: 'shot.png' }],
      ctx,
    );

    // Default is segmented → more than one image for a 6000px page.
    expect(r.screenshots.length).toBeGreaterThanOrEqual(2);
    // Zero-padded, sequential, suffixed names under output/.
    expect(r.screenshots[0]).toBe('output/shot-001.png');
    expect(r.screenshots[1]).toBe('output/shot-002.png');
    const sizes = r.screenshots.map((rel) => {
      const p = join(dir, rel);
      expect(existsSync(p)).toBe(true);
      return pngSize(p);
    });
    const h0 = sizes[0].height;
    expect(h0).toBeGreaterThan(0);
    // Every tile shares the viewport width.
    for (const s of sizes) expect(s.width).toBe(sizes[0].width);
    // The leading tiles are full viewport-height slices; the trailing tile may be
    // shorter (remaining content only, no blank padding). None exceed a full tile.
    expect(sizes[1].height).toBe(h0);
    for (const s of sizes) {
      expect(s.height).toBeGreaterThan(0);
      expect(s.height).toBeLessThanOrEqual(h0);
    }
    // Combined coverage spans well beyond a single screen (tall page captured).
    const totalCaptured = sizes.reduce((sum, s) => sum + s.height, 0);
    expect(totalCaptured).toBeGreaterThan(2 * h0);
    expect(r.consoleErrors).toEqual([]);
  }, 60000);

  it('keeps a short page as a single file with the original name', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-short-'));
    const file = writeTallPage(dir, 200);
    const ctx = { workspacePath: dir, toolsConfig: {} } as any;

    const r = await runPageActions(
      `file://${file}`,
      [{ type: 'screenshot', value: 'shot.png' }],
      ctx,
    );

    expect(r.screenshots).toEqual(['output/shot.png']);
    expect(existsSync(join(dir, 'output/shot.png'))).toBe(true);
  }, 60000);

  it('caps the number of segments at maxSegments', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-cap-'));
    const file = writeTallPage(dir, 100000);
    const ctx = { workspacePath: dir, toolsConfig: {} } as any;

    const r = await runPageActions(
      `file://${file}`,
      [{ type: 'screenshot', value: 'shot.png', maxSegments: 2 }],
      ctx,
    );

    expect(r.screenshots).toHaveLength(2);
    expect(r.screenshots).toEqual(['output/shot-001.png', 'output/shot-002.png']);
  }, 60000);

  it('capping to a single segment still yields a bounded viewport tile, not a full-page image', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-cap1-'));
    const file = writeTallPage(dir, 6000);
    const ctx = { workspacePath: dir, toolsConfig: {} } as any;

    const r = await runPageActions(
      `file://${file}`,
      [{ type: 'screenshot', value: 'shot.png', maxSegments: 1 }],
      ctx,
    );

    // maxSegments:1 must NOT fall back to an unbounded full-page capture.
    expect(r.screenshots).toEqual(['output/shot-001.png']);
    const { height } = pngSize(join(dir, 'output/shot-001.png'));
    expect(height).toBeGreaterThan(0);
    expect(height).toBeLessThan(3000); // one viewport tile, not the whole 6000px page
  }, 60000);

  it('BrowseWithSession screenshot segments a tall page, consistent with BrowseWeb', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-session-'));
    const file = writeTallPage(dir, 6000);
    const browser = await chromium.launch({ args: ['--no-sandbox'] });
    const context = await browser.newContext();
    const fakeSession = { id: 'sess-1', context };
    const fakeSm = {
      getSession: (id: string) => (id === 'sess-1' ? fakeSession : null),
      touchSession: () => {},
    };
    setSessionManager(fakeSm as never);
    try {
      const r = await executeTool(
        'BrowseWithSession',
        { sessionId: 'sess-1', url: `file://${file}`, action: 'screenshot' },
        { workspacePath: dir, toolsConfig: {} } as never,
      );
      expect(r.isError).toBeFalsy();
      const names = [...r.output.matchAll(/output\/(screenshot-[^\s,]+\.png)/g)].map((m) => m[1]);
      // A 6000px page must yield multiple viewport-sized segments (not one tall image).
      expect(names.length).toBeGreaterThanOrEqual(2);
      expect(names[0]).toMatch(/-001\.png$/);
      for (const name of names) expect(existsSync(join(dir, 'output', name))).toBe(true);
    } finally {
      setSessionManager(null);
      await browser.close();
    }
  }, 60000);

  it('produces one tall full-page image when segments is false', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'seg-off-'));
    const file = writeTallPage(dir, 6000);
    const ctx = { workspacePath: dir, toolsConfig: {} } as any;

    const r = await runPageActions(
      `file://${file}`,
      [{ type: 'screenshot', value: 'shot.png', segments: false }],
      ctx,
    );

    expect(r.screenshots).toEqual(['output/shot.png']);
    const { height } = pngSize(join(dir, 'output/shot.png'));
    // Full-page capture of a 6000px page is much taller than one viewport.
    expect(height).toBeGreaterThan(3000);
  }, 60000);
});
