import fs from "node:fs";
import path from "node:path";
import {spawn} from "node:child_process";

const workspaceRoot = path.resolve(import.meta.dirname, "../..");
const otaRoot = path.join(workspaceRoot, "ota-server");
const nativeRoot = path.join(workspaceRoot, "native");
const dataDir = path.join(otaRoot, ".verify-data");
const port = 18083;
const adminToken = "verification-admin-token";
const publicBase = `http://127.0.0.1:${port}/mobile-bundles`;
const apiBase = `http://127.0.0.1:${port}/mobile-updates`;

fs.rmSync(dataDir, {recursive: true, force: true});
fs.mkdirSync(dataDir, {recursive: true});

function run(command, args, options = {}) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      cwd: options.cwd,
      env: options.env,
      stdio: ["ignore", "pipe", "pipe"],
    });
    let stdout = "";
    let stderr = "";
    child.stdout.on("data", (b) => { stdout += String(b); });
    child.stderr.on("data", (b) => { stderr += String(b); });
    child.on("error", reject);
    child.on("exit", (code) => {
      if (code === 0) resolve({stdout, stderr});
      else reject(new Error(
        `${command} ${args.join(" ")} failed (${code})\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}`
      ));
    });
  });
}

const serverEnv = {
  ...process.env,
  DANOGO_MOBILE_OTA_HOST: "127.0.0.1",
  DANOGO_MOBILE_OTA_PORT: String(port),
  DANOGO_MOBILE_OTA_DATA_DIR: dataDir,
  DANOGO_MOBILE_BUNDLE_DIR: path.join(dataDir, "bundles"),
  DANOGO_MOBILE_OTA_ADMIN_TOKEN: adminToken,
};

const server = spawn(process.execPath, ["src/server.mjs"], {
  cwd: otaRoot,
  env: serverEnv,
  stdio: ["ignore", "pipe", "pipe"],
});

let serverStderr = "";
server.stderr.on("data", (b) => { serverStderr += String(b); });

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function waitForHealth() {
  for (let i = 0; i < 60; i += 1) {
    try {
      const response = await fetch(`http://127.0.0.1:${port}/health`);
      if (response.ok) return response.json();
    } catch {}
    await sleep(100);
  }
  throw new Error("verification_ota_health_timeout");
}

try {
  const health = await waitForHealth();

  const cliEnv = {
    ...process.env,
    DANOGO_MOBILE_OTA_INTERNAL_URL: apiBase,
    DANOGO_MOBILE_OTA_ADMIN_TOKEN: adminToken,
    DANOGO_MOBILE_BUNDLE_DIR: path.join(dataDir, "bundles"),
    DANOGO_MOBILE_BUNDLE_PUBLIC_BASE: publicBase,
  };

  const doctor = await run(
    "npx",
    ["hot-updater", "doctor", "--server-base-url", apiBase, "--json"],
    {cwd: nativeRoot, env: cliEnv},
  );

  const deploy = await run(
    "npx",
    [
      "hot-updater", "deploy",
      "--platform", "android",
      "--target-app-version", "1.0.0",
      "--channel", "verification/android",
      "--message", "DanoGo Mobile verification bundle",
      "--bundle-output-path", "build/verification",
    ],
    {cwd: nativeRoot, env: cliEnv},
  );

  const bundlesResponse = await fetch(`${apiBase}/api/bundles?limit=50`, {
    headers: {Authorization: `Bearer ${adminToken}`},
  });
  if (!bundlesResponse.ok) {
    throw new Error(`bundle_list_failed_${bundlesResponse.status}`);
  }
  const bundleList = await bundlesResponse.json();
  const bundles = Array.isArray(bundleList?.data)
    ? bundleList.data
    : Array.isArray(bundleList)
      ? bundleList
      : [];

  const verificationBundles = bundles.filter(
    (bundle) => bundle?.channel === "verification/android" && bundle?.platform === "android",
  );
  if (verificationBundles.length < 1) {
    throw new Error("verification_bundle_metadata_missing");
  }

  const newest = verificationBundles[0];
  if (typeof newest.storageUri !== "string" || !newest.storageUri.startsWith(publicBase + "/")) {
    throw new Error("verification_storage_uri_invalid");
  }

  const bundleResponse = await fetch(newest.storageUri);
  if (!bundleResponse.ok) {
    throw new Error(`verification_bundle_http_${bundleResponse.status}`);
  }
  const bundleBytes = (await bundleResponse.arrayBuffer()).byteLength;
  if (bundleBytes < 1000) {
    throw new Error("verification_bundle_too_small");
  }

  console.log(JSON.stringify({
    ok: true,
    health,
    doctor: JSON.parse(doctor.stdout || "{}"),
    deployedChannel: newest.channel,
    deployedPlatform: newest.platform,
    targetAppVersion: newest.targetAppVersion,
    bundleId: newest.id,
    storageUri: newest.storageUri,
    bundleBytes,
    deployOutputTail: deploy.stdout.trim().split(/\r?\n/).slice(-8),
  }, null, 2));
} finally {
  server.kill("SIGTERM");
  await sleep(300);
  fs.rmSync(dataDir, {recursive: true, force: true});
  if (serverStderr.trim()) process.stderr.write(serverStderr);
}
