import { waitForMeteorOutput } from "./helpers";
import { testMeteorRspackBundler } from './test-helpers';
import { assertBodyStyles, assertMetaTags } from "./assertions";

describe('R.Router App Bundling /', () => {
  describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({
    appName: 'react-router',
    port: 3142,
    filePaths: { 
      client: 'client/main.jsx', 
      server: 'server/main.js',
      test: 'tests/main.app-test.js',
    },
    testFullApp: true,
    checkBundleFilePaths: [
      'programs/web.browser/app/1x1.png',
      'programs/web.browser/app/images/1x1.png',
      'programs/web.browser/app/docs/text.md',
      'programs/web.browser.legacy/app/1x1.png',
      'programs/web.browser.legacy/app/images/1x1.png',
      'programs/web.browser.legacy/app/docs/text.md',
    ],
    beforeAllBehavior: async () => {
      process.env.METEOR_PACKAGE_DIRS = './my-packages';
    },
    afterAllBehavior: async () => {
      process.env.METEOR_PACKAGE_DIRS = '';
    },
    customAssertions: {
      afterInit: async ({ result }) => {
        await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/);
      },
      afterRun: async ({ result, port }) => {
        await waitForReactEnvs(result.outputLines, { isTsxEnabled: true });
        // negated as cached output (babel.config.js)
        await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/, { negate: true });
        await assert404Page(port);
        // Less styles support
        await assertBodyStyles({
          'white-space': 'break-spaces',
        });
        // Meteor modules config
        await assertBodyStyles({
          'align-content': 'center',
        });
        // Custom html rspack plugin options
        await assertMetaTags({
          'theme-color': '#4285f4',
        });
        // default-package loading
        await waitForMeteorOutput(result.outputLines, /.*default-package loaded.*/);
        // custom-package loading
        await waitForMeteorOutput(result.outputLines, /.*custom-package loaded.*/);
        // resolve.extensions loading
        await waitForMeteorOutput(result.outputLines, /.*first\.jsx loaded.*/);
        // Check custom plugin gets loaded from rspack.config.override.js file
        await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/);
      },
      afterRunRebuildClient: async ({ allConsoleLogs }) => {
        // Check for HMR output as enabled by default
        await waitForMeteorOutput(allConsoleLogs, /.*HMR.*Updated modules:.*/);
      },
      afterRunProduction: async ({ result, port }) => {
        await waitForReactEnvs(result.outputLines, { isTsxEnabled: true });
        await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/);
        await assert404Page(port, { isProductionMode: true });
        // Less styles support
        await assertBodyStyles({
          'white-space': 'break-spaces',
        });
        // Meteor modules config
        await assertBodyStyles({
          'align-content': 'center',
        });
        // Custom html rspack plugin options
        await assertMetaTags({
          'theme-color': '#4285f4',
        });
      },
      afterRunProductionRebuildClient: async ({ allConsoleLogs }) => {
        // Check for HMR to not be enabled in production-like mode
        await waitForMeteorOutput(allConsoleLogs, /.*HMR.*Updated modules:*/, { negate: true });
      },
      afterTest: async ({ result }) => {
        await waitForReactEnvs(result.outputLines);
        // Check custom plugin gets loaded from rspack.config.override.js file
        await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/);
      },
      afterTestOnce: async ({ result }) => {
        await waitForReactEnvs(result.outputLines);
      },
      afterBuild: async ({ result }) => {
        await waitForReactEnvs(result.outputLines, { isTsxEnabled: true });
        await waitForMeteorOutput(result.outputLines, /.*babel-plugin-react-compiler.*/);
        // Check custom plugin gets loaded from rspack.config.override.js file
        await waitForMeteorOutput(result.outputLines, /.*CustomConsoleLogPlugin.*/);
      },
    }
  }));
});

/**
 * Helper function to wait for React environment output from both Rspack Client and Server
 * @param {string[]} outputLines - Array that will be populated with output lines
 * @param {Object} options - Options for waiting
 * @param {number} options.timeout - Maximum time to wait in milliseconds
 * @param {number} options.checkInterval - Interval between checks in milliseconds
 * @returns {Promise<void>} - A promise that resolves when react envs are enabled
 */
export async function waitForReactEnvs(outputLines, options = {}) {
  await waitForMeteorOutput(
    outputLines,
    /.*isReactEnabled:.*true.*/,
    options
  );
  if (options.isTsxEnabled) {
    await waitForMeteorOutput(
      outputLines,
      /.*isTsxEnabled:.*true.*/,
      options
    );
  }
}

/**
 * Helper function to assert that the 404 page is working correctly
 * @param {number} port - Port where the app is running
 * @param {Object} options - Options for the assertion
 * @param {boolean} options.isProductionMode - Whether the app is running in production mode
 * @returns {Promise<void>} - A promise that resolves when the assertion is complete
 */
async function assert404Page(port, options = {}) {
  const { isProductionMode = false } = options;
  const modeText = isProductionMode ? 'in production mode' : '';

  // Test 404 page
  console.log(`Testing 404 page${modeText ? ' ' + modeText : ''}...`);
  await page.goto(`http://localhost:${port}/not-found`);

  // Check for 404 message
  await page.waitForSelector('h1');
  const notFoundText = await page.$eval('h1', el => el.textContent);
  expect(notFoundText).toBe('404 - Page Not Found');

  // Check for additional text
  await page.waitForSelector('p');
  const paragraphText = await page.$eval('p', el => el.textContent);
  expect(paragraphText).toBe('The page you are looking for does not exist.');

  console.log(`✅ 404 page test passed${modeText ? ' ' + modeText : ''}`);
}
