import {
	existsSync,
	readdirSync,
	readFileSync,
	realpathSync,
	statSync,
} from "node:fs";
import { homedir, platform } from "node:os";
import { dirname, isAbsolute, join, relative, resolve } from "node:path";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

/**
 * Resolve the configured dev root from web preferences.
 * Returns the devRoot path if set, otherwise the user's home directory.
 */
function getDevRoot(): string {
	try {
		const prefsPath = join(homedir(), ".sf", "web-preferences.json");
		if (existsSync(prefsPath)) {
			const prefs = JSON.parse(readFileSync(prefsPath, "utf-8")) as Record<
				string,
				unknown
			>;
			if (typeof prefs.devRoot === "string" && prefs.devRoot) {
				return resolve(prefs.devRoot);
			}
		}
	} catch {
		// Fall through to default
	}
	return homedir();
}

/**
 * Get available mount points on Linux (external drives, removable media)
 * Returns paths like /media, /mnt, /run/media/<user>
 */
function getLinuxMountPoints(): string[] {
	const mountPoints: string[] = [];
	const home = homedir();

	const standardMounts = ["/media", "/mnt", "/run/media"];

	for (const mp of standardMounts) {
		if (existsSync(mp)) {
			mountPoints.push(mp);
		}
	}

	const runMediaUser = `/run/media/${home.split("/").pop()}`;
	if (existsSync(runMediaUser)) {
		mountPoints.push(runMediaUser);
	}

	return mountPoints;
}

/**
 * Get additional root-level directories to show as shortcuts on Linux
 * (for accessing external drives and mounted filesystems)
 */
function getAdditionalRoots(): string[] {
	const os = platform();
	if (os === "linux") {
		return getLinuxMountPoints();
	}
	return [];
}

/**
 * GET /api/browse-directories?path=/some/path
 *
 * Returns the directory listing for the given path.
 * Defaults to the configured devRoot (or home directory) if no path is given.
 * Only returns directories (no files) for the folder picker use case.
 *
 * Security: Paths are restricted to the devRoot and its children. Requests
 * for paths outside devRoot are rejected with 403 to prevent full filesystem
 * enumeration.
 */
export async function GET(request: Request): Promise<Response> {
	try {
		const url = new URL(request.url);
		const rawPath = url.searchParams.get("path");
		const devRoot = getDevRoot();
		const targetPath = rawPath ? resolve(rawPath) : devRoot;

		// Restrict browsing to devRoot and its subtree, or the home directory
		// if no devRoot is configured. Navigating to the parent of devRoot is
		// allowed (one level up) so the UI can show the devRoot in context,
		// but nothing further.
		// Also allow navigation to common mount points (/media, /mnt, /run/media) on Linux
		const devRootParent = dirname(devRoot);
		const additionalRoots = getAdditionalRoots();

		// Use realpath + relative to prevent prefix-based path traversal
		// (e.g. /home/user/projects-backup matching /home/user/projects)
		const isAllowedPath = (() => {
			try {
				const realTarget = realpathSync(targetPath);
				const realDevRoot = realpathSync(devRoot);
				const relToDevRoot = relative(realDevRoot, realTarget);
				const inDevRoot =
					relToDevRoot === "" ||
					(!relToDevRoot.startsWith("..") && !isAbsolute(relToDevRoot));
				if (inDevRoot) return true;
				const realDevRootParent = realpathSync(devRootParent);
				if (realTarget === realDevRootParent) return true;
				return additionalRoots.some((root) => {
					if (!existsSync(root)) return false;
					const realRoot = realpathSync(root);
					const rel = relative(realRoot, realTarget);
					return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
				});
			} catch {
				return false;
			}
		})();

		if (!isAllowedPath) {
			return Response.json(
				{ error: "Path outside allowed scope" },
				{ status: 403 },
			);
		}

		if (!existsSync(targetPath)) {
			return Response.json(
				{ error: `Path does not exist: ${targetPath}` },
				{ status: 404 },
			);
		}

		const stat = statSync(targetPath);
		if (!stat.isDirectory()) {
			return Response.json(
				{ error: `Not a directory: ${targetPath}` },
				{ status: 400 },
			);
		}

		const parentPath = dirname(targetPath);
		// Only offer the parent navigation if it's within the allowed scope
		const parentAllowed = (() => {
			try {
				const realParent = realpathSync(parentPath);
				const realDevRootParent = realpathSync(devRootParent);
				const rel = relative(realDevRootParent, realParent);
				return (
					(rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) &&
					parentPath !== targetPath
				);
			} catch {
				return false;
			}
		})();
		const entries: Array<{ name: string; path: string }> = [];

		// On Linux, show mount points as quick-access when browsing from home directory
		const showMountPoints =
			platform() === "linux" &&
			(targetPath === homedir() || targetPath === devRoot);

		try {
			const items = readdirSync(targetPath, { withFileTypes: true });
			for (const item of items) {
				// Only directories, skip dotfiles and common non-project dirs
				if (!item.isDirectory()) continue;
				if (item.name.startsWith(".")) continue;
				if (item.name === "node_modules") continue;

				entries.push({
					name: item.name,
					path: resolve(targetPath, item.name),
				});
			}

			// Add mount points as quick-access entries on Linux
			if (showMountPoints) {
				for (const mp of additionalRoots) {
					if (existsSync(mp)) {
						const mpName = mp.split("/").pop() || mp;
						entries.push({
							name: mpName,
							path: mp,
						});
					}
				}
			}
		} catch {
			// Permission denied or other read error — return empty entries
		}

		entries.sort((a, b) => a.name.localeCompare(b.name));

		return Response.json({
			current: targetPath,
			parent: parentAllowed ? parentPath : null,
			entries,
		});
	} catch (err) {
		return Response.json(
			{
				error: `Browse failed: ${err instanceof Error ? err.message : String(err)}`,
			},
			{ status: 500 },
		);
	}
}
