/**
 * SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

import prop from 'dot-prop';

import {Project} from '.';
import {BundlerCopyPluginState} from '../api/plugins';
import PkgDesc from '../pkg-desc';
import {BundlerPluginDescriptor} from './types';
import * as util from './util';

/**
 * Defines configuration for the copy step.
 */
export default class Copy {
	constructor(project: Project) {
		this._project = project;
	}

	/**
	 * Extra dependencies to add to the final bundle (in addition to those
	 * listed under the dependencies section of package.json).
	 */
	get includedDependencies(): string[] {
		const {npmbundlerrc} = this._project;

		return prop.get(npmbundlerrc, 'include-dependencies', []);
	}

	/**
	 * Get the configured file exclusions for a given package.
	 * @param pkg the package descriptor
	 * @return an array of glob expressions (as defined by `globby`)
	 */
	getExclusions(pkg: PkgDesc): string[] {
		const {npmbundlerrc} = this._project;

		let exclusions = npmbundlerrc['exclude'] || {};

		// If it is explicitly false, return an empty exclusions array

		if (
			exclusions[pkg.id] === false ||
			exclusions[pkg.name] === false ||
			exclusions['*'] === false
		) {
			return [];
		}

		// If it is explicitly true, return an array with '**/*'

		if (
			exclusions[pkg.id] === true ||
			exclusions[pkg.name] === true ||
			exclusions['*'] === true
		) {
			return ['**/*'];
		}

		// In any other case, return what's in the config

		exclusions =
			exclusions[pkg.id] || exclusions[pkg.name] || exclusions['*'] || [];

		return exclusions;
	}

	getPluginDescriptors(
		pkg: PkgDesc
	): BundlerPluginDescriptor<BundlerCopyPluginState>[] {
		const {_project} = this;

		const pkgConfig = util.getPackageConfig(
			_project,
			pkg,
			'copy-plugins',
			[]
		) as [];

		return util.createBundlerPluginDescriptors(_project, pkgConfig);
	}

	/**
	 * Filters out dependencies to bundle based on the global bundler config
	 * @param dependencies array of dependency names
	 * @return an array of dependency names
	 */
	filterDependencies(dependencies: string[]): string[] {
		const {globalConfig, pkgJson} = this._project;

		if (!globalConfig?.strictGlobalDependencies) {
			return dependencies;
		}

		if (!this._globalDependencyMap) {
			const dependencyMap = new Map();

			Object.entries(globalConfig?.imports).forEach(
				([pkgName, deps = {}]) => {
					dependencyMap.set(pkgName, pkgName);

					Object.keys(deps).forEach((dep) => {
						dependencyMap.set(dep, pkgName);
					});
				}
			);

			this._globalDependencyMap = dependencyMap;
		}

		return dependencies.filter(
			(dependency) =>
				!this._globalDependencyMap.has(dependency) ||
				(this._globalDependencyMap.has(dependency) &&
					this._globalDependencyMap.get(dependency) === pkgJson.name)
		);
	}

	private _globalDependencyMap: Map<string, string>;

	private readonly _project: Project;
}
