#!/usr/bin/env node
// Copyright (c) 2026 phluzz.com. All rights reserved.

import { createHash } from "node:crypto";
import { createWriteStream } from "node:fs";
import { access, chmod, copyFile, mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
import { get } from "node:https";
import { tmpdir, homedir } from "node:os";
import { dirname, join } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const dryRun = process.argv.includes("--dry-run");
const npmInstall = process.argv.includes("--npm");
const packageJson = JSON.parse(
  await readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"),
);
const version = process.env.WORKFLOW_VERSION ?? packageJson.version;
const tag = version.startsWith("v") ? version : `v${version}`;
const repo = process.env.WORKFLOW_RELEASE_REPO ?? "phluzz/workflow";
const baseUrl = process.env.WORKFLOW_RELEASE_BASE_URL ?? `https://github.com/${repo}/releases/download/${tag}`;
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
const packageBinDir = join(packageRoot, "bin");
const vendorDir = join(packageRoot, "vendor");
const installDir = process.env.WORKFLOW_INSTALL_DIR ?? (npmInstall ? packageBinDir : join(homedir(), ".workflow", "bin"));

const targets = new Map([
  ["darwin-arm64", { target: "aarch64-apple-darwin", binary: "workflow" }],
  ["darwin-x64", { target: "x86_64-apple-darwin", binary: "workflow" }],
  ["linux-x64", { target: "x86_64-unknown-linux-gnu", binary: "workflow" }],
  ["win32-x64", { target: "x86_64-pc-windows-msvc", binary: "workflow.exe" }],
]);

const platform = `${process.platform}-${process.arch}`;
const selected = targets.get(platform);
if (!selected) {
  console.error(`Unsupported platform: ${platform}`);
  process.exit(1);
}

const artifact = `workflow-${tag}-${selected.target}.tar.gz`;
if (dryRun) {
  console.log(`Would use bundled ${join(vendorDir, artifact)} when present`);
  console.log(`Would download ${baseUrl}/${artifact} when bundled artifact is absent`);
  console.log(`Would verify checksum from bundled or downloaded SHA256SUMS`);
  console.log(`Would install to ${join(installDir, selected.binary)}`);
  process.exit(0);
}

const tmpRoot = await mkdtemp(join(tmpdir(), "workflow-install-"));

try {
  const archivePath = join(tmpRoot, artifact);
  const sumsPath = join(tmpRoot, "SHA256SUMS");
  const vendorArchivePath = join(vendorDir, artifact);
  const vendorSumsPath = join(vendorDir, "SHA256SUMS");
  if ((await exists(vendorArchivePath)) && (await exists(vendorSumsPath))) {
    await copyFile(vendorArchivePath, archivePath);
    await copyFile(vendorSumsPath, sumsPath);
  } else {
    await download(`${baseUrl}/${artifact}`, archivePath);
    await download(`${baseUrl}/SHA256SUMS`, sumsPath);
  }
  await verifyChecksum(sumsPath, archivePath, artifact);
  extract(archivePath, tmpRoot);
  await mkdir(installDir, { recursive: true });
  const installedBinary = join(installDir, selected.binary);
  await copyFile(join(tmpRoot, `workflow-${tag}-${selected.target}`, selected.binary), installedBinary);
  await chmod(installedBinary, 0o755).catch(() => {});
  console.log(`Installed workflow to ${installedBinary}`);
  if (!npmInstall) {
    console.log(`Add ${installDir} to PATH if workflow is not already available.`);
  }
} finally {
  await rm(tmpRoot, { recursive: true, force: true });
}

async function exists(path) {
  try {
    await access(path);
    return true;
  } catch {
    return false;
  }
}

async function download(url, outPath) {
  if (url.startsWith("file://")) {
    await copyFile(fileURLToPath(url), outPath);
    return;
  }
  await new Promise((resolve, reject) => {
    const request = get(url, (response) => {
      if ([301, 302, 303, 307, 308].includes(response.statusCode ?? 0) && response.headers.location) {
        response.resume();
        download(response.headers.location, outPath).then(resolve, reject);
        return;
      }
      if (response.statusCode !== 200) {
        response.resume();
        reject(new Error(`Download failed ${response.statusCode}: ${url}`));
        return;
      }
      const file = createWriteStream(outPath, { mode: 0o600 });
      response.pipe(file);
      file.on("finish", () => file.close(resolve));
      file.on("error", reject);
    });
    request.on("error", reject);
  });
}

async function verifyChecksum(sumsPath, archivePath, artifactName) {
  const sums = await readFile(sumsPath, "utf8");
  const line = sums.split(/\r?\n/).find((entry) => entry.endsWith(`  ${artifactName}`));
  if (!line) {
    throw new Error(`Checksum for ${artifactName} not found in SHA256SUMS`);
  }
  const expected = line.trim().split(/\s+/)[0];
  const actual = createHash("sha256").update(await readFile(archivePath)).digest("hex");
  if (actual !== expected) {
    throw new Error(`Checksum verification failed for ${artifactName}`);
  }
}

function extract(archivePath, destination) {
  const result = spawnSync("tar", ["-xzf", archivePath, "-C", destination], { stdio: "inherit" });
  if (result.status !== 0) {
    throw new Error("Failed to extract workflow archive with tar");
  }
}
