import * as fsPr from "fs/promises";
import { join as pathJoin, sep as pathSep, dirname as pathDirname } from "path";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../shared/buildContext";
import type { UiModuleMeta } from "./uiModuleMeta";
import { existsAsync } from "../tools/fs.existsAsync";

export type BuildContextLike = {
    themeSrcDirPath: string;
};

assert<BuildContext extends BuildContextLike ? true : false>();

const DELIMITER_START = `# === Ejected files start ===`;
const DELIMITER_END = `# === Ejected files end =====`;

export async function writeManagedGitignoreFile(params: {
    buildContext: BuildContextLike;
    uiModuleMetas: UiModuleMeta[];
    ejectedFilesRelativePaths: string[];
}): Promise<void> {
    const { buildContext, uiModuleMetas, ejectedFilesRelativePaths } = params;

    if (uiModuleMetas.length === 0) {
        return;
    }

    const filePath = pathJoin(buildContext.themeSrcDirPath, ".gitignore");

    const content_new = Buffer.from(
        [
            `# This file is managed by Keycloakify, do not edit it manually.`,
            ``,
            DELIMITER_START,
            ...ejectedFilesRelativePaths.map(fileRelativePath =>
                fileRelativePath.split(pathSep).join("/")
            ),
            DELIMITER_END,
            ``,
            ...uiModuleMetas
                .map(uiModuleMeta => [
                    `# === ${uiModuleMeta.moduleName} v${uiModuleMeta.version} ===`,
                    ...uiModuleMeta.files
                        .map(({ fileRelativePath }) => fileRelativePath)
                        .filter(
                            fileRelativePath =>
                                !ejectedFilesRelativePaths.includes(fileRelativePath)
                        )
                        .map(
                            fileRelativePath =>
                                `/${fileRelativePath.split(pathSep).join("/").replace(/^\.\//, "")}`
                        ),

                    ``
                ])
                .flat()
        ].join("\n"),
        "utf8"
    );

    const content_current = await (async () => {
        if (!(await existsAsync(filePath))) {
            return undefined;
        }

        return await fsPr.readFile(filePath);
    })();

    if (content_current !== undefined && content_current.equals(content_new)) {
        return;
    }

    create_dir: {
        const dirPath = pathDirname(filePath);

        if (await existsAsync(dirPath)) {
            break create_dir;
        }

        await fsPr.mkdir(dirPath, { recursive: true });
    }

    await fsPr.writeFile(filePath, content_new);
}