import { getPackageManager } from '@servicetitan/install';
import { exec as execAsync, execSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { promisify } from 'node:util';
import { log, logErrors } from '../../utils';
import { ProcessTree } from '../utils/process-tree';
import type { entry } from './registry/clean';
import { Command } from './types';

const exec = promisify(execAsync);

enum CleanProcesses {
    CleanGitWorkingTree,
    ClearJestCache,
    ClearNpxCache,
    ClearPackageManagerCache,
    ResetNx,
}

export class Clean extends Command<typeof entry> {
    @logErrors
    async execute() {
        const processTree = new ProcessTree<typeof CleanProcesses>();

        processTree.add(CleanProcesses.ResetNx, () => this.runCommand('nx reset'));
        processTree.add(CleanProcesses.ClearJestCache, () => this.runCommand('jest --clearCache'));
        processTree.add(CleanProcesses.ClearPackageManagerCache, () =>
            Promise.resolve(this.clearPackageManagerCache())
        );
        processTree.add(CleanProcesses.ClearNpxCache, () => this.clearNpxCache());

        await processTree.run();

        this.cleanGitWorkingTree();
    }

    private async runCommand(command: string) {
        try {
            log.info(`Running: ${command}`);
            await exec(command);
        } catch (error) {
            log.error(`Error running ${command}: ${error}`);
        }
    }

    private cleanGitWorkingTree() {
        // Use exec() to avoid error when startup dependencies are deleted while startup is running
        const command = 'git clean -fdX';
        log.info(`Running: ${command}`);
        exec(command);
    }

    private async clearNpxCache() {
        try {
            const npmCacheDir = execSync('npm config get cache').toString().trim();
            const npxCachePath = path.join(npmCacheDir, '_npx');

            log.info('Clearing npx cache');
            await fs.promises.rm(npxCachePath, { recursive: true, force: true });
        } catch (error) {
            log.error(`Error clearing npx cache: ${error}`);
        }
    }

    private clearPackageManagerCache() {
        try {
            const packageManager = getPackageManager();
            log.info(`Clearing ${packageManager.command} cache`);
            packageManager.clearCache();
        } catch (error) {
            log.error(`Error clearing package manager cache: ${error}`);
        }
    }
}
