// storage/path-resolver.ts — SPEC-753: projectPath auto-resolution from any subdirectory
//
// Walks up from cwd looking for a planu/ directory; falls back to git root.
// Caches resolution per-session to avoid repeated FS walks.
import { stat, access } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { spawnSync } from 'node:child_process';
import { constants } from 'node:fs';
// ---------------------------------------------------------------------------
// Session-level cache
// ---------------------------------------------------------------------------
const resolutionCache = new Map();
/** Clear the session cache — TEST USE ONLY. */
export function clearProjectPathCache() {
    resolutionCache.clear();
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function hasPlanuDir(dir) {
    try {
        const s = await stat(join(dir, 'planu'));
        return s.isDirectory();
    }
    catch {
        return false;
    }
}
/** @internal exported for testing */
export async function isAccessible(p) {
    try {
        await access(p, constants.F_OK);
        return true;
    }
    catch {
        return false;
    }
}
function detectGitRootSync() {
    try {
        const result = spawnSync('git', ['rev-parse', '--show-toplevel'], {
            cwd: process.cwd(),
            encoding: 'utf-8',
            timeout: 3000,
        });
        if (result.status === 0 && result.stdout) {
            return result.stdout.trim() || null;
        }
        return null;
    }
    catch {
        return null;
    }
}
/**
 * Walk upward from `startDir` until we find a directory containing `planu/`.
 * Stops at filesystem root. Returns null if not found.
 */
async function walkUpForPlanu(startDir) {
    let current = startDir;
    const maxDepth = 20;
    for (let i = 0; i < maxDepth; i++) {
        if (await hasPlanuDir(current)) {
            return current;
        }
        const parent = dirname(current);
        if (parent === current) {
            break;
        }
        current = parent;
    }
    return null;
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Resolve the projectPath for a Planu project.
 *
 * Priority:
 *  1. Explicit `input` argument (fastest, no I/O)
 *  2. PLANU_PROJECT_PATH environment variable
 *  3. Walk up from cwd looking for `planu/`
 *  4. Git root with `planu/` directory
 *
 * Results are cached per cwd key for the session lifetime.
 */
export async function resolveProjectPath(input) {
    if (input?.trim()) {
        return { projectPath: input.trim(), resolvedFrom: 'arg' };
    }
    const cwd = process.cwd();
    const cacheKey = `cwd:${cwd}`;
    const cached = resolutionCache.get(cacheKey);
    if (cached !== undefined) {
        return cached;
    }
    // Env var
    const envPath = process.env.PLANU_PROJECT_PATH?.trim();
    if (envPath) {
        const result = { projectPath: envPath, resolvedFrom: 'env' };
        resolutionCache.set(cacheKey, result);
        return result;
    }
    // Walk up from cwd
    const walked = await walkUpForPlanu(cwd);
    if (walked) {
        const result = {
            projectPath: walked,
            resolvedFrom: 'cwd-walk-planu',
        };
        resolutionCache.set(cacheKey, result);
        return result;
    }
    // Git root fallback
    const gitRoot = detectGitRootSync();
    if (gitRoot && (await hasPlanuDir(gitRoot))) {
        const result = { projectPath: gitRoot, resolvedFrom: 'git-root' };
        resolutionCache.set(cacheKey, result);
        return result;
    }
    if (gitRoot) {
        const result = { projectPath: gitRoot, resolvedFrom: 'git-root' };
        resolutionCache.set(cacheKey, result);
        return result;
    }
    // Cannot resolve — fall back to cwd
    const result = { projectPath: cwd, resolvedFrom: 'cwd-walk-planu' };
    resolutionCache.set(cacheKey, result);
    return result;
}
/** Check if a path looks like a valid Planu project root (has planu/ dir). */
export async function isValidPlanuProject(projectPath) {
    return hasPlanuDir(projectPath);
}
//# sourceMappingURL=path-resolver.js.map