/*
 * Copyright (C) 2018-2021 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {existsSync, PathLike} from 'fs';
import {platform} from 'os';
import path from 'path';
import {Application, ProjectReflection} from 'typedoc';

/**
 * Get a project reflection from a path
 * @param sourcePath Path to get reflection from
 * @param excludeExternals Exclude external dependencies
 */
export function getProjectReflection(sourcePath: PathLike, excludeExternals = true): ProjectReflection {
  console.info(`Generating project reflection for ${sourcePath.toString()}.`);

  const tsconfigPath = getTsconfigPath(sourcePath.toString());

  // initialize new Typedoc application
  const app = new Application();

  app.bootstrap({
    excludeExternals: excludeExternals,
    ignoreCompilerErrors: true,
    includeDeclarations: true,
    tsconfig: path.join(tsconfigPath, 'tsconfig.json'),
  });

  let inputFilePath = sourcePath;
  if (inputFilePath === tsconfigPath) {
    inputFilePath = path.join(tsconfigPath, 'src');
  }

  // get input files
  const inputFiles = app.expandInputFiles([inputFilePath.toString()]);

  // get project reflection from input files
  const result = app.convert(inputFiles);

  if (result === undefined) {
    throw new TypeError('Project reflection could not be generated.');
  }

  return result;
}

/**
 * Get path that contains a tsconfig.json
 * @param startPath Path from where to start searching "upwards"
 */
export function getTsconfigPath(startPath: string): string {
  let tsconfigPath = startPath;

  // see https://stackoverflow.com/questions/9652043/identifying-the-file-system-root-with-node-js
  const root = platform() === 'win32' ? process.cwd().split(path.sep)[0] : '/';

  // repeat until a tsconfig.json is found
  while (!existsSync(path.join(tsconfigPath, 'tsconfig.json'))) {
    if (tsconfigPath === root) {
      throw new Error(
        `Reached file system root ${root} while searching for 'tsconfig.json' in ${startPath}!`,
      );
    }

    // pop last directory
    const tsconfigPathParts = tsconfigPath.split(path.sep);
    tsconfigPathParts.pop();
    tsconfigPath = tsconfigPathParts.join(path.sep);
  }

  console.info(`Using 'tsconfig.json' from ${tsconfigPath}.`);

  return tsconfigPath;
}
