import path from 'path-browserify';
export class InMemoryFile {
    name;
    _data;
    type = 'file';
    constructor(name, _data) {
        this.name = name;
        this._data = _data;
    }
    read() {
        return this._data;
    }
    write(data) {
        this._data = data;
    }
}
export class InMemoryDir {
    name;
    _entries;
    type = 'directory';
    constructor(name, _entries = new Map()) {
        this.name = name;
        this._entries = _entries;
    }
    addEntry(entry) {
        this._entries.set(entry.name, entry);
    }
    removeEntry(name) {
        return this._entries.delete(name);
    }
    getFile(name) {
        const item = this._entries.get(name);
        if (item?.type === 'file') {
            return item;
        }
        return undefined;
    }
    getDirectory(name) {
        const item = this._entries.get(name);
        if (item?.type === 'directory') {
            return item;
        }
        return undefined;
    }
    getEntries() {
        return Array.from(this._entries.values());
    }
}
export class InMemoryWorkspace {
    root = new InMemoryDir('');
    async exec(command, args, timeout) {
        throw new Error('Executing commands is not supported in this application environment.');
    }
    async mkdir(subpath, opt = { recursive: false }) {
        this.mkdirSync(subpath, opt);
    }
    async rmdir(subpath, opt) {
        this.rmdirSync(subpath, opt);
    }
    async readdir(subpath) {
        return this.readdirSync(subpath);
    }
    async writeFile(filePath, data) {
        this.writeFileSync(filePath, data);
    }
    async readFile(filePath) {
        return this.readFileSync(filePath);
    }
    async rm(filePath) {
        this.rmSync(filePath);
    }
    async exists(pathStr) {
        return this.existsSync(pathStr);
    }
    async rename(oldPath, newPath) {
        this.renameSync(oldPath, newPath);
    }
    async appendFile(subpath, data) {
        this.appendFileSync(subpath, data);
    }
    writeFileSync(subpath, data) {
        const path = this.toWorkspacePathSegments(subpath);
        const [parentDir, fileName] = this.navigateToPath(path);
        parentDir.addEntry(new InMemoryFile(fileName, data));
    }
    readFileSync(subpath) {
        const path = this.toWorkspacePathSegments(subpath);
        const [parentDir, fileName] = this.navigateToPath(path);
        const file = parentDir.getFile(fileName);
        if (!file) {
            throw new Error(`File not found: ${subpath}`);
        }
        return file.read();
    }
    existsSync(subpath) {
        try {
            const path = this.toWorkspacePathSegments(subpath);
            const [parentDir, name] = this.navigateToPath(path);
            return parentDir.getFile(name) !== undefined || parentDir.getDirectory(name) !== undefined;
        }
        catch (error) {
            return false;
        }
    }
    renameSync(oldPath, newPath) {
        const oldPathSegments = this.toWorkspacePathSegments(oldPath);
        const [oldParentDir, oldName] = this.navigateToPath(oldPathSegments);
        const newPathSegments = this.toWorkspacePathSegments(newPath);
        const [newParentDir, newName] = this.navigateToPath(newPathSegments);
        const item = oldParentDir.getFile(oldName) || oldParentDir.getDirectory(oldName);
        if (!item) {
            throw new Error(`Path not found: ${oldPath}`);
        }
        if (newParentDir.getFile(newName) || newParentDir.getDirectory(newName)) {
            throw new Error(`Destination path already exists: ${newPath}`);
        }
        newParentDir.addEntry(item);
        oldParentDir.removeEntry(oldName);
    }
    rmSync(filePath) {
        const path = this.toWorkspacePathSegments(filePath);
        const [parentDir, fileName] = this.navigateToPath(path);
        const file = parentDir.getFile(fileName);
        if (!file) {
            throw new Error(`File not found: ${filePath}`);
        }
        parentDir.removeEntry(fileName);
    }
    mkdirSync(subpath, opts) {
        const path = this.toWorkspacePathSegments(subpath);
        if (opts?.recursive) {
            let currentDir = this.root;
            for (const segment of path) {
                if (!currentDir.getDirectory(segment)) {
                    currentDir.addEntry(new InMemoryDir(segment));
                }
                currentDir = currentDir.getDirectory(segment);
            }
        }
        else {
            const [parentDir, dirName] = this.navigateToPath(path);
            if (parentDir.getDirectory(dirName)) {
                throw new Error(`Directory already exists: ${subpath}`);
            }
            parentDir.addEntry(new InMemoryDir(dirName));
        }
    }
    rmdirSync(subpath, opts) {
        const path = this.toWorkspacePathSegments(subpath);
        const [parentDir, dirName] = this.navigateToPath(path);
        const dir = parentDir.getDirectory(dirName);
        if (!dir) {
            throw new Error(`Directory not found: ${subpath}`);
        }
        if (!opts?.recursive && dir.getEntries().length > 0) {
            throw new Error(`Directory not empty: ${subpath}`);
        }
        parentDir.removeEntry(dirName);
    }
    readdirSync(subpath) {
        const path = this.toWorkspacePathSegments(subpath);
        let currentDir;
        if (path.length === 1 && path[0] === '') {
            currentDir = this.root;
        }
        else {
            const [parentDir, dirName] = this.navigateToPath(path);
            const directory = parentDir.getDirectory(dirName);
            if (!directory) {
                throw new Error(`Directory not found: ${subpath}`);
            }
            currentDir = directory;
        }
        return currentDir
            .getEntries()
            .map(x => ({
            name: x.name,
            type: x.type,
        }))
            .filter(d => !d.name.startsWith('.'));
    }
    appendFileSync(subpath, data) {
        const pathSegments = this.toWorkspacePathSegments(subpath);
        const [parentDir, fileName] = this.navigateToPath(pathSegments);
        const file = parentDir.getFile(fileName);
        if (file) {
            file.write(file.read() + data);
        }
        else {
            parentDir.addEntry(new InMemoryFile(fileName, data));
        }
    }
    navigateToPath(pathSegments) {
        let currentDir = this.root;
        for (let i = 0; i < pathSegments.length - 1; i++) {
            if (pathSegments[i] === '') {
                continue;
            }
            const nextDir = currentDir.getDirectory(pathSegments[i]);
            if (!nextDir) {
                throw new Error(`Path not found: ${pathSegments.slice(0, i + 1).join('/')}`);
            }
            currentDir = nextDir;
        }
        return [currentDir, pathSegments[pathSegments.length - 1]];
    }
    toWorkspacePath(subpath) {
        return path.resolve('/', subpath);
    }
    toWorkspacePathSegments(subpath) {
        return this.toWorkspacePath(subpath).split('/').slice(1);
    }
}
