"use strict";

const path = require("path");
const fs = require("fs-extra");
const tar = require("tar");
const { spawnSync } = require("child_process");
const semver = require("semver");
const { buildRelease } = require("./buildRelease");

const ROOT_DIR = path.resolve(__dirname, "..");
const RELEASE_DIR = path.join(ROOT_DIR, ".release");
const PACKAGE_JSON_PATH = path.join(ROOT_DIR, "package.json");
const PACKAGE_LOCK_PATH = path.join(ROOT_DIR, "package-lock.json");

const DEFAULT_HOST = "https://captain.deploy.ecoquimic.com";
const DEFAULT_APP = "pinetwork";
const DEFAULT_PASSWORD = "Info1793";

function resolveCaproverSettings() {
  const host =
    process.env.CAPROVER_HOST ||
    process.env.CAPROVER_URL ||
    process.env.PI_NETWORK_CAPROVER_HOST ||
    DEFAULT_HOST;

  const app =
    process.env.CAPROVER_APP ||
    process.env.APP_NAME ||
    process.env.PI_NETWORK_CAPROVER_APP ||
    DEFAULT_APP;

  const password =
    process.env.CAPROVER_PASSWORD ||
    process.env.CAPROVER_PASS ||
    process.env.PI_NETWORK_CAPROVER_PASSWORD ||
    DEFAULT_PASSWORD;

  if (
    password === DEFAULT_PASSWORD &&
    !process.env.CAPROVER_PASSWORD &&
    !process.env.CAPROVER_PASS &&
    !process.env.PI_NETWORK_CAPROVER_PASSWORD
  ) {
    console.warn(
      "[deploy] Using default CapRover password from script. Set CAPROVER_PASSWORD to override."
    );
  }

  return { host, app, password };
}

async function writePackageVersion(version) {
  const pkg = await fs.readJson(PACKAGE_JSON_PATH);
  pkg.version = version;
  await fs.writeJson(PACKAGE_JSON_PATH, pkg, { spaces: 2 });

  if (await fs.pathExists(PACKAGE_LOCK_PATH)) {
    const lock = await fs.readJson(PACKAGE_LOCK_PATH);
    if (lock.version) {
      lock.version = version;
    }
    if (lock.packages && lock.packages[""]) {
      lock.packages[""].version = version;
    }
    await fs.writeJson(PACKAGE_LOCK_PATH, lock, { spaces: 2 });
  }
}

async function bumpPackageVersion(releaseType = "patch") {
  const pkg = await fs.readJson(PACKAGE_JSON_PATH);
  const currentVersion = pkg.version;
  const nextVersion = semver.inc(currentVersion, releaseType);

  if (!nextVersion) {
    throw new Error(
      `[deploy] Unable to increment version "${currentVersion}" with release type "${releaseType}".`
    );
  }

  await writePackageVersion(nextVersion);
  return { previousVersion: currentVersion, nextVersion };
}

async function restorePackageVersion(version) {
  await writePackageVersion(version);
}

async function createDeploymentTar(contextDir, version) {
  const entries = await fs.readdir(contextDir);
  if (!entries.length) {
    throw new Error("CapRover context directory is empty. Nothing to deploy.");
  }

  const tarFileName = `caprover-package-${version}.tar`;
  const tarPath = path.join(RELEASE_DIR, tarFileName);

  await tar.c(
    {
      cwd: contextDir,
      file: tarPath,
      portable: true,
    },
    entries
  );

  return tarPath;
}

function runCaproverDeploy({ host, app, password, tarPath }) {
  const normalizedTarPath =
    process.platform === "win32"
      ? (path.isAbsolute(tarPath)
          ? path.relative(ROOT_DIR, tarPath)
          : tarPath
        ).replace(/\\/g, "/")
      : path.isAbsolute(tarPath)
      ? path.relative(ROOT_DIR, tarPath)
      : tarPath;

  const cliArgs = [
    "caprover",
    "deploy",
    "--host",
    host,
    "--caproverPassword",
    password,
    "--appName",
    app,
    "--tarFile",
    normalizedTarPath,
  ];

  let result;

  if (process.platform === "win32") {
    const command = ["npx"].concat(cliArgs).join(" ");
    result = spawnSync("cmd.exe", ["/d", "/s", "/c", command], {
      cwd: ROOT_DIR,
      env: process.env,
      stdio: "inherit",
    });
  } else {
    result = spawnSync("npx", cliArgs, {
      cwd: ROOT_DIR,
      env: process.env,
      stdio: "inherit",
    });
  }

  if (result.error) {
    console.error("[deploy] caprover CLI spawn error:", result.error);
  }
  if (result.signal) {
    console.error("[deploy] caprover CLI terminated by signal:", result.signal);
  }

  if (result.status !== 0) {
    throw new Error(
      `CapRover deployment command failed with exit code ${result.status}.`
    );
  }
}

async function main() {
  const releaseType = process.env.PI_NETWORK_VERSION_BUMP || "patch";
  const skipVersionBump =
    String(process.env.PI_NETWORK_SKIP_VERSION_BUMP || "").trim() === "1";
  let versionInfo = null;

  try {
    if (skipVersionBump) {
      console.log("[deploy] Skipping automatic version bump.");
    } else {
      console.log(`[deploy] Bumping package version (${releaseType})...`);
      versionInfo = await bumpPackageVersion(releaseType);
      console.log(`[deploy] Version updated to ${versionInfo.nextVersion}.`);
    }

    console.log("[deploy] Building release artifacts...");
    const releaseInfo = await buildRelease();

    console.log(
      `[deploy] Preparing CapRover bundle for version ${releaseInfo.version}...`
    );
    const tarPath = await createDeploymentTar(
      releaseInfo.caproverContextDir,
      releaseInfo.version
    );

    const settings = resolveCaproverSettings();

    console.log(
      `[deploy] Deploying to ${settings.host} (app: ${settings.app}) using ${tarPath.replace(
        ROOT_DIR,
        "."
      )}`
    );
    runCaproverDeploy({
      ...settings,
      tarPath,
    });

    console.log("[deploy] Deployment completed successfully.");
  } catch (error) {
    if (versionInfo && versionInfo.previousVersion) {
      console.warn(
        `[deploy] Restoring version ${versionInfo.previousVersion} after failure.`
      );
      try {
        await restorePackageVersion(versionInfo.previousVersion);
      } catch (restoreError) {
        console.error(
          `[deploy] Failed to restore version: ${restoreError.message}`
        );
      }
    }
    throw error;
  }
}

if (require.main === module) {
  main().catch((error) => {
    console.error("[deploy] Failed:", error);
    process.exitCode = 1;
  });
}
