const fs = require("node:fs");
const path = require("node:path");

function argValue(name, fallback = "") {
  const prefix = `--${name}=`;
  const match = process.argv.find((entry) => entry.startsWith(prefix));
  return match ? match.slice(prefix.length) : fallback;
}

function hasFlag(name) {
  return process.argv.includes(`--${name}`);
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function parseWaitSeconds(value) {
  if (value === "") return 0;
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed < 0) {
    throw new Error("--wait must be a non-negative integer number of seconds.");
  }
  return parsed;
}

function expectedSignature() {
  const mainPath = path.join(process.cwd(), "electron", "main.ts");
  const source = fs.readFileSync(mainPath, "utf8");
  const match = source.match(/APP_RUNTIME_SIGNATURE\s*=\s*"([^"]+)"/);
  return match?.[1] || "";
}

async function fetchJson(baseUrl, endpoint) {
  let response;
  try {
    response = await fetch(`${baseUrl}${endpoint}`);
  } catch (error) {
    throw new Error(`Could not reach ${baseUrl}${endpoint}. Start the elevated app with npm run dev:admin and confirm UAC before running live scans. (${error instanceof Error ? error.message : String(error)})`);
  }
  const text = await response.text();
  let payload;
  try {
    payload = text ? JSON.parse(text) : null;
  } catch {
    throw new Error(`${endpoint} returned non-JSON response (${response.status}).`);
  }
  if (!response.ok) throw new Error(`${endpoint} returned ${response.status}: ${JSON.stringify(payload)}`);
  return payload;
}

function validatePreflight({ health, status, expected, requireElevated = true, requireGenshin = true }) {
  const errors = [];
  const appBuild = health?.appBuild;
  const runtime = status?.status?.runtimeInfo;

  if (!appBuild?.signature) {
    errors.push("/health is missing appBuild.signature.");
  } else if (expected && appBuild.signature !== expected) {
    errors.push(`Runtime signature '${appBuild.signature}' does not match source '${expected}'.`);
  }

  if (!status?.status) errors.push("/scanner/status is missing status payload.");
  if (!runtime) {
    errors.push("/scanner/status is missing runtimeInfo. Open the scanner view and restart the elevated app if needed.");
  } else {
    if (requireElevated && runtime.isElevated !== true) errors.push("Runtime is not elevated.");
    if (requireGenshin && runtime.genshinFound !== true) errors.push("Genshin process/window was not found.");
  }

  return {
    ok: errors.length === 0,
    errors,
    signature: appBuild?.signature || "",
    expectedSignature: expected || "",
    isElevated: runtime?.isElevated,
    genshinFound: runtime?.genshinFound,
    targetProcess: runtime?.targetProcess || "",
    foregroundProcess: runtime?.foregroundProcess || "",
  };
}

function formatSummary(result) {
  const lines = [
    `live preflight: ${result.ok ? "PASS" : "FAIL"}`,
    `signature: ${result.signature || "missing"}`,
    `expected: ${result.expectedSignature || "unknown"}`,
    `elevated: ${result.isElevated === true ? "yes" : result.isElevated === false ? "no" : "unknown"}`,
    `genshin: ${result.genshinFound === true ? "yes" : result.genshinFound === false ? "no" : "unknown"}`,
    `target: ${result.targetProcess || "unknown"}`,
    `foreground: ${result.foregroundProcess || "unknown"}`,
  ];
  if (!result.ok) {
    lines.push("errors:");
    for (const error of result.errors) lines.push(`- ${error}`);
  }
  return lines.join("\n");
}

async function runPreflight({ baseUrl, expected, requireElevated, requireGenshin }) {
  const health = await fetchJson(baseUrl, "/health");
  const status = await fetchJson(baseUrl, "/scanner/status");
  return validatePreflight({ health, status, expected, requireElevated, requireGenshin });
}

async function waitForPreflight(options, waitSeconds) {
  const deadline = Date.now() + waitSeconds * 1000;
  let lastError = null;
  let lastResult = null;

  while (true) {
    try {
      const result = await runPreflight(options);
      lastResult = result;
      if (result.ok || Date.now() >= deadline) return result;
    } catch (error) {
      lastError = error;
      if (Date.now() >= deadline) throw lastError;
    }
    await sleep(1000);
  }
}

async function main() {
  const baseUrl = argValue("base-url", "http://127.0.0.1:17317").replace(/\/$/, "");
  const expected = argValue("expected-signature", expectedSignature());
  const requireElevated = !hasFlag("allow-standard");
  const requireGenshin = !hasFlag("allow-missing-genshin");
  const waitSeconds = parseWaitSeconds(argValue("wait", ""));
  const options = { baseUrl, expected, requireElevated, requireGenshin };
  const result = waitSeconds > 0 ? await waitForPreflight(options, waitSeconds) : await runPreflight(options);
  console.log(hasFlag("json") ? JSON.stringify(result, null, 2) : formatSummary(result));
  if (!result.ok) process.exit(1);
}

if (require.main === module) {
  main().catch((error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
  });
}

module.exports = {
  formatSummary,
  parseWaitSeconds,
  runPreflight,
  validatePreflight,
};
