/**
 * 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 fs from 'fs-extra';
import path from 'path';
import logger from '@docusaurus/logger';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {
  getVersionsFilePath,
  getVersionDocsDirPath,
  getVersionSidebarsPath,
  getDocsDirPathLocalized,
  readVersionsFile,
} from './versions/files';
import {validateVersionName} from './versions/validation';
import {loadSidebarsFileUnsafe} from './sidebars';
import {CURRENT_VERSION_NAME} from './constants';
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
import type {LoadContext} from '@docusaurus/types';

async function createVersionedSidebarFile({
  siteDir,
  pluginId,
  sidebarPath,
  version,
}: {
  siteDir: string;
  pluginId: string;
  sidebarPath: string | false | undefined;
  version: string;
}) {
  // Load current sidebar and create a new versioned sidebars file (if needed).
  // Note: we don't need the sidebars file to be normalized: it's ok to let
  // plugin option changes to impact older, versioned sidebars
  // We don't validate here, assuming the user has already built the version
  const sidebars = await loadSidebarsFileUnsafe(sidebarPath);

  // Do not create a useless versioned sidebars file if sidebars file is empty
  // or sidebars are disabled/false)
  const shouldCreateVersionedSidebarFile = Object.keys(sidebars).length > 0;

  if (shouldCreateVersionedSidebarFile) {
    await fs.outputFile(
      getVersionSidebarsPath(siteDir, pluginId, version),
      `${JSON.stringify(sidebars, null, 2)}\n`,
      'utf8',
    );
  }
}

// Tests depend on non-default export for mocking.
export async function cliDocsVersionCommand(
  version: unknown,
  {id: pluginId, path: docsPath, sidebarPath}: PluginOptions,
  {siteDir, i18n}: LoadContext,
): Promise<void> {
  // It wouldn't be very user-friendly to show a [default] log prefix,
  // so we use [docs] instead of [default]
  const pluginIdLogPrefix =
    pluginId === DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;

  try {
    validateVersionName(version);
  } catch (err) {
    logger.info`${pluginIdLogPrefix}: Invalid version name provided. Try something like: 1.0.0`;
    throw err;
  }

  const versions = (await readVersionsFile(siteDir, pluginId)) ?? [];

  // Check if version already exists.
  if (versions.includes(version)) {
    throw new Error(
      `${pluginIdLogPrefix}: this version already exists! Use a version tag that does not already exist.`,
    );
  }

  if (i18n.locales.length > 1) {
    logger.info`Versioned docs will be created for the following locales: name=${i18n.locales}`;
  }

  await Promise.all(
    i18n.locales.map(async (locale) => {
      const localizationDir = path.resolve(
        siteDir,
        i18n.path,
        i18n.localeConfigs[locale]!.path,
      );
      // Copy docs files.
      const docsDir =
        locale === i18n.defaultLocale
          ? path.resolve(siteDir, docsPath)
          : getDocsDirPathLocalized({
              localizationDir,
              pluginId,
              versionName: CURRENT_VERSION_NAME,
            });

      if (
        !(await fs.pathExists(docsDir)) ||
        (await fs.readdir(docsDir)).length === 0
      ) {
        if (locale === i18n.defaultLocale) {
          throw new Error(
            logger.interpolate`${pluginIdLogPrefix}: no docs found in path=${docsDir}.`,
          );
        } else {
          logger.warn`${pluginIdLogPrefix}: no docs found in path=${docsDir}. Skipping.`;
          return;
        }
      }

      const newVersionDir =
        locale === i18n.defaultLocale
          ? getVersionDocsDirPath(siteDir, pluginId, version)
          : getDocsDirPathLocalized({
              localizationDir,
              pluginId,
              versionName: version,
            });
      await fs.copy(docsDir, newVersionDir);
    }),
  );

  await createVersionedSidebarFile({
    siteDir,
    pluginId,
    version,
    sidebarPath,
  });

  // Update versions.json file.
  versions.unshift(version);
  await fs.outputFile(
    getVersionsFilePath(siteDir, pluginId),
    `${JSON.stringify(versions, null, 2)}\n`,
  );

  logger.success`name=${pluginIdLogPrefix}: version name=${version} created!`;
}
