#!/usr/bin/env node
import { execFile, spawn } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);
const distributionRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
const supportedArguments = new Set(["--json", "--verify-only"]);
const argumentsList = process.argv.slice(2);
const jsonOutput = argumentsList.includes("--json");
const verifyOnly = argumentsList.includes("--verify-only");

function previewError(code, message) {
  const error = new Error(message);
  error.code = code;
  return error;
}

function parseJsonOutput(value, code) {
  try {
    return JSON.parse(String(value || "").trim());
  } catch {
    throw previewError(code, "Runrail returned an invalid setup response.");
  }
}

async function runJson(command, args, { timeout }) {
  try {
    const result = await execFileAsync(command, args, {
      env: process.env,
      maxBuffer: 8 * 1024 * 1024,
      timeout,
      windowsHide: true
    });
    return parseJsonOutput(result.stdout, "runrail-preview-response-invalid");
  } catch (error) {
    if (error?.code && String(error.code).startsWith("runrail-preview-")) throw error;
    let refusal = null;
    try {
      refusal = parseJsonOutput(error?.stdout, "runrail-preview-response-invalid");
    } catch {
      refusal = null;
    }
    throw previewError(
      String(refusal?.code || "runrail-preview-command-failed"),
      String(refusal?.message || error?.message || "Runrail preview setup failed.")
    );
  }
}

async function launchDesktop(runnerPath) {
  const child = spawn(process.execPath, [runnerPath, "desktop"], {
    env: process.env,
    stdio: "inherit",
    windowsHide: false
  });
  return new Promise((resolve, reject) => {
    child.once("error", reject);
    child.once("exit", (code, signal) => {
      if (signal) {
        reject(previewError("runrail-preview-desktop-interrupted", `Runrail Desktop stopped after signal ${signal}.`));
        return;
      }
      resolve(Number.isInteger(code) ? code : 1);
    });
  });
}

try {
  const unsupported = argumentsList.filter((value) => !supportedArguments.has(value));
  if (unsupported.length > 0 || (jsonOutput && !verifyOnly)) {
    throw previewError(
      "runrail-preview-arguments-invalid",
      "Runrail preview setup accepts no inputs. --verify-only --json is reserved for package verification."
    );
  }

  const installation = await runJson(process.execPath, [path.join(distributionRoot, "install.mjs")], {
    timeout: 180_000
  });
  if (installation.ok !== true || installation.doctor?.ok !== true) {
    throw previewError("runrail-preview-installation-invalid", "Runrail installation or doctor verification did not pass.");
  }

  const runnerPath = path.join(installation.releaseDir, "bin", "runrail.mjs");
  const sample = await runJson(process.execPath, [runnerPath, "sample", "--json"], {
    timeout: 180_000
  });
  if (
    sample.ok !== true ||
    sample.guidedSample !== true ||
    sample.sourceApplyPerformed !== false ||
    sample.projectTestsExecuted !== false ||
    sample.repositoryUpload !== false
  ) {
    throw previewError("runrail-preview-sample-invalid", "Runrail guided sample verification did not pass its safety checks.");
  }

  const result = {
    schemaVersion: 1,
    artifactType: "runrail-preview-quickstart",
    ok: true,
    product: "Runrail Desktop",
    version: installation.upgrade?.toVersion,
    mode: "install-doctor-sample-open",
    platform: installation.platform,
    installationVerified: true,
    doctorPassed: true,
    guidedSamplePassed: true,
    sampleId: sample.sampleId,
    currentRepositoryCommand: installation.nextCommands?.desktopCurrentRepository,
    desktopLaunched: !verifyOnly,
    repositoryUpload: false,
    sourceApplyPerformed: false,
    projectTestsExecuted: false,
    providerRequired: false,
    authority: "none",
    executionAuthorityGranted: false
  };

  if (verifyOnly) {
    process.stdout.write(`${JSON.stringify(result, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(0);
  }

  process.stdout.write(
    "Runrail is installed and verified. The disposable local sample passed.\n" +
    "Opening Runrail Desktop now; no account is required for the sample.\n" +
    "To inspect the repository in your terminal later, run this command from that repository:\n" +
    `${result.currentRepositoryCommand}\n`
  );
  const exitCode = await launchDesktop(runnerPath);
  process.exit(exitCode);
} catch (error) {
  const payload = {
    ok: false,
    code: String(error?.code || "runrail-preview-setup-failed"),
    message: String(error?.message || "Runrail preview setup failed."),
    authority: "none",
    executionAuthorityGranted: false
  };
  const message = jsonOutput
    ? JSON.stringify(payload)
    : `Runrail preview setup failed [${payload.code}]: ${payload.message}`;
  process.stderr.write(`${message}\n`);
  process.exit(1);
}
