/**
 * Filesystem path helpers for project management.
 *
 * Projects in `spectral serve` are user-owned directories on disk. We accept
 * paths typed by the user from the browser, so two small concerns:
 *
 *  - Tilde expansion: `~/foo/bar` is the natural way to refer to a path
 *    inside `$HOME`. Node's `path` module does not expand tildes; we do it
 *    ourselves with a leading-segment check (NOT a global string replace —
 *    `~user/...` and embedded `~` are intentionally left alone).
 *  - Validation: the path must exist, must be a directory, and must be
 *    readable. We do NOT attempt to verify writability — spectral may want to
 *    create files inside it, but failure modes there surface naturally
 *    through spectral's tools, and a write probe here would create stray files.
 *
 * Both helpers are synchronous because they're called on hot HTTP paths
 * (POST /api/projects) where blocking is acceptable: the operation is rare
 * and the user is waiting on the response anyway. `node:fs` sync calls in
 * this regime are simpler than juggling promises through validation.
 */
import { existsSync, statSync } from "node:fs";
import { homedir } from "node:os";
import { isAbsolute, resolve, sep } from "node:path";
/**
 * Expand a leading `~` or `~/` to the user's home directory and return an
 * absolute, normalized path.
 *
 * Behavior:
 *   - `~`           → `$HOME`
 *   - `~/foo`       → `$HOME/foo`
 *   - `/abs/path`   → `/abs/path` (unchanged, normalized)
 *   - `relative`    → resolved against `$HOME` (NOT process.cwd, since the
 *     server runs from `$HOME` and "relative to home" matches the user's
 *     mental model when typing into a form)
 *   - `~user/...`   → returned as-is and resolved against `$HOME`. We do NOT
 *     attempt to look up other users' home directories — out of scope.
 */
export function expandPath(input) {
    const trimmed = input.trim();
    if (!trimmed)
        return homedir();
    // Tilde expansion — leading-segment only. `~` alone or `~/...`.
    if (trimmed === "~")
        return homedir();
    if (trimmed.startsWith("~/") || trimmed.startsWith(`~${sep}`)) {
        return resolve(homedir(), trimmed.slice(2));
    }
    if (isAbsolute(trimmed))
        return resolve(trimmed);
    // Relative path → resolve against $HOME. Matches the form-typing UX where
    // a user enters `projects/foo` expecting `~/projects/foo`.
    return resolve(homedir(), trimmed);
}
/**
 * Validate a user-supplied project path.
 *
 * Returns `{ok:true, path}` for a real, accessible directory. On failure,
 * `error` carries a message suitable for surfacing to the user. The expanded
 * path is included in both cases so callers can echo it back in errors.
 */
export function validateProjectPath(input) {
    const path = expandPath(input);
    let st;
    try {
        if (!existsSync(path)) {
            return { ok: false, path, error: `Path does not exist: ${path}` };
        }
        st = statSync(path);
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        return { ok: false, path, error: `Cannot access path: ${msg}` };
    }
    if (!st.isDirectory()) {
        return { ok: false, path, error: `Path is not a directory: ${path}` };
    }
    return { ok: true, path };
}
