import { appendFile, copyFile, mkdir, readFile, readdir, unlink, writeFile, } from 'node:fs/promises';
import { existsSync, statSync } from 'node:fs';
import { dirname } from 'node:path';
import { execa } from 'execa';
import { memfs } from 'memfs';
import { rimraf } from 'rimraf';
import { cleanUpFileArray, cleanUpFiles, getBinaryFile, } from './file-helpers.js';
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);
        },
        writeFileBase64: async (path, base64Contents) => {
            await mkdir(dirname(path), { recursive: true });
            return writeFile(path, getBinaryFile(base64Contents));
        },
        execute: async (command, args, cwd, options) => {
            try {
                if (options?.inherit) {
                    // For commands that should show output directly to the user
                    await execa(command, args, {
                        cwd,
                        stdio: 'inherit',
                    });
                    return { stdout: '' };
                }
                else {
                    // For commands where we need to capture output
                    const result = await execa(command, args, {
                        cwd,
                    });
                    return { stdout: result.stdout };
                }
            }
            catch {
                errors.push(`Command "${command} ${args.join(' ')}" did not run successfully. Please run this manually in your project.`);
                return { stdout: '' };
            }
        },
        deleteFile: async (path) => {
            if (existsSync(path)) {
                await unlink(path);
            }
        },
        readFile: async (path) => {
            return (await readFile(path)).toString();
        },
        exists: (path) => existsSync(path),
        isDirectory: (path) => statSync(path).isDirectory(),
        readdir: async (path) => readdir(path),
        rimraf: async (path) => {
            await rimraf(path);
        },
        appName: 'TanStack',
        startStep: () => { },
        finishStep: () => { },
        intro: () => { },
        outro: () => { },
        info: () => { },
        error: () => { },
        warn: () => { },
        confirm: () => Promise.resolve(true),
        spinner: () => ({
            start: () => { },
            stop: () => { },
        }),
    };
}
export function createMemoryEnvironment(returnPathsRelativeTo = '') {
    const environment = createDefaultEnvironment();
    const output = {
        files: {},
        commands: [],
        deletedFiles: [],
    };
    const { fs, vol } = memfs({});
    environment.appendFile = async (path, contents) => {
        fs.mkdirSync(dirname(path), { recursive: true });
        await fs.appendFileSync(path, contents);
    };
    environment.copyFile = async (from, to) => {
        fs.mkdirSync(dirname(to), { recursive: true });
        fs.copyFileSync(from, to);
        return Promise.resolve();
    };
    environment.execute = async (command, args) => {
        output.commands.push({
            command,
            args,
        });
        return Promise.resolve({ stdout: '' });
    };
    environment.readFile = async (path) => {
        return Promise.resolve(fs.readFileSync(path, 'utf-8').toString());
    };
    environment.writeFile = async (path, contents) => {
        fs.mkdirSync(dirname(path), { recursive: true });
        await fs.writeFileSync(path, contents);
    };
    environment.writeFileBase64 = async (path, contents) => {
        // For the in-memory file system, we are not converting the base64 to binary
        // because it's not needed.
        fs.mkdirSync(dirname(path), { recursive: true });
        await fs.writeFileSync(path, contents);
    };
    environment.deleteFile = async (path) => {
        output.deletedFiles.push(path);
        if (fs.existsSync(path)) {
            await fs.unlinkSync(path);
        }
    };
    environment.finishRun = () => {
        output.files = vol.toJSON();
        for (const file of Object.keys(output.files)) {
            if (fs.statSync(file).isDirectory()) {
                delete output.files[file];
            }
        }
        if (returnPathsRelativeTo.length) {
            output.files = cleanUpFiles(output.files, returnPathsRelativeTo);
            output.deletedFiles = cleanUpFileArray(output.deletedFiles, returnPathsRelativeTo);
        }
    };
    environment.exists = (path) => {
        return fs.existsSync(path);
    };
    environment.isDirectory = (path) => {
        return fs.statSync(path).isDirectory();
    };
    environment.readdir = async (path) => {
        return Promise.resolve(fs.readdirSync(path).map((d) => d.toString()));
    };
    environment.rimraf = async () => { };
    return {
        environment,
        output,
    };
}
