/**
 * GULPFILE.JS
 *
 * This file is required for the 'Critical CSS Generator' Drupal module.
 * It defines the Gulp tasks that generate critical CSS files.
 *
 * It is designed to be triggered by the Drupal module, which passes
 * a '--url' argument (e.g., --url=https://www.my-live-site.com).
 */

// Import required packages
const gulp = require('gulp');
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer');
// NOTE: 'critical' is now ESM-only with top-level await.
// Avoid require('critical'); dynamically import it within tasks instead.
// 'argv' will contain our parsed command-line arguments (like --url)
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const argv = yargs(hideBin(process.argv)).argv;

// --- CONFIGURATION ---
// IMPORTANT: Change these values to match your theme!

// 1. Path where CSS files should be saved, relative to this gulpfile.js
//    Your theme's 'css' folder is a good choice.
const themeAssetPath = './css/critical';

const drupalDocroot = path.resolve(__dirname, '../../../..');

// 1c. CSS sources to feed into Critical directly. Supplying these prevents
//     Critical from crawling <link> tags (and failing on dynamic URLs).
//     Use globs relative to the base (drupalDocroot) where suitable.
const colorsCssAbs = path.join(drupalDocroot, 'sites/default/files/toc_master_components_bg_col_css/colors.css');
const criticalCssSources = [
  // Theme global CSS (relative to theme cwd).
  '../wingsuit/*.css',
  './css/**/*.css',
  // Dynamic background colours CSS generated by the module (absolute path).
  colorsCssAbs,
];

console.log(`Critical base: ${drupalDocroot}`);
console.log(`Including colors CSS: ${colorsCssAbs}`);

// 2. The dimensions to scan.
const dimensions = [
  { width: 375, height: 812 }, // Mobile (iPhone X)
  { width: 1300, height: 900 }, // Desktop
];

// 3. The Base URL.
//    This code dynamically gets the URL from the '--url' argument
//    passed by the Drupal module.
//    If you run 'gulp' manually, it falls back to your local site.
//    !! CHANGE 'http://d9-westmidlandsrailway.lndo.site' to your local URL.
const baseUrl = (argv && argv.url) ? argv.url : 'https://www.westmidlandsrailway.co.uk';

console.log(`Using Base URL: ${baseUrl}`);

// --- GULP TASKS ---

// Shared Puppeteer launcher for Penthouse inside Docker/CI
function resolveChromeExecutable() {
  const candidates = [
    process.env.PUPPETEER_EXECUTABLE_PATH,
    '/usr/bin/chromium-browser',
    '/usr/bin/chromium',
    '/usr/bin/google-chrome',
    '/usr/bin/google-chrome-stable',
  ].filter(Boolean);
  for (const p of candidates) {
    try {
      if (fs.existsSync(p)) return p;
    } catch (_) {}
  }
  return undefined;
}

const chromeExecutable = resolveChromeExecutable();
const launchOpts = {
  args: ['--no-sandbox', '--disable-setuid-sandbox', '--ignore-certificate-errors'],
};
if (chromeExecutable) {
  launchOpts.executablePath = chromeExecutable;
}

// Keep a single Puppeteer browser alive for all tasks, but recreate it if the
// process crashes or disconnects. This avoids the hanging process while still
// preventing "WebSocket is not open" errors when a browser dies mid-run.
let sharedBrowserPromise;
async function getSharedBrowser() {
  if (sharedBrowserPromise) {
    try {
      const existing = await sharedBrowserPromise;
      if (existing && existing.isConnected()) {
        return existing;
      }
    } catch (_) {
      // fall through to relaunch
    }
  }

  sharedBrowserPromise = puppeteer.launch(launchOpts);
  const browser = await sharedBrowserPromise;
  browser.on('disconnected', () => {
    sharedBrowserPromise = null;
  });
  return browser;
}

async function closeSharedBrowser() {
  if (!sharedBrowserPromise) return;
  try {
    const browser = await sharedBrowserPromise;
    if (browser && browser.isConnected()) {
      await browser.close();
    }
  }
  catch (e) {
    console.warn('Warning: failed to close Puppeteer browser', e);
  }
}

const penthouseOpts = {
  puppeteer: {
    getBrowser: () => getSharedBrowser(),
  },
};

// Retry helper to handle occasional Chromium socket drops when tasks run in parallel.
async function runWithRetry(taskName, fn, retries = 1) {
  let attempt = 0;
  // eslint-disable-next-line no-constant-condition
  while (true) {
    try {
      return await fn();
    }
    catch (err) {
      const message = err && err.message ? err.message : String(err);
      const canRetry = attempt < retries && message.includes('WebSocket is not open');
      if (!canRetry) {
        throw err;
      }
      console.warn(`[${taskName}] WebSocket closed; restarting browser and retrying (attempt ${attempt + 2})`);
      await closeSharedBrowser();
      attempt += 1;
    }
  }
}

/**
 * Task: Homepage (Fallback)
 * This is the main fallback CSS for your site.
 * It runs on the homepage.
 * Generates: default-critical.css
 *
 * The Critical CSS module will use this file for all pages
 * that don't have a more-specific file.
 */
gulp.task('critical:homepage', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:homepage', () => generate({
    src: baseUrl, // The homepage URL
    target: { css: `${themeAssetPath}/front.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
    // You can add 'penthouse' options here if needed
    // See: https://github.com/penthouse/penthouse#options
  }));
});

/**
 * Task: Article Content Type
 * !! CHANGE '/path-to-real-article' to a real article path.
 * Generates: article.css
 *
 * In Drupal, you'll configure the Critical CSS module to use
 * this 'article.css' file for the 'Article' content type.
 */
gulp.task('critical:article', async () => {
  // !! IMPORTANT: Change this to a real, representative article on your site.
  const articlePath = '/blog/my-first-article'; // e.g., /node/123

  const { generate } = await import('critical');
  return runWithRetry('critical:article', () => generate({
    src: `${baseUrl}${articlePath}`, // e.g., https://www.mysite.com/blog/my-first-article
    target: { css: `${themeAssetPath}/article.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Basic Page Content Type
 * !! CHANGE '/path-to-real-page' to a real basic page path.
 * Generates: page.css
 *
 * In Drupal, you'll configure the Critical CSS module to use
 * this 'page.css' file for the 'Basic page' content type.
 */
gulp.task('critical:page', async () => {
  // !! IMPORTANT: Change this to a real, representative page on your site.
  const pagePath = '/about-us'; // e.g., /node/456

  const { generate } = await import('critical');
  return runWithRetry('critical:page', () => generate({
    src: `${baseUrl}${pagePath}`, // e.g., https://www.mysite.com/about-us
    target: { css: `${themeAssetPath}/page.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Routes page
 * Generates: route.css
 */
gulp.task('critical:routes', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:routes', () => generate({
    src: `${baseUrl}/train-times/lichfield-to-birmingham`,
    target: { css: `${themeAssetPath}/route.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Stations page
 * Generates: station.css
 */
gulp.task('critical:stations', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:stations', () => generate({
    src: `${baseUrl}/stations/birmingham-international`,
    target: { css: `${themeAssetPath}/station.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Destinations page
 * Generates: destination.css
 */
gulp.task('critical:destinations', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:destinations', () => generate({
    src: `${baseUrl}/destinations/trains-to-birmingham`,
    target: { css: `${themeAssetPath}/destination.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Live departures/arrivals
 * Generates: travel-information-journey-planning-live-departure-arrival-boards.css
 */
gulp.task('critical:live-departures-arrivals', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:live-departures-arrivals', () => generate({
    src: `${baseUrl}/travel-information/journey-planning/live-departure-arrival-boards`,
    target: { css: `${themeAssetPath}/travel-information-journey-planning-live-departure-arrival-boards.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Tickets & discounts
 * Generates: tickets-discounts.css
 */
gulp.task('critical:tickets-discounts', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:tickets-discounts', () => generate({
    src: `${baseUrl}/tickets-discounts`,
    target: { css: `${themeAssetPath}/tickets-discounts.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Off-peak train tickets
 * Generates: tickets-discounts-ticket-types-off-peak-train-tickets.css
 */
gulp.task('critical:off-peak-train-tickets', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:off-peak-train-tickets', () => generate({
    src: `${baseUrl}/tickets-discounts/ticket-types/off-peak-train-tickets`,
    target: { css: `${themeAssetPath}/tickets-discounts-ticket-types-off-peak-train-tickets.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Contact us
 * Generates: contact-us.css
 */
gulp.task('critical:contact-us', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:contact-us', () => generate({
    src: `${baseUrl}/contact-us`,
    target: { css: `${themeAssetPath}/contact-us.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Customer relations
 * Generates: contact-us-customer-relations.css
 */
gulp.task('critical:customer-relations', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:customer-relations', () => generate({
    src: `${baseUrl}/contact-us/customer-relations`,
    target: { css: `${themeAssetPath}/contact-us-customer-relations.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Service disruption
 * Generates: travel-information-journey-planning-service-disruption.css
 */
gulp.task('critical:service-disruption', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:service-disruption', () => generate({
    src: `${baseUrl}/travel-information/journey-planning/service-disruption`,
    target: { css: `${themeAssetPath}/travel-information-journey-planning-service-disruption.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Browse our careers
 * Generates: about-us-careers-browse-our-careers.css
 */
gulp.task('critical:browse-our-careers', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:browse-our-careers', () => generate({
    src: `${baseUrl}/about-us/careers/browse-our-careers`,
    target: { css: `${themeAssetPath}/about-us-careers-browse-our-careers.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

/**
 * Task: Train driver jobs
 * Generates: about-us-careers-browse-our-careers-train-driver-jobs.css
 */
gulp.task('critical:train-driver-jobs', async () => {
  const { generate } = await import('critical');
  return runWithRetry('critical:train-driver-jobs', () => generate({
    src: `${baseUrl}/about-us/careers/browse-our-careers/train-driver-jobs`,
    target: { css: `${themeAssetPath}/about-us-careers-browse-our-careers-train-driver-jobs.css` },
    dimensions: dimensions,
    penthouse: penthouseOpts,
  }));
});

// Add more tasks for other templates (e.g., 'critical:news', 'critical:event')
// just by copying the 'critical:article' task and changing the paths.

gulp.task(
  'critical:all',
  gulp.series(
    // Run sequentially to reduce browser churn in containerized environments.
    'critical:homepage',
    'critical:article',
    'critical:page',
    'critical:routes',
    'critical:stations',
    'critical:destinations',
    'critical:live-departures-arrivals',
    'critical:tickets-discounts',
    'critical:off-peak-train-tickets',
    'critical:contact-us',
    'critical:customer-relations',
    'critical:service-disruption',
    'critical:browse-our-careers',
    'critical:train-driver-jobs',
    async function cleanup() {
      await closeSharedBrowser();
    }
  )
);

gulp.task('default', async () => {
  // Use gulp.series to run critical:all, which in turn runs the parallel tasks.
  await gulp.series('critical:all')();
});
