// Smoke test for scripts/local-setup.sh — verifies idempotency and that
// the .env upserts preserve unrelated keys. Uses a stubbed `mkcert` on
// the runner so no real CA is touched.
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, writeFileSync, readFileSync, copyFileSync, mkdirSync, existsSync, chmodSync, cpSync, rmSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";

// Resolve the real local-setup.sh from this test file's location so the
// test works regardless of pnpm/node cwd.
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const LOCAL_SETUP_SH = path.resolve(__dirname, "..", "local-setup.sh");

function runLocalSetup(projectDir, extraEnv = {}) {
  const stubDir = path.join(projectDir, ".stub-bin");
  mkdirSync(stubDir, { recursive: true });

  // mkcert stub: writes minimal PEM-looking files so the script's
  // existence check is satisfied. Also implements `-CAROOT`.
  const mkcertStub = `#!/usr/bin/env bash
set -e
if [ "$1" = "-CAROOT" ]; then
  echo "/tmp/fake-caroot"
  exit 0
fi
cert=""; key=""
positional=()
while [ $# -gt 0 ]; do
  case "$1" in
    -cert-file) cert="$2"; shift 2;;
    -key-file)  key="$2";  shift 2;;
    *) positional+=("$1"); shift;;
  esac
done
san=""
for s in "\${positional[@]}"; do
  if [[ "$s" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then
    if [ -z "$san" ]; then san="IP:$s"; else san="$san,IP:$s"; fi
  else
    if [ -z "$san" ]; then san="DNS:$s"; else san="$san,DNS:$s"; fi
  fi
done
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \\
  -subj "/CN=local-test" \\
  -addext "subjectAltName=$san" \\
  -keyout "$key" -out "$cert" >/dev/null 2>&1
`;
  const stubPath = path.join(stubDir, "mkcert");
  writeFileSync(stubPath, mkcertStub);
  chmodSync(stubPath, 0o755);

  return spawnSync("bash", [path.join(projectDir, "scripts", "local-setup.sh")], {
    cwd: projectDir,
    env: {
      ...process.env,
      PATH: `${stubDir}:${process.env.PATH}`,
      LOCAL_SETUP_DRY_RUN: "1",
      LOCAL_SETUP_NONINTERACTIVE: "1",
      OSTYPE: "linux-gnu",
      ...extraEnv,
    },
    encoding: "utf8",
  });
}

function makeFakeProject() {
  const dir = mkdtempSync(path.join(tmpdir(), "tx-localsetup-"));
  mkdirSync(path.join(dir, "scripts"), { recursive: true });
  // Copy the script under test verbatim.
  copyFileSync(
    LOCAL_SETUP_SH,
    path.join(dir, "scripts", "local-setup.sh"),
  );
  chmodSync(path.join(dir, "scripts", "local-setup.sh"), 0o755);

  // Minimal .env.example so bootstrap works.
  writeFileSync(
    path.join(dir, ".env.example"),
    [
      "SESSION_SECRET=change-me",
      "POSTGRES_PASSWORD=change-me",
      "SOME_USER_KEY=preserve-me",
      "LOCAL_DOMAIN=tx.local",
      "LOCAL_IP=127.0.0.1",
      "BASE_URL=https://tx.local",
      "PUBLIC_BASE_URL=https://tx.local",
      "ALLOWED_ORIGINS=https://tx.local",
      "HTTPS_MODE=local",
      "",
    ].join("\n"),
  );
  return dir;
}

test("first run bootstraps .env and writes a cert", () => {
  const dir = makeFakeProject();
  try {
    const r = runLocalSetup(dir);
    assert.equal(r.status, 0, r.stderr || r.stdout);
    const env = readFileSync(path.join(dir, ".env"), "utf8");
    assert.match(env, /^LOCAL_DOMAIN=tx\.local$/m);
    assert.match(env, /^BASE_URL=https:\/\/tx\.local$/m);
    assert.match(env, /^PUBLIC_BASE_URL=https:\/\/tx\.local$/m);
    assert.match(env, /^SOME_USER_KEY=preserve-me$/m);
    assert.ok(existsSync(path.join(dir, "certs", "local-cert.pem")));
    assert.ok(existsSync(path.join(dir, "certs", "local-key.pem")));
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
});

test("second run is a no-op for cert generation and preserves user edits", () => {
  const dir = makeFakeProject();
  try {
    const first = runLocalSetup(dir);
    assert.equal(first.status, 0, first.stderr || first.stdout);

    // Operator edits an unrelated key.
    let env = readFileSync(path.join(dir, ".env"), "utf8");
    env = env.replace(/^SOME_USER_KEY=.*$/m, "SOME_USER_KEY=user-edited");
    writeFileSync(path.join(dir, ".env"), env);

    // Capture cert mtime to verify second run didn't rewrite it.
    const certPath = path.join(dir, "certs", "local-cert.pem");
    const before = readFileSync(certPath);

    const second = runLocalSetup(dir);
    assert.equal(second.status, 0, second.stderr || second.stdout);

    const after = readFileSync(certPath);
    assert.deepEqual(before, after, "cert was rewritten on idempotent re-run");

    const env2 = readFileSync(path.join(dir, ".env"), "utf8");
    assert.match(env2, /^SOME_USER_KEY=user-edited$/m);
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
});
