// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
// Node module: @loopback/tsdocs
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import debugFactory from 'debug';
import fs from 'fs-extra';
import path from 'path';
import {
  ApiDocsOptions,
  DEFAULT_APIDOCS_EXTRACTION_PATH,
  DEFAULT_APIDOCS_GENERATION_PATH,
  getPackagesWithTsDocs,
  getUnscopedPackageName,
  LernaPackage,
} from './helper';

const debug = debugFactory('loopback:tsdocs');

/**
 * Update markdown files generated by api-documenter to prepend Jekyll metadata
 * and generate `apidocs/index.md`.
 *
 * @param options - Options for api docs
 */
export async function updateApiDocs(options: ApiDocsOptions = {}) {
  options = Object.assign(
    {
      rootDir: process.cwd(),
      apiDocsGenerationPath: DEFAULT_APIDOCS_GENERATION_PATH,
      apiDocsExtractionPath: DEFAULT_APIDOCS_EXTRACTION_PATH,
      generateDefaultPackageDoc: true,
    },
    options,
  );
  const packages = await getPackagesWithTsDocs(options.rootDir);

  /* istanbul ignore if  */
  if (!packages.length) return;

  const packagesByName: Record<string, LernaPackage> = {};

  for (const pkg of packages) {
    packagesByName[getUnscopedPackageName(pkg.name)] = pkg;
  }

  options.lernaPackages = packagesByName;

  const found = await addJekyllMetadata(packages[0].rootPath, options);
  if (found) {
    // await generateIndex(packages, options);
  }
}

/**
 * Prepend Jekyll metadata to markdown files
 *
 * @param lernaRootDir - Root directory for the monorepo
 * @param options - Options for api docs
 */
async function addJekyllMetadata(
  lernaRootDir: string,
  options: ApiDocsOptions,
) {
  const apiDocsRoot = path.join(lernaRootDir, options.apiDocsGenerationPath!);
  const exists = await fs.pathExists(apiDocsRoot);
  if (!exists) {
    console.error('No API docs found at %s.', apiDocsRoot);
    return false;
  }
  const apiFiles = await fs.readdir(apiDocsRoot);
  for (const f of apiFiles) {
    /* istanbul ignore if  */
    if (!f.endsWith('.md')) continue;
    const name = f.replace(/\.md$/, '');
    const isPackage = f.match(/^[^\.]+.md$/) && f !== 'index.md';

    /* istanbul ignore if  */
    if (!options.silent) {
      // Only print the package level name
      if (isPackage) {
        console.log('Updating *.md files for %s', name);
      }
    }

    const docFile = path.join(apiDocsRoot, f);
    let doc = await fs.readFile(docFile, 'utf-8');

    const pkgName = name.split('.')[0];
    // Calculate the relative uri for the package
    let relativeUri = `packages/${pkgName}`;
    const pkg = options.lernaPackages?.[pkgName];
    if (pkg != null) {
      relativeUri = path
        .relative(pkg.rootPath, pkg.location)
        .split(path.sep) // On Windows, the relative path has `\`
        .join('/');
    }
    const pkgUrl =
      f === 'index.md'
        ? 'https://github.com/loopbackio/loopback-next'
        : `https://github.com/loopbackio/loopback-next/tree/master/${relativeUri}`;

    if (isPackage && options.generateDefaultPackageDoc) {
      const modelFile = path.join(
        path.join(
          lernaRootDir,
          options.apiDocsExtractionPath!,
          `models/${name}.api.json`,
        ),
      );
      /**
       * "kind": "Package",
       * "canonicalReference": "@loopback/authentication",
       * "docComment": "",
       * "name": "@loopback/authentication",
       */
      const model = await fs.readJson(modelFile, {encoding: 'utf-8'});
      debug('Package %s', name, model);
      if (model.kind === 'Package' && !model.docComment) {
        const pkgDoc = `[${model.name}](${pkgUrl})`;
        doc = doc.replace(
          `## ${name} package`,
          `## ${name} package\n\n${pkgDoc}`,
        );
      }
    }

    doc = `---
lang: en
title: 'API docs: ${name}'
keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI
sidebar: lb4_sidebar
editurl: ${pkgUrl}
permalink: /doc/en/lb4/apidocs.${name}.html
---

${doc}
`;

    if (!options.dryRun) {
      await fs.writeFile(docFile, doc, 'utf-8');
    }
  }
  return true;
}
