/**
 * Project path validation for registration.
 * Ensures paths are valid, accessible directories outside system locations.
 */
import { access, realpath, stat } from "node:fs/promises";
import { constants as fsConstants } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
import { ErrorCode } from "@pocketcoder/shared/constants";
import { AppError } from "../errors/index.js";
/** System paths that should never be registered as projects. */
const SYSTEM_PATHS_UNIX = [
    "/bin",
    "/boot",
    "/dev",
    "/etc",
    "/lib",
    "/lib64",
    "/proc",
    "/root",
    "/sbin",
    "/sys",
    "/usr",
];
/** macOS-specific system paths (symlinks resolve to /private/...) */
const SYSTEM_PATHS_MACOS = ["/private/etc"];
const SYSTEM_PATHS_WINDOWS = ["C:\\Windows", "C:\\Program Files", "C:\\Program Files (x86)"];
/**
 * Checks whether a resolved path is within a system directory.
 * Note: /var is NOT blocked because macOS uses /var/folders/ for user temp directories.
 */
function isSystemPath(resolvedPath) {
    const isWin = process.platform === "win32";
    const isMac = process.platform === "darwin";
    let systemPaths;
    if (isWin) {
        systemPaths = SYSTEM_PATHS_WINDOWS;
    }
    else if (isMac) {
        // On macOS, also check /private/etc (symlink resolution)
        systemPaths = [...SYSTEM_PATHS_UNIX, ...SYSTEM_PATHS_MACOS];
    }
    else {
        systemPaths = SYSTEM_PATHS_UNIX;
    }
    const normalizedTarget = isWin ? resolvedPath.toLowerCase() : resolvedPath;
    for (const sysPath of systemPaths) {
        const normalizedSys = isWin ? sysPath.toLowerCase() : sysPath;
        if (normalizedTarget === normalizedSys || normalizedTarget.startsWith(normalizedSys + "/")) {
            return true;
        }
        if (isWin && normalizedTarget.startsWith(normalizedSys + "\\")) {
            return true;
        }
    }
    return false;
}
/**
 * Validates a project path before registration.
 * Normalizes, resolves symlinks, checks existence, verifies it's a directory,
 * checks read permissions, and rejects system paths.
 *
 * @param pathArg - The path to validate (may be relative)
 * @returns The resolved, normalized absolute path
 * @throws AppError with PATH_NOT_FOUND if path doesn't exist
 * @throws AppError with INVALID_REQUEST if path is not a directory or is a system path
 * @throws AppError with PERMISSION_DENIED if path is not readable
 */
export async function validateProjectPath(pathArg) {
    if (!pathArg || pathArg.trim() === "") {
        throw new AppError(ErrorCode.INVALID_REQUEST, "Project path cannot be empty");
    }
    // Normalize and resolve to absolute path
    const normalized = resolve(pathArg);
    // Resolve symlinks to get the real path
    let resolvedPath;
    try {
        resolvedPath = await realpath(normalized);
    }
    catch {
        throw new AppError(ErrorCode.PATH_NOT_FOUND, `Path does not exist: ${normalized}`);
    }
    // Verify it's a directory
    const stats = await stat(resolvedPath);
    if (!stats.isDirectory()) {
        throw new AppError(ErrorCode.INVALID_REQUEST, `Path is not a directory: ${resolvedPath}`);
    }
    // Check read permissions
    try {
        await access(resolvedPath, fsConstants.R_OK);
    }
    catch {
        throw new AppError(ErrorCode.PERMISSION_DENIED, `No read permission for: ${resolvedPath}`);
    }
    // Reject system paths
    if (isSystemPath(resolvedPath)) {
        throw new AppError(ErrorCode.INVALID_REQUEST, `System paths cannot be registered as projects: ${resolvedPath}`);
    }
    // Reject the filesystem root
    if (resolvedPath === "/" || /^[A-Z]:\\$/i.test(resolvedPath)) {
        throw new AppError(ErrorCode.INVALID_REQUEST, `Root directory cannot be registered as a project`);
    }
    // Reject the home directory itself
    if (resolvedPath === homedir()) {
        throw new AppError(ErrorCode.INVALID_REQUEST, `Home directory cannot be registered as a project`);
    }
    return resolvedPath;
}
//# sourceMappingURL=validate-project-path.js.map