import "dotenv/config";
import * as childProcess from "child_process";
import semver from "semver";

export const dryRun = process.env.RELEASE_DRY_RUN === "1";

if (dryRun) {
    console.info("Dry run enabled.");
}

/**
 * Check if docker is running
 * @returns {void}
 */
export function checkDocker() {
    try {
        childProcess.execSync("docker ps");
    } catch (error) {
        console.error("Docker is not running. Please start docker and try again.");
        process.exit(1);
    }
}

/**
 * Get Docker Hub repository name
 */
export function getRepoNames() {
    if (process.env.RELEASE_REPO_NAMES) {
        // Split by comma
        return process.env.RELEASE_REPO_NAMES.split(",").map((name) => name.trim());
    }
    return [
        "louislam/uptime-kuma",
        "ghcr.io/louislam/uptime-kuma",
    ];
}

/**
 * Build frontend dist
 * @returns {void}
 */
export function buildDist() {
    if (!dryRun) {
        childProcess.execSync("npm run build", { stdio: "inherit" });
    } else {
        console.info("[DRY RUN] npm run build");
    }
}

/**
 * Build docker image and push to Docker Hub
 * @param {string[]} repoNames Docker Hub repository names
 * @param {string[]} tags Docker image tags
 * @param {string} target Dockerfile's target name
 * @param {string} buildArgs Docker build args
 * @param {string} dockerfile Path to Dockerfile
 * @param {string} platform Build platform
 * @returns {void}
 */
export function buildImage(repoNames, tags, target, buildArgs = "", dockerfile = "docker/dockerfile", platform = "linux/amd64,linux/arm64,linux/arm/v7") {
    let args = [
        "buildx",
        "build",
        "-f",
        dockerfile,
        "--platform",
        platform,
    ];

    for (let repoName of repoNames) {
        // Add tags
        for (let tag of tags) {
            args.push("-t", `${repoName}:${tag}`);
        }
    }

    args = [
        ...args,
        "--target",
        target,
    ];

    // Add build args
    if (buildArgs) {
        args.push("--build-arg", buildArgs);
    }

    args = [
        ...args,
        ".",
        "--push",
    ];

    if (!dryRun) {
        childProcess.spawnSync("docker", args, { stdio: "inherit" });
    } else {
        console.log(`[DRY RUN] docker ${args.join(" ")}`);
    }
}

/**
 * Check if the version already exists on Docker Hub
 * TODO: use semver to compare versions if it is greater than the previous?
 * @param {string[]} repoNames repository name (Only check the name with single slash)
 * @param {string} version Version to check
 * @returns {void}
 */
export async function checkTagExists(repoNames, version) {
    // Skip if the tag is not on Docker Hub
    // louislam/uptime-kuma
    let dockerHubRepoNames = repoNames.filter((name) => {
        return name.split("/").length === 2;
    });

    for (let repoName of dockerHubRepoNames) {
        await checkTagExistsSingle(repoName, version);
    }
}

/**
 * Check if the version already exists on Docker Hub
 * @param {string} repoName repository name
 * @param {string} version Version to check
 * @returns {Promise<void>}
 */
export async function checkTagExistsSingle(repoName, version) {
    console.log(`Checking if version ${version} exists on Docker Hub:`, repoName);

    // Get a list of tags from the Docker Hub repository
    let tags = [];

    // It is mainly to check my careless mistake that I forgot to update the release version in .env, so `page_size` is set to 100 is enough, I think.
    const response = await fetch(`https://hub.docker.com/v2/repositories/${repoName}/tags/?page_size=100`);
    if (response.ok) {
        const data = await response.json();
        tags = data.results.map((tag) => tag.name);
    } else {
        console.error("Failed to get tags from Docker Hub");
        process.exit(1);
    }

    // Check if the version already exists
    if (tags.includes(version)) {
        console.error(`Version ${version} already exists`);
        process.exit(1);
    }
}

/**
 * Check the version format
 * @param {string} version Version to check
 * @returns {void}
 */
export function checkVersionFormat(version) {
    if (!version) {
        console.error("VERSION is required");
        process.exit(1);
    }

    // Check the version format, it should be a semver and must be like this: "2.0.0-beta.0"
    if (!semver.valid(version)) {
        console.error("VERSION is not a valid semver version");
        process.exit(1);
    }
}

/**
 * Press any key to continue
 * @returns {Promise<void>}
 */
export function pressAnyKey() {
    console.log("Git Push and Publish the release note on github, then press any key to continue");
    process.stdin.setRawMode(true);
    process.stdin.resume();
    return new Promise(resolve => process.stdin.once("data", data => {
        process.stdin.setRawMode(false);
        process.stdin.pause();
        resolve();
    }));
}

/**
 * Append version identifier
 * @param {string} version Version
 * @param {string} identifier Identifier
 * @returns {string} Version with identifier
 */
export function ver(version, identifier) {
    const obj = semver.parse(version);

    if (obj.prerelease.length === 0) {
        obj.prerelease = [ identifier ];
    } else {
        obj.prerelease[0] = [ obj.prerelease[0], identifier ].join("-");
    }
    return obj.format();
}

/**
 * Upload artifacts to GitHub
 * docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain
 * @param {string} version Version
 * @param {string} githubToken GitHub token
 * @returns {void}
 */
export function uploadArtifacts(version, githubToken) {
    let args = [
        "buildx",
        "build",
        "-f",
        "docker/dockerfile",
        "--platform",
        "linux/amd64",
        "-t",
        "louislam/uptime-kuma:upload-artifact",
        "--build-arg",
        `VERSION=${version}`,
        "--build-arg",
        "GITHUB_TOKEN",
        "--target",
        "upload-artifact",
        ".",
        "--progress",
        "plain",
    ];

    if (!dryRun) {
        childProcess.spawnSync("docker", args, {
            stdio: "inherit",
            env: {
                ...process.env,
                GITHUB_TOKEN: githubToken,
            },
        });
    } else {
        console.log(`[DRY RUN] docker ${args.join(" ")}`);
    }
}

/**
 * Execute a command
 * @param {string} cmd Command to execute
 * @returns {void}
 */
export function execSync(cmd) {
    if (!dryRun) {
        childProcess.execSync(cmd, { stdio: "inherit" });
    } else {
        console.info(`[DRY RUN] ${cmd}`);
    }
}