/**
 * Find Project Root Utility
 *
 * CRITICAL: This utility finds the SpecWeave project root by searching for .specweave directory.
 * Use this instead of process.cwd() when the working directory may not be the project root.
 *
 * Common scenarios where CWD != project root:
 * - Hooks executed from parent directory
 * - CI/CD pipelines with different working directories
 * - Background jobs spawned from different locations
 * - Multi-repo setups where scripts run from umbrella root
 *
 * @module utils/find-project-root
 */
import * as fs from 'fs';
import path from 'path';
/**
 * Find project root by searching for .specweave/config.json
 *
 * Walks up the directory tree from startDir until it finds a directory
 * containing .specweave/config.json. Requires config.json to distinguish
 * real projects from stale .specweave/ folders (created by runtime bugs).
 *
 * @param startDir - Directory to start searching from (defaults to process.cwd())
 * @returns Project root path or null if not found
 *
 * @example
 * // From /projects/my-app/src/components
 * findProjectRoot()
 * // Returns: "/projects/my-app" (if .specweave/config.json exists there)
 *
 * @example
 * // No .specweave/config.json found anywhere in tree
 * findProjectRoot()
 * // Returns: null
 */
export function findProjectRoot(startDir = process.cwd()) {
    let current = path.resolve(startDir);
    const root = path.parse(current).root;
    while (current !== root) {
        const specweavePath = path.join(current, '.specweave');
        if (fs.existsSync(specweavePath) &&
            fs.statSync(specweavePath).isDirectory() &&
            fs.existsSync(path.join(specweavePath, 'config.json'))) {
            return current;
        }
        current = path.dirname(current);
    }
    return null;
}
/**
 * Get project root, falling back to CWD if not found
 *
 * This is the RECOMMENDED function to use in most cases.
 * It returns a valid path even if .specweave doesn't exist.
 *
 * NOTE: This returns the NEAREST project root (for config reading).
 * For increment operations in multi-repo setups, use resolveEffectiveRoot()
 * which returns the umbrella root.
 *
 * @param startDir - Directory to start searching from (defaults to process.cwd())
 * @returns Project root path or process.cwd() if not found
 *
 * @example
 * // .specweave exists
 * getProjectRoot() // Returns: "/projects/my-app"
 *
 * // .specweave doesn't exist
 * getProjectRoot() // Returns: process.cwd() (e.g., "/projects/my-app")
 */
export function getProjectRoot(startDir = process.cwd()) {
    return findProjectRoot(startDir) || process.cwd();
}
/**
 * Check if current directory is within a SpecWeave project
 *
 * @param startDir - Directory to check (defaults to process.cwd())
 * @returns true if .specweave directory exists in tree
 */
export function isInsideSpecWeaveProject(startDir = process.cwd()) {
    return findProjectRoot(startDir) !== null;
}
/**
 * Check if a directory has an initialized SpecWeave project with actual increments.
 *
 * A repo is considered "initialized" only when `.specweave/increments/` exists
 * and contains at least one non-hidden entry. A `.specweave/` folder with just
 * logs, state, or config is NOT considered initialized.
 *
 * @param dir - Directory to check (the repo root, not .specweave/ itself)
 * @returns true if the directory has .specweave/increments/ with content
 */
export function hasSpecweaveIncrements(dir) {
    const incDir = path.join(dir, '.specweave', 'increments');
    if (!fs.existsSync(incDir))
        return false;
    try {
        return fs.readdirSync(incDir).filter(f => !f.startsWith('.')).length > 0;
    }
    catch {
        return false;
    }
}
/**
 * Find the umbrella root by walking up from the nearest project root.
 *
 * In a multi-repo umbrella project, nested repos each have their own
 * .specweave/config.json, but the umbrella root is the TOPMOST one
 * with `umbrella.enabled: true` or a sibling `repositories/` directory.
 *
 * @param startDir - Directory to start searching from (defaults to process.cwd())
 * @returns Umbrella root path or null if not inside an umbrella project
 */
export function findUmbrellaRoot(startDir = process.cwd()) {
    // Find the nearest project root first
    const nearestRoot = findProjectRoot(startDir);
    if (!nearestRoot)
        return null;
    // Check if the nearest root IS an umbrella root
    const nearestConfigPath = path.join(nearestRoot, '.specweave', 'config.json');
    if (fs.existsSync(nearestConfigPath)) {
        try {
            const config = JSON.parse(fs.readFileSync(nearestConfigPath, 'utf-8'));
            if (config?.umbrella?.enabled || config?.repository?.umbrellaRepo) {
                return nearestRoot;
            }
        }
        catch {
            // Invalid JSON, continue walking up
        }
        const reposDir = path.join(nearestRoot, 'repositories');
        if (fs.existsSync(reposDir) && fs.statSync(reposDir).isDirectory()) {
            return nearestRoot;
        }
    }
    // Walk up from the parent of the nearest root
    let current = path.dirname(nearestRoot);
    const root = path.parse(current).root;
    while (current !== root) {
        const configPath = path.join(current, '.specweave', 'config.json');
        if (fs.existsSync(configPath)) {
            try {
                const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
                // Check umbrella config flags
                if (config?.umbrella?.enabled || config?.repository?.umbrellaRepo) {
                    return current;
                }
            }
            catch {
                // Invalid JSON, skip
            }
            // Check for sibling repositories/ directory (the multi-repo marker)
            const reposDir = path.join(current, 'repositories');
            if (fs.existsSync(reposDir) && fs.statSync(reposDir).isDirectory()) {
                return current;
            }
        }
        current = path.dirname(current);
    }
    return null;
}
/**
 * Resolve the effective project root for increment operations.
 *
 * - In a multi-repo umbrella: returns the umbrella root (increments belong there)
 * - In a single-repo project: returns the nearest project root
 * - Fallback: returns process.cwd()
 *
 * @param startDir - Directory to start searching from (defaults to process.cwd())
 * @returns Effective project root path
 */
export function resolveEffectiveRoot(startDir = process.cwd()) {
    const umbrellaRoot = findUmbrellaRoot(startDir);
    if (umbrellaRoot)
        return umbrellaRoot;
    return findProjectRoot(startDir) || process.cwd();
}
/**
 * Get the umbrella-aware .specweave directory path.
 *
 * Uses resolveEffectiveRoot() to find the correct root — returns the
 * umbrella root's .specweave/ when inside an umbrella workspace, or
 * the nearest project root's .specweave/ in standalone mode.
 *
 * This is the RECOMMENDED function for code that writes to .specweave/.
 * Unlike getSpecWeaveDir() which uses findProjectRoot() (nearest root,
 * returns null), this always returns a valid path.
 *
 * @param startDir - Directory to start searching from (defaults to process.cwd())
 * @returns Absolute path to the effective .specweave directory
 */
export function getSpecweavePath(startDir) {
    return path.join(resolveEffectiveRoot(startDir), '.specweave');
}
/**
 * Get SpecWeave directory path (.specweave folder)
 *
 * @param projectRoot - Project root (will search if not provided)
 * @returns Path to .specweave directory or null if not found
 */
export function getSpecWeaveDir(projectRoot) {
    const root = projectRoot || findProjectRoot();
    if (!root)
        return null;
    const specweavePath = path.join(root, '.specweave');
    return fs.existsSync(specweavePath) ? specweavePath : null;
}
/**
 * Resolve a path relative to project root
 *
 * Useful for constructing paths that should be relative to project root,
 * not the current working directory.
 *
 * @param relativePath - Path relative to project root
 * @param startDir - Directory to start search from (defaults to process.cwd())
 * @returns Resolved absolute path or null if project root not found
 *
 * @example
 * // From any subdirectory
 * resolveFromProjectRoot('.specweave/config.json')
 * // Returns: "/projects/my-app/.specweave/config.json"
 */
export function resolveFromProjectRoot(relativePath, startDir = process.cwd()) {
    const projectRoot = findProjectRoot(startDir);
    if (!projectRoot)
        return null;
    return path.resolve(projectRoot, relativePath);
}
//# sourceMappingURL=find-project-root.js.map