import type { Page, Locator, Frame, Response } from 'playwright';
import type { CsfLoginSession } from './sat-csf-login.js';

async function tryFetchPdfFromUrl(page: Page, url: string): Promise<Buffer | null> {
  if (url.startsWith('blob:') || url.startsWith('data:')) {
    const arr = await page.evaluate(async (u) => {
      const r = await fetch(u);
      const buf = await r.arrayBuffer();
      return Array.from(new Uint8Array(buf));
    }, url);
    return Buffer.from(arr);
  }
  if (url.startsWith('http')) {
    const response = await page.context().request.get(url);
    if (!response.ok()) return null;
    return Buffer.from(await response.body());
  }
  return null;
}

/**
 * Busca "Generar Constancia" en cualquiera de los frames del appPage (vive
 * típicamente en un iframe JSF legacy: rfcampc.siat.sat.gob.mx/PTSC/...).
 * Intenta 3 rutas: download event, popup con viewer, response interception.
 */
export async function extractCsfPdf(session: CsfLoginSession): Promise<Buffer> {
  const { context, appPage } = session;

  let interceptedPdf: Buffer | null = null;
  const responseListener = async (response: Response) => {
    const ct = response.headers()['content-type'] ?? '';
    if (ct.includes('application/pdf')) {
      try { interceptedPdf = Buffer.from(await response.body()); } catch { /* ok */ }
    }
  };
  context.on('response', responseListener);

  const GENERAR_SELECTORS = [
    'button:has-text("Generar Constancia")',
    'button:has-text("Generar constancia")',
    'input[type="button"][value*="Generar" i]',
    'input[type="submit"][value*="Generar" i]',
    'a:has-text("Generar Constancia")',
    'a:has-text("Generar constancia")',
  ].join(', ');

  let generarLocator: Locator | null = null;
  let foundFrame: Frame | null = null;
  const deadline = Date.now() + 90_000;
  while (Date.now() < deadline) {
    for (const frame of appPage.frames()) {
      const loc = frame.locator(GENERAR_SELECTORS).first();
      const count = await loc.count().catch(() => 0);
      if (count > 0 && await loc.isVisible().catch(() => false)) {
        generarLocator = loc;
        foundFrame = frame;
        break;
      }
    }
    if (generarLocator) break;
    await appPage.waitForTimeout(1000);
  }

  if (!generarLocator || !foundFrame) {
    context.off('response', responseListener);
    throw new Error('Botón "Generar Constancia" no encontrado en ningún frame del portal SAT (tras 90s)');
  }

  await generarLocator.scrollIntoViewIfNeeded();
  await appPage.waitForTimeout(500);

  const popupPromise = context.waitForEvent('page', { timeout: 15_000 }).catch(() => null);
  const downloadPromise = appPage.waitForEvent('download', { timeout: 15_000 }).catch(() => null);
  await generarLocator.click();

  const [popup, download] = await Promise.all([popupPromise, downloadPromise]);

  try {
    // Path 1: download event
    if (download) {
      const stream = await download.createReadStream();
      const chunks: Buffer[] = [];
      for await (const chunk of stream) chunks.push(chunk as Buffer);
      const pdf = Buffer.concat(chunks);
      if (!pdf.subarray(0, 5).toString().startsWith('%PDF-')) {
        throw new Error('El archivo descargado no es un PDF válido');
      }
      return pdf;
    }

    // Path 2: viewer popup
    if (popup) {
      await popup.waitForLoadState('domcontentloaded').catch(() => undefined);
      await popup.waitForTimeout(2000);

      let pdf = await tryFetchPdfFromUrl(popup, popup.url()).catch(() => null);
      if (!pdf) {
        const embedSrc = await popup.locator('embed[type="application/pdf"], iframe').first().getAttribute('src').catch(() => null);
        if (embedSrc) {
          const absolute = new URL(embedSrc, popup.url()).toString();
          pdf = await tryFetchPdfFromUrl(popup, absolute).catch(() => null);
        }
      }
      if (!pdf && interceptedPdf) pdf = interceptedPdf;
      if (!pdf || pdf.length === 0) throw new Error('El visor abrió pero no se pudo extraer el PDF');
      if (!pdf.subarray(0, 5).toString().startsWith('%PDF-')) throw new Error('Buffer extraído no es un PDF válido');
      return pdf;
    }

    // Path 3: inline response (no popup, no download)
    await appPage.waitForTimeout(3000);
    if (interceptedPdf) {
      const pdf = interceptedPdf as Buffer;
      if (!pdf.subarray(0, 5).toString().startsWith('%PDF-')) throw new Error('Buffer interceptado no es un PDF válido');
      return pdf;
    }
    throw new Error('Click en "Generar Constancia" no produjo descarga, popup ni respuesta PDF');
  } finally {
    context.off('response', responseListener);
  }
}
