import { appendFile, copyFile, mkdir, readFile, unlink, writeFile, } from 'node:fs/promises';
import { existsSync, readdirSync, statSync } from 'node:fs';
import { dirname } from 'node:path';
import { execa } from 'execa';
import { memfs } from 'memfs';
export function createDefaultEnvironment() {
    let errors = [];
    return {
        startRun: () => {
            errors = [];
        },
        finishRun: () => { },
        getErrors: () => errors,
        appendFile: async (path, contents) => {
            await mkdir(dirname(path), { recursive: true });
            return appendFile(path, contents);
        },
        copyFile: async (from, to) => {
            await mkdir(dirname(to), { recursive: true });
            return copyFile(from, to);
        },
        writeFile: async (path, contents) => {
            await mkdir(dirname(path), { recursive: true });
            return writeFile(path, contents);
        },
        execute: async (command, args, cwd) => {
            try {
                await execa(command, args, {
                    cwd,
                });
            }
            catch {
                errors.push(`Command "${command} ${args.join(' ')}" did not run successfully. Please run this manually in your project.`);
            }
        },
        deleteFile: async (path) => {
            await unlink(path);
        },
        readFile: (path, encoding) => readFile(path, { encoding: encoding || 'utf8' }),
        exists: (path) => existsSync(path),
        readdir: (path) => readdirSync(path),
        isDirectory: (path) => {
            const stat = statSync(path);
            return stat.isDirectory();
        },
    };
}
export function createMemoryEnvironment() {
    const environment = createDefaultEnvironment();
    const output = {
        files: {},
        commands: [],
    };
    const { fs, vol } = memfs({});
    const cwd = process.cwd();
    const isInCwd = (path) => path.startsWith(cwd);
    const isTemplatePath = (path) => !isInCwd(path);
    environment.appendFile = async (path, contents) => {
        fs.mkdirSync(dirname(path), { recursive: true });
        await fs.appendFileSync(path, contents);
    };
    environment.copyFile = async (from, to) => {
        if (isTemplatePath(from)) {
            const contents = (await readFile(from)).toString();
            fs.mkdirSync(dirname(to), { recursive: true });
            fs.writeFileSync(to, contents);
        }
        else {
            fs.mkdirSync(dirname(to), { recursive: true });
            fs.copyFileSync(from, to);
        }
    };
    environment.execute = async (command, args) => {
        output.commands.push({
            command,
            args,
        });
        return Promise.resolve();
    };
    environment.readFile = async (path, encoding) => {
        if (isTemplatePath(path)) {
            return (await readFile(path, encoding)).toString();
        }
        return fs.readFileSync(path, 'utf8').toString();
    };
    environment.writeFile = async (path, contents) => {
        fs.mkdirSync(dirname(path), { recursive: true });
        await fs.writeFileSync(path, contents);
    };
    environment.deleteFile = async (path) => {
        await fs.unlinkSync(path);
    };
    environment.exists = (path) => {
        if (isTemplatePath(path)) {
            return existsSync(path);
        }
        return fs.existsSync(path);
    };
    environment.readdir = (path) => {
        if (isTemplatePath(path)) {
            return readdirSync(path);
        }
        return fs.readdirSync(path).map((file) => file.toString());
    };
    environment.isDirectory = (path) => {
        if (isTemplatePath(path)) {
            const stat = statSync(path);
            return stat.isDirectory();
        }
        return fs.statSync(path).isDirectory();
    };
    environment.finishRun = () => {
        output.files = vol.toJSON();
    };
    return {
        environment,
        output,
    };
}
