import { chromium, type Browser, type BrowserContext } from 'playwright';

const VIEWPORT = { width: 1200, height: 630 };
const NAVIGATION_TIMEOUT = 15_000;
const TILE_BUFFER_MS = 500;
const MAX_CONCURRENT = 2;

let browser: Browser | null = null;
let concurrency = 0;
const queue: Array<{ resolve: () => void }> = [];

async function ensureBrowser(): Promise<Browser> {
  if (!browser || !browser.isConnected()) {
    browser = await chromium.launch({
      args: [
        '--no-sandbox',
        '--disable-dev-shm-usage',
        '--use-gl=angle',
        '--use-angle=swiftshader',
      ],
    });
  }
  return browser;
}

async function acquireSlot(): Promise<void> {
  if (concurrency < MAX_CONCURRENT) {
    concurrency++;
    return;
  }
  return new Promise<void>((resolve) => {
    queue.push({ resolve });
  });
}

function releaseSlot(): void {
  concurrency--;
  const next = queue.shift();
  if (next) {
    concurrency++;
    next.resolve();
  }
}

export async function takeScreenshot(url: string): Promise<Buffer> {
  await acquireSlot();
  let context: BrowserContext | null = null;

  try {
    const instance = await ensureBrowser();
    context = await instance.newContext({
      viewport: VIEWPORT,
      deviceScaleFactor: 1,
    });
    const page = await context.newPage();

    await page.goto(url, { waitUntil: 'networkidle', timeout: NAVIGATION_TIMEOUT });

    // Wait for the frontend to signal readiness
    try {
      await page.waitForFunction('window.__og_ready === true', {
        timeout: NAVIGATION_TIMEOUT,
      });
    } catch {
      // Proceed anyway — partial screenshot is better than nothing
    }

    // Extra buffer for map tiles to finish rendering
    await page.waitForTimeout(TILE_BUFFER_MS);

    const screenshot = await page.screenshot({ type: 'png' });
    return Buffer.from(screenshot);
  } finally {
    if (context) {
      await context.close().catch(() => {});
    }
    releaseSlot();
  }
}

export async function closeBrowser(): Promise<void> {
  if (browser) {
    await browser.close().catch(() => {});
    browser = null;
  }
}
