import { promises as fs } from 'node:fs';
import path from 'node:path';
import os from 'node:os';
const HOME_DIRECTORY = path.resolve(os.homedir());
function resolveTargetDirectory(target) {
    if (!target)
        return HOME_DIRECTORY;
    let expanded = target;
    if (expanded.startsWith('~')) {
        expanded = path.join(HOME_DIRECTORY, expanded.slice(1));
    }
    const absolute = path.isAbsolute(expanded)
        ? expanded
        : path.join(HOME_DIRECTORY, expanded);
    const resolved = path.resolve(absolute);
    if (!isPathWithinHome(resolved)) {
        throw new Error(`Access to "${resolved}" is not permitted. You may only list directories under "${HOME_DIRECTORY}".`);
    }
    return resolved;
}
function resolveTargetPath(target) {
    if (!target) {
        throw new Error('File path is required');
    }
    let expanded = target;
    if (expanded.startsWith('~')) {
        expanded = path.join(HOME_DIRECTORY, expanded.slice(1));
    }
    const absolute = path.isAbsolute(expanded)
        ? expanded
        : path.join(HOME_DIRECTORY, expanded);
    const resolved = path.resolve(absolute);
    if (!isPathWithinHome(resolved)) {
        throw new Error(`Access to '${resolved}' is not permitted. You may only access files under '${HOME_DIRECTORY}'.`);
    }
    return resolved;
}
function isPathWithinHome(candidate) {
    const relative = path.relative(HOME_DIRECTORY, candidate);
    return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
}
export async function listDirectory(options) {
    const { directoryPath, recursive = false, showHidden = false, exclude = [], } = options;
    const targetDirectory = resolveTargetDirectory(directoryPath);
    const entries = await fs.readdir(targetDirectory, {
        withFileTypes: true,
    });
    const excludeSet = new Set(exclude.map((item) => item.toLowerCase()));
    const results = [];
    for (const entry of entries) {
        if (!showHidden && entry.name.startsWith('.')) {
            continue;
        }
        const fullPath = path.join(targetDirectory, entry.name);
        const resolvedFullPath = path.resolve(fullPath);
        if (!isPathWithinHome(resolvedFullPath)) {
            continue;
        }
        const normalizedName = entry.name.toLowerCase();
        const normalizedPath = resolvedFullPath.toLowerCase();
        if (excludeSet.has(normalizedName) || excludeSet.has(normalizedPath)) {
            continue;
        }
        const stats = await fs.stat(resolvedFullPath);
        const isDirectory = stats.isDirectory();
        results.push({
            name: entry.name,
            path: resolvedFullPath,
            size: stats.size,
            isDirectory,
            lastModified: stats.mtime,
        });
        if (recursive && isDirectory) {
            const childEntries = await listDirectory({
                directoryPath: resolvedFullPath,
                recursive,
                showHidden,
                exclude,
            });
            results.push(...childEntries);
        }
    }
    return results;
}
export async function readFile(filePath) {
    const resolvedPath = resolveTargetPath(filePath);
    return fs.readFile(resolvedPath, 'utf-8');
}
export async function writeFile(filePath, contents) {
    const resolvedPath = resolveTargetPath(filePath);
    await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
    await fs.writeFile(resolvedPath, contents, 'utf-8');
}
function normalizeEncodingOption(option) {
    if (!option)
        return null;
    if (typeof option === 'string')
        return option;
    if (typeof option === 'object') {
        if (option.encoding === null)
            return null;
        return option.encoding ?? null;
    }
    return null;
}
export async function readFileWithEncoding(filePath, options) {
    const resolvedPath = resolveTargetPath(filePath);
    const encoding = normalizeEncodingOption(options);
    const data = await fs.readFile(resolvedPath);
    if (!encoding) {
        return data;
    }
    return data.toString(encoding);
}
export async function writeFileWithEncoding(filePath, data, options) {
    const resolvedPath = resolveTargetPath(filePath);
    const encoding = normalizeEncodingOption(options);
    await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
    if (typeof data === 'string') {
        await fs.writeFile(resolvedPath, data, encoding ?? 'utf-8');
        return;
    }
    await fs.writeFile(resolvedPath, Buffer.from(data));
}
function serializeStats(stats) {
    return {
        size: stats.size,
        mode: stats.mode,
        mtimeMs: stats.mtimeMs,
        ctimeMs: stats.ctimeMs,
        atimeMs: stats.atimeMs,
        birthtimeMs: stats.birthtimeMs,
        isFile: stats.isFile(),
        isDirectory: stats.isDirectory(),
        isSymbolicLink: stats.isSymbolicLink()
    };
}
export async function statPath(targetPath) {
    const resolvedPath = resolveTargetPath(targetPath);
    const stats = await fs.stat(resolvedPath);
    return serializeStats(stats);
}
export async function accessPath(targetPath, mode) {
    const resolvedPath = resolveTargetPath(targetPath);
    await fs.access(resolvedPath, mode);
}
export async function mkdirPath(targetPath, options) {
    const resolvedPath = resolveTargetPath(targetPath);
    await fs.mkdir(resolvedPath, options);
}
export async function removePath(targetPath, options) {
    const resolvedPath = resolveTargetPath(targetPath);
    await fs.rm(resolvedPath, options);
}
