import { chromium } from 'playwright';
import { createServer } from 'node:http';
import { execFileSync } from 'node:child_process';
import { mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';

const root = process.cwd();
const dogCatRoot = process.env.DOGCAT_ROOT || path.join(root, 'aiAssets', 'DogCat3');
const waitMs = Number(process.env.RUNTIME_WAIT_MS || 9000);

const mraidStub = `
window.mraid = window.mraid || {
  getState: function(){ return 'default'; },
  isViewable: function(){ return true; },
  addEventListener: function(name, cb){ if (name === 'ready') setTimeout(cb, 0); },
  removeEventListener: function(){},
  open: function(url){ console.log('[stub mraid.open]', url); },
  close: function(){ console.log('[stub mraid.close]'); },
  supports: function(){ return false; }
};
`;

const exitApiStub = `
window.ExitApi = window.ExitApi || {
  exit: function(){ console.log('[stub ExitApi.exit]'); }
};
`;

async function collectCases(tempDir) {
  const cases = [];
  const networkFolders = [
    'Applovin',
    'Facebook',
    'Ironsource',
    'Moloco',
    'Unity',
    'GoogleAds',
    'Mintegral',
    'Vungle',
  ];
  for (const name of networkFolders) {
    const dir = path.join(dogCatRoot, name);
    if (!existsSync(dir)) continue;
    const entries = await readdir(dir);
    const htmlName = entries.find((entry) => /\.html?$/i.test(entry));
    if (htmlName) {
      cases.push([name, await readFile(path.join(dir, htmlName))]);
      continue;
    }
    const zipName = entries.find((entry) => /\.zip$/i.test(entry));
    if (!zipName) continue;
    const zip = path.join(dir, zipName);
    const out = path.join(tempDir, name);
    execFileSync('powershell', [
      '-NoProfile',
      '-Command',
      `Expand-Archive -LiteralPath '${zip.replaceAll("'", "''")}' -DestinationPath '${out.replaceAll("'", "''")}' -Force`,
    ], { stdio: 'ignore' });
    const expanded = await readdir(out);
    const indexName = expanded.find((entry) => entry.toLowerCase() === 'index.html');
    if (indexName) cases.push([name, await readFile(path.join(out, indexName))]);
  }
  return cases;
}

async function startServer(cases) {
  const html = new Map(cases.map(([name, body]) => [`/${name}/index.html`, body]));
  const server = createServer((req, res) => {
    const url = new URL(req.url || '/', 'http://127.0.0.1');
    if (url.pathname.endsWith('/mraid.js')) {
      res.writeHead(200, { 'content-type': 'application/javascript' });
      res.end(mraidStub);
      return;
    }
    if (url.pathname.endsWith('/exitapi.js')) {
      res.writeHead(200, { 'content-type': 'application/javascript' });
      res.end(exitApiStub);
      return;
    }
    const body = html.get(url.pathname);
    if (body) {
      res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
      res.end(body);
      return;
    }
    res.writeHead(404, { 'content-type': 'text/plain' });
    res.end('not found');
  });
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
  return {
    baseUrl: `http://127.0.0.1:${server.address().port}`,
    close: () => new Promise((resolve) => server.close(resolve)),
  };
}

function simplifyUrl(url) {
  return url.replace(/^https?:\/\/127\.0\.0\.1:\d+\//, '/');
}

async function runCase(browser, baseUrl, name) {
  const page = await browser.newPage({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 2, isMobile: true });
  const issues = [];

  page.on('pageerror', (error) => {
    issues.push({ type: 'pageerror', text: error.stack || error.message });
  });
  page.on('console', (message) => {
    if (['error', 'assert'].includes(message.type())) {
      const location = message.location();
      const suffix = location.url ? ` @ ${simplifyUrl(location.url)}:${location.lineNumber}:${location.columnNumber}` : '';
      issues.push({ type: `console.${message.type()}`, text: `${message.text()}${suffix}` });
    }
  });
  page.on('requestfailed', (request) => {
    issues.push({
      type: 'requestfailed',
      text: `${simplifyUrl(request.url())} :: ${request.failure()?.errorText || 'failed'}`,
    });
  });
  page.on('response', (response) => {
    if (response.status() >= 400) {
      issues.push({
        type: `http.${response.status()}`,
        text: simplifyUrl(response.url()),
      });
    }
  });

  await page.goto(`${baseUrl}/${name}/index.html`, { waitUntil: 'domcontentloaded', timeout: 20000 });
  await page.waitForTimeout(waitMs);

  const bodyText = await page.locator('body').evaluate((body) => body.innerText.slice(0, 2000)).catch(() => '');
  const canvasCount = await page.locator('canvas').count().catch(() => 0);
  const title = await page.title().catch(() => '');
  await page.close();

  return { issues, canvasCount, title, bodyText };
}

async function main() {
  const tempDir = await mkdtemp(path.join(tmpdir(), 'dogcat3-playwright-'));
  let server;
  let browser;
  try {
    const cases = await collectCases(tempDir);
    server = await startServer(cases);
    browser = await chromium.launch({ channel: 'msedge', headless: true });

    for (const [name] of cases) {
      const result = await runCase(browser, server.baseUrl, name);
      console.log(`\n## ${name}`);
      console.log(`canvas=${result.canvasCount}`);
      if (result.title) console.log(`title=${result.title}`);
      if (!result.issues.length) {
        console.log('No page errors, console errors, failed requests, or HTTP 4xx/5xx responses captured.');
      } else {
        for (const issue of result.issues) {
          console.log(`- ${issue.type}: ${issue.text.replace(/\s+/g, ' ').slice(0, 700)}`);
        }
      }
      if (result.bodyText && /error|exception|cannot|failed/i.test(result.bodyText)) {
        console.log(`body_error_text=${result.bodyText.replace(/\s+/g, ' ').slice(0, 700)}`);
      }
    }
  } finally {
    if (browser) await browser.close();
    if (server) await server.close();
    await rm(tempDir, { recursive: true, force: true });
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
