/**
 * File operation validation utilities.
 *
 * Provides centralized validation logic for all filesystem operations,
 * including blacklist checking, path restrictions, and operation-specific rules.
 *
 * Supports both YAML mode (flat structure) and SDK mode (nested structure)
 * through the PathValidator abstraction.
 */
import path from "node:path";
import { getFileOperationBlacklist, DEFAULT_VALIDATION_OPTIONS, } from "../constants.js";
import { SdkPathValidator } from "./sdk-path-validator.js";
import { YamlPathValidator } from "./yaml-path-validator.js";
/**
 * Creates the appropriate path validator based on feature flag.
 *
 * @param sdkApiEnabled - Whether SDK API mode is enabled
 * @returns PathValidator implementation for the appropriate mode
 */
export function createPathValidator(sdkApiEnabled) {
    return sdkApiEnabled ? new SdkPathValidator() : new YamlPathValidator();
}
/** Default path validator (YAML mode for backwards compatibility) */
const defaultValidator = new YamlPathValidator();
/**
 * Error thrown when a file operation fails validation.
 */
export class FileValidationError extends Error {
    code;
    constructor(message, code) {
        super(message);
        this.code = code;
        this.name = "FileValidationError";
    }
}
/**
 * Checks if a path is an API file path.
 *
 * @param filePath - Relative file path to check
 * @param validator - Optional path validator (defaults to YAML mode)
 * @returns true if the path is an API file path
 */
export function isApiFilePath(filePath, validator = defaultValidator) {
    return validator.isApiFilePath(filePath);
}
/**
 * Checks if a path is a direct page path (pages/xyz.tsx rather than pages/xyz/index.tsx).
 *
 * @param filePath - Relative file path to check
 * @param validator - Optional path validator (defaults to YAML mode)
 * @returns true if the path is a direct page file
 */
export function isDirectPagePath(filePath, validator = defaultValidator) {
    return validator.isDirectPagePath(filePath);
}
/**
 * Checks if a path is a scope file.
 *
 * @param filePath - Relative file path to check
 * @param validator - Optional path validator (defaults to YAML mode)
 * @returns true if the path is a scope file
 */
export function isScopeFile(filePath, validator = defaultValidator) {
    return validator.isScopeFile(filePath);
}
/**
 * Checks if a path is the package.json file.
 *
 * @param filePath - Relative file path to check
 * @returns true if the path is package.json
 */
export function isPackageJson(filePath) {
    return filePath === "package.json" || filePath.endsWith("/package.json");
}
/**
 * Checks if a file name matches any of the protected patterns.
 *
 * @param fileName - The file name (not full path) to check
 * @returns true if the file name is protected
 */
function matchesProtectedPattern(fileName, blacklistOptions) {
    const blacklist = getFileOperationBlacklist(blacklistOptions);
    return blacklist.patterns.some((pattern) => pattern.test(fileName));
}
/**
 * Checks if a path is in the blacklist.
 *
 * @param relativePath - Relative path to check
 * @param _appRootDirPath - Application root directory for relative path calculation
 * @param validator - Optional path validator (defaults to YAML mode)
 * @returns Error message if blacklisted, null otherwise
 */
export function checkBlacklist(relativePath, _appRootDirPath, validator = defaultValidator, blacklistOptions) {
    const fileName = path.basename(relativePath);
    const blacklist = getFileOperationBlacklist(blacklistOptions);
    // Check if it's the app root
    if (relativePath === "" || relativePath === ".") {
        return "Cannot operate on the application root directory";
    }
    // Check exact paths
    if (blacklist.exactPaths.some((protectedPath) => protectedPath === fileName)) {
        return `Cannot operate on protected file: ${fileName}`;
    }
    // Get protected directories from validator
    const protectedDirs = validator.getProtectedDirectories();
    // Check protected directories
    if (protectedDirs.includes(fileName)) {
        return `Cannot operate on protected directory: ${fileName}`;
    }
    // Check if path starts with a protected directory
    const firstDir = relativePath.split("/")[0];
    if (firstDir && protectedDirs.includes(firstDir)) {
        // Check if validator allows this path to bypass blacklist
        if (validator.shouldBypassBlacklist(relativePath, firstDir)) {
            return null;
        }
        return `Cannot operate on files in protected directory: ${firstDir}`;
    }
    // For SDK mode, also check nested protected paths (e.g., client/pages)
    // Allow special directories like server/apis/ and client/pages/
    if (validator.shouldBypassBlacklist(relativePath, firstDir ?? "")) {
        return null;
    }
    // Check patterns
    if (matchesProtectedPattern(fileName, blacklistOptions)) {
        return `Cannot operate on protected file matching pattern: ${fileName}`;
    }
    return null;
}
/**
 * Validates a file operation against all applicable rules.
 *
 * @param operation - The type of file operation
 * @param relativePath - Relative path of the file
 * @param appRootDirPath - Application root directory
 * @param options - Optional validation options to override defaults
 * @param validator - Optional path validator (defaults to YAML mode)
 * @throws FileValidationError if validation fails
 */
export function validateFileOperation(operation, relativePath, appRootDirPath, options, validator = defaultValidator, blacklistOptions) {
    const opts = {
        ...DEFAULT_VALIDATION_OPTIONS[operation],
        ...options,
    };
    // Check blacklist
    if (opts.checkBlacklist) {
        const blacklistError = checkBlacklist(relativePath, appRootDirPath, validator, blacklistOptions);
        if (blacklistError) {
            throw new FileValidationError(blacklistError, "BLACKLISTED");
        }
    }
    // Check API files
    if (!opts.allowApiFiles && isApiFilePath(relativePath, validator)) {
        throw new FileValidationError(`Directly ${operation === "write" ? "writing to" : operation === "delete" ? "deleting" : "operating on"} API files is not allowed. Use the API interface instead.`, "API_FILE");
    }
    // Check scope files
    if (!opts.allowScopeFiles && isScopeFile(relativePath, validator)) {
        throw new FileValidationError("Directly operating on scope files is not allowed. Use the appropriate tool.", "SCOPE_FILE");
    }
    // Check package.json
    if (!opts.allowPackageJson && isPackageJson(relativePath)) {
        throw new FileValidationError("Directly editing package.json is not allowed. Use the build_installPackages tool instead.", "PACKAGE_JSON");
    }
    // Check direct page paths
    if (!opts.allowDirectPagePath && isDirectPagePath(relativePath, validator)) {
        const pagesPrefix = validator.pagesPrefix;
        throw new FileValidationError(`Direct file creation in the pages directory is not allowed. Please create files within subdirectories (e.g., ${pagesPrefix}/yourFeature/index.tsx)`, "DIRECT_PAGE");
    }
}
/**
 * Recursively checks if a directory contains any blacklisted content.
 *
 * @param entries - Directory entries to scan
 * @param validator - Optional path validator (defaults to YAML mode)
 * @returns Error message if blacklisted content found, null otherwise
 */
export function checkDirectoryContentBlacklist(entries, validator = defaultValidator, blacklistOptions) {
    const blacklist = getFileOperationBlacklist(blacklistOptions);
    const protectedDirs = validator.getProtectedDirectories();
    for (const entry of entries) {
        const fileName = entry.name;
        // Check exact paths
        if (blacklist.exactPaths.some((protectedPath) => protectedPath === fileName)) {
            return `Cannot remove protected file: ${fileName}`;
        }
        // Check protected directories
        if (entry.isDirectory && protectedDirs.includes(fileName)) {
            return `Cannot remove protected directory: ${fileName}`;
        }
        // Check patterns
        if (matchesProtectedPattern(fileName, blacklistOptions)) {
            return `Cannot remove protected file matching pattern: ${fileName}`;
        }
    }
    return null;
}
//# sourceMappingURL=validation.js.map