// @ts-check
import path from "path";
import fs from "fs";
import { execSync, spawnSync } from "child_process";

require("dotenv").config({
    path: path.resolve(__dirname, "../.env"),
});

const isLocal = process.env.NEXT_PUBLIC_DSQL_LOCAL || null;
const DIST_DIR = path.resolve(process.cwd(), "./.dist");
let PREV_BUILD_NO = "0";

const MAX_BUILDS = process.env.DSQL_MAX_BUILDS
    ? Number(process.env.DSQL_MAX_BUILDS)
    : 10;

if (
    MAX_BUILDS < 1 ||
    Number.isNaN(MAX_BUILDS) ||
    typeof MAX_BUILDS !== "number"
) {
    throw new Error("Invalid MAX_BUILDS");
}

if (!fs.existsSync(DIST_DIR)) fs.mkdirSync(DIST_DIR);

if (fs.existsSync(`${DIST_DIR}/BUILD`)) {
    PREV_BUILD_NO = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
} else {
    fs.writeFileSync(`${DIST_DIR}/BUILD`, "0", "utf-8");
}

try {
    const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
    const newBuildNumber = Number(buildNumber) + 1;

    if (newBuildNumber < 0) {
        throw new Error("Invalid Build Number");
    }

    fs.writeFileSync(`${DIST_DIR}/BUILD`, String(newBuildNumber));

    if (newBuildNumber > MAX_BUILDS) {
        const builds = fs.readdirSync(DIST_DIR);
        const buildDirs = builds.filter((build) => build.match(/build-\d+/));
        for (const buildDir of buildDirs) {
            const buildDirPath = path.join(DIST_DIR, buildDir);
            const buildDirStat = fs.statSync(buildDirPath);
            if (buildDirStat.isDirectory()) {
                const buildDirName = buildDir.split("-")[1];
                const buildDirNumber = Number(buildDirName);

                if (buildDirNumber <= newBuildNumber - MAX_BUILDS) {
                    fs.rmdirSync(buildDirPath, { recursive: true });
                    console.log("Deleted Build Directory =>", buildDirPath);
                }
            }
        }
    }
} catch (error: any) {
    console.log("Build Number Parse Error =>", error.message);
    process.exit(1);
}

/** @type {import('child_process').SpawnSyncOptionsWithStringEncoding} */
const spawnSyncOptions: import("child_process").SpawnSyncOptionsWithStringEncoding =
    {
        stdio: "inherit",
        encoding: "utf-8",
        shell: process.platform?.match(/win32/i) ? "bash.exe" : undefined,
        env: {
            ...process.env,
            BUILDING_APP: "true",
        },
    };

const build = spawnSync("bunx", ["next", "build"], spawnSyncOptions);

/**
 * @returns {string}
 */
function grabNewDistDir(): string {
    if (isLocal) return ".local_dist";

    try {
        const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
        return `.dist/build-${buildNumber}`;
    } catch (/** @type {*} */ error: any) {
        console.log("Build Number Parse Error =>", error.message);
        process.exit();
    }
}

const newDistDir = grabNewDistDir();

/**
 * # Revert Directories
 * @param {string} dir - New Build Directory Path
 */
function revert(dir: string) {
    console.log("Build Failed!", build?.error?.message || build.stderr);
    fs.writeFileSync(`${DIST_DIR}/BUILD`, PREV_BUILD_NO, "utf-8");
    execSync(`rm -Rf ${dir}`, { cwd: process.cwd() });

    const writeErr = build.error
        ? build.error.message
        : build.stderr
        ? build.stderr.toString()
        : "NO BUILD_ID found in New Build Folder";
    fs.writeFileSync(
        `${DIST_DIR}/LAST_BUILD_FAIL`,
        Date() + "\n\n" + writeErr,
        "utf-8"
    );

    process.exit(1);
}

if (
    build.error ||
    build.stderr ||
    build.status != 0 ||
    !fs.existsSync(`${newDistDir}/BUILD_ID`)
) {
    if (!isLocal) {
        revert(newDistDir);
        throw new Error("Build Failed!");
    }
}

process.on("exit", () => {
    const onExitDir = grabNewDistDir();
    if (!fs.existsSync(`${onExitDir}/BUILD_ID`) && !isLocal) {
        revert(onExitDir);
    }
});
