"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilesystemManager = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const fast_glob_1 = __importDefault(require("fast-glob"));
const tsyringe_1 = require("tsyringe");
const ipc_1 = require("../ipc");
const FilesystemHandler_1 = require("./FilesystemHandler");
/**
 * FilesystemManager
 *
 * Manages filesystem operations and path resolution.
 */
let FilesystemManager = class FilesystemManager {
    baseDir;
    handler;
    constructor(baseDir, commandRegistry) {
        // Normalize baseDir to an absolute path to ensure proper path resolution
        this.baseDir = path.resolve(baseDir);
        // Create and register handler
        this.handler = new FilesystemHandler_1.FilesystemHandler(this);
        commandRegistry.registerManager(this.handler);
    }
    /**
     * Get the base directory (user directory)
     */
    getBaseDir() {
        return this.baseDir;
    }
    /**
     * Resolve a masked path (as seen by apps) to an absolute filesystem path.
     *
     * @param targetPath - The masked path (e.g., "/file.json" or "/Documents/notes.txt")
     * @returns The resolved absolute path (e.g., "/home/user/.eden/file.json")
     * @throws Error if the path attempts to escape the base directory
     */
    resolvePath(targetPath) {
        // Prevent directory traversal
        const safePath = path.normalize(targetPath).replace(/^(\.\.[\\/])+/, "");
        // Remove leading slashes to ensure path.join works correctly
        const relativePath = safePath.replace(/^[\\/]+/, "");
        const resolved = path.join(this.baseDir, relativePath);
        // Double check it's still inside baseDir
        if (!resolved.startsWith(this.baseDir)) {
            throw new Error(`Access denied: Path '${targetPath}' resolves to '${resolved}', which is outside of base directory '${this.baseDir}'`);
        }
        return resolved;
    }
    /**
     * Convert an absolute filesystem path back to a masked path (as seen by apps).
     *
     * @param absolutePath - The absolute filesystem path
     * @returns The masked path relative to the base directory
     * @throws Error if the path is outside the base directory
     */
    toMaskedPath(absolutePath) {
        const normalizedAbsolute = path.resolve(absolutePath);
        if (!normalizedAbsolute.startsWith(this.baseDir)) {
            throw new Error(`Path '${absolutePath}' is outside of base directory '${this.baseDir}'`);
        }
        const relativePath = normalizedAbsolute.slice(this.baseDir.length);
        // Ensure it starts with /
        return relativePath.startsWith("/") ? relativePath : "/" + relativePath;
    }
    /**
     * Check if a path is within the allowed base directory
     */
    isPathAllowed(targetPath) {
        try {
            this.resolvePath(targetPath);
            return true;
        }
        catch {
            return false;
        }
    }
    // =====================
    // Filesystem Operations
    // =====================
    /**
     * Read the contents of a file.
     */
    async readFile(targetPath, encoding = "utf-8") {
        const fullPath = this.resolvePath(targetPath);
        return await fs.readFile(fullPath, encoding);
    }
    /**
     * Write content to a file, creating directories if needed.
     */
    async writeFile(targetPath, content, encoding = "utf-8") {
        const fullPath = this.resolvePath(targetPath);
        // Ensure directory exists
        await fs.mkdir(path.dirname(fullPath), { recursive: true });
        await fs.writeFile(fullPath, content, encoding);
    }
    /**
     * Check if a file or directory exists.
     */
    async exists(targetPath) {
        try {
            const fullPath = this.resolvePath(targetPath);
            await fs.access(fullPath);
            return true;
        }
        catch {
            return false;
        }
    }
    /**
     * Create a directory and any necessary parent directories.
     */
    async mkdir(targetPath) {
        const fullPath = this.resolvePath(targetPath);
        await fs.mkdir(fullPath, { recursive: true });
    }
    /**
     * List contents of a directory.
     */
    async readdir(targetPath) {
        const fullPath = this.resolvePath(targetPath);
        return await fs.readdir(fullPath);
    }
    /**
     * Get file or directory statistics.
     */
    async stat(targetPath) {
        const fullPath = this.resolvePath(targetPath);
        const stats = await fs.stat(fullPath);
        return {
            isFile: stats.isFile(),
            isDirectory: stats.isDirectory(),
            size: stats.size,
            mtime: stats.mtime,
        };
    }
    /**
     * Search for files and directories using glob patterns.
     */
    async search(basePath, pattern, limit = 10) {
        const fullPath = this.resolvePath(basePath);
        // Create glob pattern
        // If pattern is empty, match everything
        const globPattern = pattern ? `**/*${pattern}*` : "**/*";
        try {
            const entries = await (0, fast_glob_1.default)(globPattern, {
                cwd: fullPath,
                onlyFiles: false,
                deep: 3, // Limit depth for performance
                suppressErrors: true,
                stats: true,
            });
            const results = [];
            for (const entry of entries) {
                if (results.length >= limit)
                    break;
                const entryPath = basePath === "/" ? `/${entry.path}` : `${basePath}/${entry.path}`;
                const isDirectory = entry.stats?.isDirectory() ?? false;
                results.push({
                    name: path.basename(entry.path),
                    path: entryPath,
                    type: isDirectory ? "folder" : "file",
                });
            }
            return results;
        }
        catch (error) {
            console.error("Search error:", error);
            return [];
        }
    }
    /**
     * Delete a file or directory.
     * For directories, removes recursively.
     */
    async delete(targetPath) {
        const fullPath = this.resolvePath(targetPath);
        // Check if it exists and get stats
        const stats = await fs.stat(fullPath);
        if (stats.isDirectory()) {
            // Remove directory recursively
            await fs.rm(fullPath, { recursive: true, force: true });
        }
        else {
            // Remove file
            await fs.unlink(fullPath);
        }
    }
};
exports.FilesystemManager = FilesystemManager;
exports.FilesystemManager = FilesystemManager = __decorate([
    (0, tsyringe_1.singleton)(),
    (0, tsyringe_1.injectable)(),
    __param(0, (0, tsyringe_1.inject)("userDirectory")),
    __param(1, (0, tsyringe_1.inject)(ipc_1.CommandRegistry)),
    __metadata("design:paramtypes", [String, ipc_1.CommandRegistry])
], FilesystemManager);
//# sourceMappingURL=FilesystemManager.js.map