import process from 'node:process';
import path from 'node:path';
import fs, {promises as fsPromises} from 'node:fs';
import {fileURLToPath} from 'node:url';
import pLocate from 'p-locate';

const typeMappings = {
	directory: 'isDirectory',
	file: 'isFile',
};

function checkType(type) {
	if (Object.hasOwnProperty.call(typeMappings, type)) {
		return;
	}

	throw new Error(`Invalid type specified: ${type}`);
}

const matchType = (type, stat) => stat[typeMappings[type]]();

const toPath = urlOrPath => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;

export async function locatePath(
	paths,
	{
		cwd = process.cwd(),
		type = 'file',
		allowSymlinks = true,
		concurrency,
		preserveOrder,
	} = {},
) {
	checkType(type);
	cwd = toPath(cwd);

	const statFunction = allowSymlinks ? fsPromises.stat : fsPromises.lstat;

	return pLocate(paths, async path_ => {
		try {
			const stat = await statFunction(path.resolve(cwd, path_));
			return matchType(type, stat);
		} catch {
			return false;
		}
	}, {concurrency, preserveOrder});
}

export function locatePathSync(
	paths,
	{
		cwd = process.cwd(),
		type = 'file',
		allowSymlinks = true,
	} = {},
) {
	checkType(type);
	cwd = toPath(cwd);

	const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync;

	for (const path_ of paths) {
		try {
			const stat = statFunction(path.resolve(cwd, path_), {
				throwIfNoEntry: false,
			});

			if (!stat) {
				continue;
			}

			if (matchType(type, stat)) {
				return path_;
			}
		} catch {}
	}
}
