import * as fs from "fs";
import * as path from "path";
import { crawl } from "./crawl";
import { rmSync } from "../tools/fs.rmSync";

type TransformSourceCode = (params: {
    sourceCode: Buffer;
    filePath: string;
    fileRelativePath: string;
}) =>
    | {
          modifiedSourceCode: Buffer;
          newFileName?: string;
      }
    | undefined;

/**
 * Apply a transformation function to every file of directory
 * If source and destination are the same this function can be used to apply the transformation in place
 * like filtering out some files or modifying them.
 * */
export function transformCodebase(params: {
    srcDirPath: string;
    destDirPath: string;
    transformSourceCode?: TransformSourceCode;
}) {
    const { srcDirPath, transformSourceCode } = params;

    const isTargetSameAsSource = path.relative(srcDirPath, params.destDirPath) === "";

    const destDirPath = isTargetSameAsSource
        ? path.join(srcDirPath, "..", "tmp_xOsPdkPsTdzPs34sOkHs")
        : params.destDirPath;

    fs.mkdirSync(destDirPath, {
        recursive: true
    });

    for (const fileRelativePath of crawl({
        dirPath: srcDirPath,
        returnedPathsType: "relative to dirPath"
    })) {
        const filePath = path.join(srcDirPath, fileRelativePath);
        const destFilePath = path.join(destDirPath, fileRelativePath);

        // NOTE: Optimization, if we don't need to transform the file, just copy
        // it using the lower level implementation.
        if (transformSourceCode === undefined) {
            fs.mkdirSync(path.dirname(destFilePath), {
                recursive: true
            });

            fs.copyFileSync(filePath, destFilePath);

            continue;
        }

        const transformSourceCodeResult = transformSourceCode({
            sourceCode: fs.readFileSync(filePath),
            filePath,
            fileRelativePath
        });

        if (transformSourceCodeResult === undefined) {
            continue;
        }

        fs.mkdirSync(path.dirname(destFilePath), {
            recursive: true
        });

        const { newFileName, modifiedSourceCode } = transformSourceCodeResult;

        fs.writeFileSync(
            path.join(
                path.dirname(destFilePath),
                newFileName ?? path.basename(destFilePath)
            ),
            modifiedSourceCode
        );
    }

    if (isTargetSameAsSource) {
        rmSync(srcDirPath, { recursive: true });

        fs.renameSync(destDirPath, srcDirPath);
    }
}
