const fs = require('fs/promises');
const Path = require('path');
const mime = require('./mime-types');
const { translatePath, logicalRoot } = require('../utils/logicalPath');
const systemCfg = require('../config');
const projectCfg = require('../db/configuration');

async function directoryListing (fullPath, root) {
	const contents = await fs.readdir(fullPath, {withFileTypes: true});
	const listing = [];
	for (const entry of contents) {
		const resolved = Path.resolve(fullPath, entry.name);
		const relative = resolved.substring(fullPath.length).replace(/^\/+/, "");
		const logical = Path.join(root, relative);
		const stat = await fs.stat(resolved);
		const mimetype = entry.isDirectory()
			? "inode/directory"
			: (mime.contentType(entry.name) || "application/octet-stream");
		listing.push({
			path: logical,
			basename: Path.basename(relative),
			"Content-Type": mimetype,
			size: stat.size,
			atime: stat.atime,
			mtime: stat.mtime,
			ctime: stat.ctime,
			birthtime: stat.birthtime
		});
	}
	return listing;
}

async function virtualDirectoryListing (logicalPaths) {
	const listing = [];
	for (const logical of logicalPaths) {
		const fullPath = translatePath(logical);
		const resolved = Path.resolve("/", logical);
		const stat = await fs.stat(fullPath);
		const mimetype = stat.isDirectory()
			? "inode/directory"
			: (mime.contentType(fullPath) || "application/octet-stream");
		listing.push({
			path: resolved,
			basename: Path.basename(logical),
			"Content-Type": mimetype,
			size: stat.size,
			atime: stat.atime,
			mtime: stat.mtime,
			ctime: stat.ctime,
			birthtime: stat.birthtime
		});
	}
	return listing;
}

async function projectRelativeGet (path, query) {
	console.log("Not implemented yet");
	throw {status: 404, message: "ENOENT"};
}

async function systemRelativeGet (path, query) {
	try {
		if (!path) {
			return await systemRelativeGet("/", query);
		} else if (Path.resolve(path) == "/") {
			return await virtualDirectoryListing(logicalRoot())
		} else {
			const physicalPath = translatePath(path);
			const stats = await fs.stat(physicalPath);
			if (stats.isDirectory()) {
				// Return directory listing, with types.
				return await directoryListing(physicalPath, "/"+path.replace(/^\/+/, ""));
			} else if (stats.isFile()) {
				// Returns a list of arguments suitable for ExpressJS res.download
				const headers = {
					"Content-Type": mime.lookup(physicalPath) || "application/octet-stream"
				};
				return {
					download: [ physicalPath, Path.basename(path), { headers } ]
				};
			} else {
				throw {status: 403, message: "ENOACCESS"};
			}
		}
	} catch (err) {
		console.error(err);
		throw err;
	}
}

module.exports = async function get (path, projectId, query) {
	if (projectId) {
		return await projectRelativeGet(path, query);
	} else {
		return await systemRelativeGet(path, query);
	}
};

/*
module.exports = async function get (path, projectId, query) {
	const root = projectId
		? Path.resolve(systemCfg.global.files.root, await projectCfg.get(projectId, "rootPath"))
		: systemCfg.global.files.root;

	const fullPath = Path.resolve(root, path);

	// Check if there is an attempt to break out of root path
	if (Path.relative(root, fullPath).includes("..")) {
		// Throw something resolving to a 404
		throw {status: 404, message: "ENOENT"};
	} else {
		const stats = await fs.stat(fullPath);
		if (stats.isDirectory()) {
			// Return directory listing, with types.
			return await directoryListing(fullPath, root);
		} else if (stats.isFile()) {
			// Returns a list of arguments suitable for ExpressJS res.download
			return {
				download: [ fullPath, Path.basename(path) ]
			};
		} else {
			throw {status: 403, message: "ENOACCESS"};
		}
	}
}*/
