import process__default from 'node:process';
import path__default from 'node:path';
import fs__default, { promises } from 'node:fs';

const resolvePath = (cwd, sourcePath, destinationPath) => {
	sourcePath = path__default.resolve(cwd, sourcePath);
	destinationPath = path__default.resolve(cwd, destinationPath);

	return {
		sourcePath,
		destinationPath,
	};
};

const validatePathsExist = (sourcePath, destinationPath, suffix = 'Path') => {
	if (!sourcePath || !destinationPath) {
		throw new TypeError(`\`source${suffix}\` and \`destination${suffix}\` required`);
	}
};

const validateSameDirectory = (source, destination) => {
	if (path__default.dirname(source) !== path__default.dirname(destination)) {
		throw new Error('`source` and `destination` must be in the same directory');
	}
};

const _moveFile = async (sourcePath, destinationPath, {overwrite = true, cwd = process__default.cwd(), directoryMode, validateDirectory = false} = {}) => {
	if (cwd) {
		({sourcePath, destinationPath} = resolvePath(cwd, sourcePath, destinationPath));
	}

	if (validateDirectory) {
		validateSameDirectory(sourcePath, destinationPath);
	}

	if (!overwrite && fs__default.existsSync(destinationPath)) {
		throw new Error(`The destination file exists: ${destinationPath}`);
	}

	await promises.mkdir(path__default.dirname(destinationPath), {
		recursive: true,
		mode: directoryMode,
	});

	try {
		await promises.rename(sourcePath, destinationPath);
	} catch (error) {
		if (error.code === 'EXDEV') {
			const stats = await promises.lstat(sourcePath);
			if (stats.isSymbolicLink()) {
				const target = await promises.readlink(sourcePath);
				await promises.symlink(target, destinationPath);
				await promises.unlink(sourcePath);
			} else {
				await promises.cp(sourcePath, destinationPath, {recursive: true, preserveTimestamps: true});
				await promises.rm(sourcePath, {recursive: true});
			}
		} else {
			throw error;
		}
	}
};

async function moveFile(sourcePath, destinationPath, options) {
	validatePathsExist(sourcePath, destinationPath);
	return _moveFile(sourcePath, destinationPath, options);
}

export { moveFile as m };
