/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import path from 'path';
import fs from 'fs-extra';
import {fileURLToPath} from 'url';
import logger from '@docusaurus/logger';

import classicTheme from '@docusaurus/theme-classic';

// Unsafe imports
import {
  readComponentNames,
  getMissingIntermediateComponentFolderNames,
} from '@docusaurus/core/lib/commands/swizzle/components.js';
import {normalizeSwizzleConfig} from '@docusaurus/core/lib/commands/swizzle/config.js';
import {wrap, eject} from '@docusaurus/core/lib/commands/swizzle/actions.js';

const swizzleConfig = normalizeSwizzleConfig(classicTheme.getSwizzleConfig());

/** @type {"eject" | "wrap"} */
const action = process.env.SWIZZLE_ACTION ?? 'eject';
const typescript = process.env.SWIZZLE_TYPESCRIPT === 'true';

const dirname = path.dirname(fileURLToPath(import.meta.url));

const classicThemePathBase = path.join(
  dirname,
  '../../packages/docusaurus-theme-classic',
);

const themePath = typescript
  ? path.join(classicThemePathBase, 'src/theme')
  : path.join(classicThemePathBase, 'lib/theme');

const toPath = path.join(dirname, '_swizzle_theme_tests');

console.log('\n');
console.log('Swizzle test script');
console.log('Args', {
  action,
  typescript,
  dirname,
  themePath,
  toPath,
  swizzleConfig,
});
console.log('\n');

await fs.remove(toPath);

function filterComponentNames(componentNames) {
  // TODO temp workaround: non-comps should be forbidden to wrap
  if (action === 'wrap') {
    const WrapBlocklist = [
      'Layout', // Due to theme-fallback?
    ];

    return componentNames.filter((componentName) => {
      const blocked = WrapBlocklist.includes(componentName);
      if (blocked) {
        logger.warn(`${componentName} is blocked and will not be wrapped`);
      }
      return !blocked;
    });
  }
  return componentNames;
}

async function getAllComponentNames() {
  const names = await readComponentNames(themePath);
  const allNames = names.concat(
    await getMissingIntermediateComponentFolderNames(names),
  );
  return filterComponentNames(allNames);
}

const componentNames = await getAllComponentNames();

const componentsNotFound = Object.keys(swizzleConfig.components).filter(
  (componentName) => !componentNames.includes(componentName),
);
if (componentsNotFound.length > 0) {
  logger.error(
    `${
      componentsNotFound.length
    } components exist in the swizzle config but do not exist in practice.
Please double-check or clean up these components from the config:
- ${componentsNotFound.join('\n- ')}
`,
  );
  process.exit(1);
}

/**
 * @param {string} componentName
 */
function getActionStatus(componentName) {
  const actionStatus =
    swizzleConfig.components[componentName]?.actions[action] ?? 'unsafe';
  return actionStatus;
}

for (const componentName of componentNames) {
  const executeAction = () => {
    const baseParams = {
      action,
      siteDir: toPath,
      themePath,
      componentName,
      typescript,
    };
    switch (action) {
      case 'wrap':
        return wrap({
          ...baseParams,
          importType: 'init', // For these tests, "theme-original" imports are causing an expected infinite loop
        });
      case 'eject':
        return eject(baseParams);
      default:
        throw new Error(`Unknown action: ${action}`);
    }
  };

  const actionStatus = getActionStatus(componentName);

  if (actionStatus === 'forbidden') {
    logger.warn(
      `${componentName} is marked as forbidden for action ${action} => skipping`,
    );
    continue;
  }

  const result = await executeAction();

  const safetyLog =
    actionStatus === 'unsafe' ? logger.red('unsafe') : logger.green('safe');

  console.log(
    `${componentName} ${action} (${safetyLog}) => ${
      result.createdFiles.length
    } file${result.createdFiles.length > 1 ? 's' : ''} written`,
  );
}

logger.success(`
End of the Swizzle test script.
Now try to build the site and see if it works!
`);

const componentsWithMissingConfigs = componentNames.filter(
  (componentName) => !swizzleConfig.components[componentName],
);

// TODO require theme exhaustive config, fail fast?
// (at least for our classic theme?)
// TODO provide util so that theme authors can also check exhaustiveness?
if (componentsWithMissingConfigs.length > 0) {
  logger.warn(
    `${componentsWithMissingConfigs.length} components have no swizzle config.
Sample: ${componentsWithMissingConfigs.slice(0, 5).join(', ')} ...
`,
  );
}
