import { describe, it, expect } from 'vitest';
import { writeFileSync, mkdtempSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import * as http from 'node:http';
import { runPageActions } from './browser.js';

// CONTAINER=1 disables Chromium sandbox (bwrap absent in this environment).
// Tests that require a real browser skip unless CONTAINER=1 is set.
const CONTAINER = process.env['CONTAINER'] === '1';

describe('runPageActions', () => {
  it('opens a local file and extracts text', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'rpa-'));
    const file = join(dir, 'p.html');
    writeFileSync(file, '<html><body><h1 data-testid="t">hello-rpa</h1></body></html>');
    const ctx = { workspacePath: dir } as any;
    const r = await runPageActions(`file://${file}`, [
      { type: 'getText', selector: '[data-testid="t"]' },
    ], ctx);
    expect(r.texts.join(' ')).toContain('hello-rpa');
    expect(r.consoleErrors).toEqual([]);
  }, 30000);
});

// ---------------------------------------------------------------------------
// Loopback SSRF tests — require CONTAINER=1 (Chromium sandbox disabled)
// ---------------------------------------------------------------------------

describe.skipIf(!CONTAINER)('runPageActions loopback SSRF (requires CONTAINER=1)', () => {
  /**
   * Start a minimal HTTP server on 127.0.0.1 serving a page with a known
   * marker element, then tear it down after the test.
   */
  function startLoopbackServer(): Promise<{ port: number; close: () => Promise<void> }> {
    return new Promise((resolve, reject) => {
      const server = http.createServer((_req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end('<html><body><h1 data-testid="t">loopback-ok</h1></body></html>');
      });
      server.listen(0, '127.0.0.1', () => {
        const addr = server.address() as { port: number };
        const close = () =>
          new Promise<void>((res, rej) => server.close((e) => (e ? rej(e) : res())));
        resolve({ port: addr.port, close });
      });
      server.on('error', reject);
    });
  }

  it(
    'isolatedContext + extraAllowedHosts allows loopback navigation and returns page text',
    async () => {
      const { port, close } = await startLoopbackServer();
      const dir = mkdtempSync(join(tmpdir(), 'rpa-loopback-'));
      const ctx = { workspacePath: dir, toolsConfig: {} } as any;

      try {
        const r = await runPageActions(
          `http://localhost:${port}/`,
          [{ type: 'getText', selector: '[data-testid="t"]' }],
          ctx,
          { isolatedContext: true, extraAllowedHosts: ['localhost', '127.0.0.1'] },
        );
        expect(r.texts.join('\n')).toContain('loopback-ok');
        expect(r.consoleErrors).toEqual([]);
      } finally {
        await close();
      }
    },
    60000,
  );

  it(
    'without opts (normal BrowseWeb path) localhost is SSRF-blocked',
    async () => {
      const { port, close } = await startLoopbackServer();
      const dir = mkdtempSync(join(tmpdir(), 'rpa-ssrf-'));
      const ctx = { workspacePath: dir, toolsConfig: {} } as any;

      try {
        // runPageActions without opts — should throw SSRF blocked
        await expect(
          runPageActions(
            `http://localhost:${port}/`,
            [{ type: 'getText', selector: '[data-testid="t"]' }],
            ctx,
          ),
        ).rejects.toThrow(/SSRF blocked/i);
      } finally {
        await close();
      }
    },
    60000,
  );
});
