'use strict';

const path = require('path');
const fs = require('fs-extra');
const globby = require('globby');

const { isWindows, formatDirsForMatcher, getSignatures, writeSignatures, cleanUp, onSuccess, onError, onProgress } = require('./utils');
const { dirs, symlinkDirs, copyDirs, symlinkFiles, ignoreMask } = require('./config');
const ROOT = path.resolve(__dirname, '..');


//@TODO: change signature to getSymlinks(source, options, signatures)
//		 here and elsewhere
//		 
//		 run symlinks twice, once for files (with nodir: true)
//		 once for dirs
async function getSymlinks(source, options, signatures) {
	const t1 = Date.now();
	const files = await globby(source, Object.assign({ cwd: ROOT }, options ));
	const filesDonePreviously = [];
	for (const [f, signature] of Object.entries(signatures)) {
		if ('isSymlinked' in signature && signature.isSymlinked) {
			try {
				await fs.access(path.join('build', f), fs.constants.F_OK);
				// file found in signatures and build/ dir, skip
				filesDonePreviously.push(f);
			} catch (_) {
				// file not found, needs symlinking
			}
		}
	}

	const filesToProcess = files.filter(f => !filesDonePreviously.includes(f));
	const filesProcessedCount = filesToProcess.length;

	var f;
	while ((f = filesToProcess.pop()) != null) {
		const dest = path.join('build', f);
		try {
			if (isWindows) {
				await fs.copy(f, dest, { dereference: true });
			} else {
				await fs.ensureSymlink(f, dest);
			}
			signatures[f] = {
				isSymlinked: true
			};
			onProgress(f, dest, 'ln');
		} catch (err) {
			throw new Error(`Failed on ${f}: ${err}`);
		}
	}

	const t2 = Date.now();

	return {
		action: 'symlink',
		count: filesProcessedCount,
		outFiles: files.map(f => path.join('build', f)), // also includes files that has already been symlinked
		totalCount: files.length,
		processingTime: t2 - t1
	};
}


module.exports = getSymlinks;

if (require.main === module) {
	(async () => {
		try {
			const source = symlinkFiles
				.concat(dirs.map(d => `${d}/**`))
				.concat([`!${formatDirsForMatcher(dirs)}/**/*.js`])
				.concat([`!${formatDirsForMatcher(dirs)}/**/*.jsx`])
				.concat([`!${formatDirsForMatcher(dirs)}/**/*.scss`])
				.concat([`!${formatDirsForMatcher(copyDirs)}/**`]);

			const signatures = await getSignatures();
			onSuccess(await getSymlinks(source, { nodir: true, ignore: ignoreMask }, signatures));
			onSuccess(await getSymlinks(symlinkDirs, {}, signatures));
			onSuccess(await cleanUp(signatures));
			await writeSignatures(signatures);
		} catch (err) {
			process.exitCode = 1;
			global.isError = true;
			onError(err);
		}
	})();
}