#!/usr/bin/env node
import { createHash } from "node:crypto";
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { fileURLToPath } from "node:url";

const binRoot = path.dirname(fileURLToPath(import.meta.url));
const distributionRoot = path.resolve(process.env.RUNRAIL_DISTRIBUTION_ROOT || path.resolve(binRoot, ".."));
const installationStateName = "RUNRAIL_INSTALLATION.json";
const jsonOutput = process.argv.includes("--json");
const nodeMajorVersion = Number.parseInt(String(process.versions.node || "").split(".", 1)[0], 10);
if (!Number.isSafeInteger(nodeMajorVersion) || nodeMajorVersion < 20) {
  const payload = {
    ok: false,
    code: "runrail-node-version-unsupported",
    message: `Runrail requires Node.js 20 or newer; found ${process.versions.node || "unknown"}.`,
    authority: "none"
  };
  process.stderr.write(jsonOutput ? `${JSON.stringify(payload)}\n` : `${payload.message}\n`);
  process.exit(1);
}

function sha256(value) {
  return createHash("sha256").update(value).digest("hex");
}

function stableValue(value) {
  if (Array.isArray(value)) return value.map(stableValue);
  if (value && typeof value === "object") {
    return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
  }
  return value;
}

function stableJson(value) {
  return `${JSON.stringify(stableValue(value), null, 2)}\n`;
}

function pathIdentity(value) {
  if (typeof value !== "string" || !value.trim()) return "";
  const resolved = path.resolve(value);
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
}

function samePath(left, right) {
  return pathIdentity(left) === pathIdentity(right);
}

function compareUtf8Names(left, right) {
  return Buffer.compare(Buffer.from(left, "utf8"), Buffer.from(right, "utf8"));
}

async function existingPath(target) {
  try {
    return await lstat(target);
  } catch (error) {
    if (error?.code === "ENOENT") return null;
    throw error;
  }
}

async function ownedPathDigest(target, relative = ".") {
  const info = await lstat(target);
  if (info.isSymbolicLink()) throw installOwnershipError();
  const digest = createHash("sha256");
  if (info.isDirectory()) {
    digest.update(`${relative}\0directory\0`);
    const entries = (await readdir(target)).sort(compareUtf8Names);
    for (const name of entries) {
      digest.update(await ownedPathDigest(path.join(target, name), path.posix.join(relative, name)));
    }
  } else if (info.isFile()) {
    digest.update(`${relative}\0file\0`);
    digest.update(await readFile(target));
  } else {
    throw installOwnershipError();
  }
  return digest.digest();
}

async function ownedPathSha256(target) {
  try {
    return (await ownedPathDigest(target)).toString("hex");
  } catch {
    throw installOwnershipError();
  }
}

function installOwnershipError() {
  const error = new Error("runrail-install-ownership-invalid");
  error.code = "runrail-install-ownership-invalid";
  return error;
}

function expectedLauncherPaths(binDir) {
  if (process.platform === "win32") {
    return [
      path.join(binDir, "runrail.cmd"),
      path.join(binDir, "runrail.ps1"),
      path.join(binDir, "runrail-desktop.cmd"),
      path.join(binDir, "runrail-desktop.ps1")
    ];
  }
  return [path.join(binDir, "runrail"), path.join(binDir, "runrail-desktop")];
}

function expectedDesktopEntry(entryDir) {
  if (process.platform === "win32") {
    return {
      kind: "windows-start-menu",
      path: path.join(entryDir, "Runrail Desktop.lnk"),
      markerPath: path.join(entryDir, "Runrail Desktop.runrail.json")
    };
  }
  if (process.platform === "darwin") {
    const appPath = path.join(entryDir, "Runrail Desktop.app");
    return {
      kind: "macos-application",
      path: appPath,
      markerPath: path.join(appPath, "Contents", "Resources", "RUNRAIL_DESKTOP_APP.json")
    };
  }
  return {
    kind: "linux-desktop-entry",
    path: path.join(entryDir, "runrail-desktop.desktop"),
    markerPath: path.join(entryDir, "runrail-desktop.runrail.json")
  };
}

async function managedInstallWorkspace(root) {
  const releaseDir = await realpath(root);
  const releasesRoot = path.dirname(releaseDir);
  const expectedReleaseName = process.platform === "win32" ? path.basename(releaseDir).toLowerCase() : path.basename(releaseDir);
  const expectedReleasesName = process.platform === "win32"
    ? path.basename(releasesRoot).toLowerCase()
    : path.basename(releasesRoot);
  if (expectedReleaseName !== "v1" || expectedReleasesName !== "releases") return null;

  const installBase = path.dirname(releasesRoot);
  const currentPath = path.join(installBase, "current");
  const statePath = path.join(installBase, installationStateName);
  const [currentInfo, stateInfo] = await Promise.all([existingPath(currentPath), existingPath(statePath)]);
  if (!currentInfo && !stateInfo) return null;
  if (
    !currentInfo?.isSymbolicLink() ||
    !stateInfo?.isFile() ||
    stateInfo.isSymbolicLink()
  ) {
    throw installOwnershipError();
  }

  let state;
  try {
    state = JSON.parse(await readFile(statePath, "utf8"));
  } catch {
    throw installOwnershipError();
  }
  if (!state || typeof state !== "object" || Array.isArray(state)) {
    throw installOwnershipError();
  }
  const { checksumSha256, ...body } = state;
  const workspacePath = path.join(installBase, "workspace");
  const launchers = expectedLauncherPaths(state.binDir || "");
  const desktopEntry = expectedDesktopEntry(state.desktopEntryDir || "");
  const stateLauncherPaths = Array.isArray(state.launchers)
    ? state.launchers.map((entry) => String(entry?.path || ""))
    : [];
  if (
    state.schemaVersion !== 1 ||
    state.artifactType !== "runrail-installation-ownership" ||
    state.product !== "Runrail Local Audit Runner" ||
    state.managed !== true ||
    state.version !== "2.4.14" ||
    state.platform !== process.platform ||
    !samePath(state.installBase, installBase) ||
    !samePath(state.releaseDir, releaseDir) ||
    !samePath(state.currentPath, currentPath) ||
    !path.isAbsolute(String(state.binDir || "")) ||
    !path.isAbsolute(String(state.desktopEntryDir || "")) ||
    JSON.stringify(stateLauncherPaths.map(pathIdentity)) !== JSON.stringify(launchers.map(pathIdentity)) ||
    state.desktopLauncher?.kind !== desktopEntry.kind ||
    !samePath(state.desktopLauncher?.path || "", desktopEntry.path) ||
    !samePath(state.desktopLauncher?.markerPath || "", desktopEntry.markerPath) ||
    !Array.isArray(state.preservedByDefault) ||
    JSON.stringify(state.preservedByDefault.map(pathIdentity)) !==
      JSON.stringify([pathIdentity(workspacePath)]) ||
    checksumSha256 !== sha256(stableJson(body))
  ) {
    throw installOwnershipError();
  }

  for (let index = 0; index < launchers.length; index += 1) {
    const record = state.launchers[index];
    if (
      !/^[a-f0-9]{64}$/.test(String(record?.sha256 || "")) ||
      (await ownedPathSha256(launchers[index])) !== record.sha256
    ) {
      throw installOwnershipError();
    }
  }
  if (
    !/^[a-f0-9]{64}$/.test(String(state.desktopLauncher?.targetSha256 || "")) ||
    !/^[a-f0-9]{64}$/.test(String(state.desktopLauncher?.markerSha256 || "")) ||
    (await ownedPathSha256(desktopEntry.path)) !== state.desktopLauncher.targetSha256 ||
    (await ownedPathSha256(desktopEntry.markerPath)) !== state.desktopLauncher.markerSha256
  ) {
    throw installOwnershipError();
  }

  let currentTarget;
  try {
    currentTarget = await realpath(currentPath);
  } catch {
    throw installOwnershipError();
  }
  if (!samePath(currentTarget, releaseDir)) throw installOwnershipError();
  return {
    workspacePath,
    lifecycleSha256: sha256(stableJson({
      installationStateSha256: checksumSha256,
      launchers: state.launchers,
      desktopLauncher: state.desktopLauncher
    }))
  };
}

function defaultInstallBase() {
  if (process.platform === "win32") {
    return path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "Runrail");
  }
  if (process.platform === "darwin") {
    return path.join(os.homedir(), "Library", "Application Support", "Runrail");
  }
  return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share"), "runrail");
}

const pythonExecutable = process.platform === "win32"
  ? path.join(distributionRoot, "python-env", "Scripts", "python.exe")
  : path.join(distributionRoot, "python-env", "bin", "python");

process.env.RUNRAIL_ENGINE_ROOT ||= path.join(distributionRoot, "engine");
const configuredOperatorRoot = String(process.env.RUNRAIL_OPERATOR_ROOT || "").trim();
const configuredRunrailHome = String(process.env.RUNRAIL_HOME || "").trim();
const managedInstall = await managedInstallWorkspace(distributionRoot);
if (managedInstall) {
  process.env.RUNRAIL_INSTALL_LIFECYCLE_VERIFIED = managedInstall.lifecycleSha256;
}
if (!configuredOperatorRoot) {
  process.env.RUNRAIL_OPERATOR_ROOT = configuredRunrailHome ||
    managedInstall?.workspacePath ||
    path.join(defaultInstallBase(), "workspace");
}
process.env.RUNRAIL_PYTHON ||= pythonExecutable;
process.env.RUNRAIL_DISTRIBUTION_ROOT ||= distributionRoot;
process.env.RUNRAIL_CLI_ENTRY ||= fileURLToPath(import.meta.url);
process.env.PATH = `${path.join(distributionRoot, "runtime-bin")}${path.delimiter}${process.env.PATH || ""}`;

try {
  const { verifyRunrailPythonRuntime } = await import(
    pathToFileURL(path.join(distributionRoot, "scripts", "runrail-python-runtime-core.mjs")).href
  );
  const pythonRuntime = await verifyRunrailPythonRuntime(distributionRoot);
  process.env.RUNRAIL_PYTHON_RUNTIME_VERIFIED = pythonRuntime.manifestSha256;
} catch (error) {
  const payload = {
    ok: false,
    code: String(error?.code || error?.message || "runrail-python-runtime-invalid"),
    message: "The installed Runrail Python runtime failed its integrity check. Reinstall Runrail before continuing.",
    authority: "none"
  };
  if (jsonOutput) process.stderr.write(`${JSON.stringify(payload)}\n`);
  else process.stderr.write(`Runrail failed [${payload.code}]: ${payload.message}\n`);
  process.exit(1);
}

const {
  assertRunrailLocalDesktopAccess,
  assertRunrailLocalFeatureAccess,
  createRunrailDeviceEnrollmentRequest,
  enrollRunrailDeviceFromTicket,
  renewRunrailDeviceEntitlement,
  runrailLocalFeatureForArgs,
  runrailLocalEntitlementStatus
} = await import(pathToFileURL(path.join(distributionRoot, "scripts", "runrail-local-entitlement-core.mjs")).href);

function licenseUsageError(message) {
  const error = new Error(message);
  error.code = "runrail-license-usage-invalid";
  return error;
}

function parseLicenseArgs(argv) {
  const values = [...argv];
  const action = values[0] && !values[0].startsWith("-")
    ? String(values.shift()).trim().toLowerCase()
    : "status";
  if (!["status", "request", "enroll", "renew"].includes(action)) {
    throw licenseUsageError("License action must be status, request, enroll, or renew.");
  }
  let ticketPath = "";
  let outputPath = "";
  for (let index = 0; index < values.length; index += 1) {
    const value = values[index];
    if (value === "--json") continue;
    if (value === "--ticket" && action === "enroll" && !ticketPath) {
      ticketPath = values[++index] || "";
      if (!ticketPath || ticketPath.startsWith("-")) {
        throw licenseUsageError("--ticket requires an absolute file path.");
      }
      continue;
    }
    if (value === "--output" && action === "request" && !outputPath) {
      outputPath = values[++index] || "";
      if (!outputPath || outputPath.startsWith("-")) {
        throw licenseUsageError("--output requires an absolute file path.");
      }
      continue;
    }
    throw licenseUsageError(`Unsupported license argument: ${value}`);
  }
  if (action === "enroll" && !ticketPath) {
    throw licenseUsageError("License enrollment requires --ticket with an absolute ticket path.");
  }
  return { action, ticketPath, outputPath };
}

function readinessUsageError(message) {
  const error = new Error(message);
  error.code = "runrail-readiness-usage-invalid";
  return error;
}

function parseReadinessArgs(argv) {
  const values = [...argv];
  let repo = "";
  let base = "";
  for (let index = 0; index < values.length; index += 1) {
    const value = values[index];
    if (value === "--json") continue;
    if (value === "--repo" && !repo) {
      repo = values[++index] || "";
      if (!repo || repo.startsWith("-")) {
        throw readinessUsageError(
          '--repo requires "." for the current directory or a normalized absolute local Git worktree path.'
        );
      }
      if (repo === ".") {
        repo = process.cwd();
      } else if (!path.isAbsolute(repo)) {
        throw readinessUsageError(
          '--repo accepts only "." for the current directory or a normalized absolute local Git worktree path.'
        );
      }
      repo = path.normalize(repo);
      continue;
    }
    if (value === "--base" && !base) {
      base = values[++index] || "";
      if (!base || base.startsWith("-")) {
        throw readinessUsageError("--base requires one explicit local Git reference.");
      }
      continue;
    }
    throw readinessUsageError(`Unsupported readiness argument: ${value}`);
  }
  if (!repo) {
    throw readinessUsageError(
      'Repository readiness requires --repo with "." for the current directory or an absolute local Git worktree path.'
    );
  }
  return { repo, base };
}

function renderReadinessHuman(result, handoffText) {
  const checkLines = result.readiness.checks.map((check) => {
    const mark = check.status === "pass" ? "PASS" : check.status === "blocked" ? "BLOCKED" : "CHECK";
    return `${mark.padEnd(7)} ${check.label}: ${check.detail}`;
  });
  const baseLines = result.localSelection.baseCandidates.length
    ? ["Local base choices:", ...result.localSelection.baseCandidates.map((entry) => `- ${entry.ref} (${entry.source})`)]
    : ["Local base choices: none detected"];
  const candidateLines = result.localSelection.candidateBranches.length
    ? ["Local candidate choices:", ...result.localSelection.candidateBranches.map((entry) => `- ${entry.ref} (${entry.aheadCommitCount} commits ahead)`)]
    : ["Local candidate choices: none detected"];
  return [
    `Runrail Repository Readiness: ${result.readiness.label}`,
    ...checkLines,
    ...baseLines,
    ...candidateLines,
    "",
    handoffText,
    "",
    "Nothing was stored, uploaded, executed, or changed."
  ].join("\n");
}

const command = String(process.argv[2] || "").trim().toLowerCase();
if (command === "license") {
  try {
    const license = parseLicenseArgs(process.argv.slice(3));
    let result;
    if (license.action === "request") {
      result = await createRunrailDeviceEnrollmentRequest({
        outputPath: license.outputPath || undefined
      });
    } else if (license.action === "enroll") {
      result = await enrollRunrailDeviceFromTicket({
        ticketPath: license.ticketPath
      });
    } else if (license.action === "renew") {
      result = await renewRunrailDeviceEntitlement();
    } else {
      result = await runrailLocalEntitlementStatus();
    }
    process.stdout.write(`${JSON.stringify(result, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(0);
  } catch (error) {
    const payload = {
      ok: false,
      code: error?.code || "runrail-entitlement-invalid",
      message: String(error?.message || "Runrail entitlement validation failed."),
      authority: "none"
    };
    process.stdout.write(`${JSON.stringify(payload, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(1);
  }
}

if (command === "readiness") {
  try {
    const readinessArgs = parseReadinessArgs(process.argv.slice(3));
    const {
      buildRunrailRepositoryReadinessHandoff,
      buildRunrailRepositoryReadinessReceipt,
      inspectRunrailReviewRepository,
      renderRunrailRepositoryReadinessHandoff
    } = await import(
      pathToFileURL(path.join(distributionRoot, "scripts", "runrail-review-session-core.mjs")).href
    );
    const initialInspection = await inspectRunrailReviewRepository({ repo: readinessArgs.repo });
    const selectedBase = readinessArgs.base ||
      initialInspection.defaultBaseCandidates.at(0)?.ref ||
      null;
    const inspection = selectedBase
      ? await inspectRunrailReviewRepository({ repo: readinessArgs.repo, base: selectedBase })
      : initialInspection;
    const readiness = buildRunrailRepositoryReadinessReceipt(inspection);
    const handoff = buildRunrailRepositoryReadinessHandoff(readiness);
    const result = {
      ok: true,
      command: "readiness",
      readiness,
      handoff,
      localSelection: {
        baseCandidates: inspection.defaultBaseCandidates.map((entry) => ({
          ref: entry.ref,
          source: entry.source
        })),
        candidateBranches: inspection.candidateLocalBranches.map((entry) => ({
          ref: entry.ref,
          aheadCommitCount: entry.aheadCommitCount
        })),
        candidateBranchesTruncated: inspection.candidateLocalBranchesTruncated,
        languages: inspection.languageStack.languages.map((entry) => ({
          id: entry.id,
          label: entry.label,
          fileCount: entry.fileCount
        }))
      },
      boundaries: {
        localOnly: true,
        stored: false,
        pathIncluded: false,
        branchSelectionIncluded: true,
        commitIncluded: false,
        sourceIncluded: false,
        diffIncluded: false,
        historyLoaded: false,
        repositoryFingerprintCreated: false,
        networkUsed: false,
        modelUsed: false,
        providerUsed: false,
        projectTestsExecuted: false,
        sourceMutation: false,
        authority: "none"
      }
    };
    process.stdout.write(jsonOutput
      ? `${JSON.stringify(result, null, 2)}\n`
      : `${renderReadinessHuman(result, renderRunrailRepositoryReadinessHandoff(handoff))}\n`);
    process.exit(0);
  } catch (error) {
    const payload = {
      ok: false,
      code: error?.code || "runrail-readiness-invalid",
      message: String(error?.message || "Runrail repository readiness stopped."),
      authority: "none"
    };
    process.stdout.write(`${JSON.stringify(payload, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(1);
  }
}

const requiredFeature = runrailLocalFeatureForArgs(process.argv.slice(2));
if (command === "desktop") {
  try {
    await assertRunrailLocalDesktopAccess();
  } catch (error) {
    const payload = {
      ok: false,
      code: error?.code || "runrail-entitlement-invalid",
      message: String(error?.message || "Runrail entitlement validation failed."),
      authority: "none"
    };
    process.stdout.write(`${JSON.stringify(payload, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(1);
  }
} else if (requiredFeature && command !== "sample") {
  try {
    const access = await assertRunrailLocalFeatureAccess(requiredFeature, {
      opportunisticRenewal: true
    });
    if (access.renewalWarning) {
      const warning = {
        ok: true,
        warning: access.renewalWarning,
        authority: "subscription-entitlement-only",
        executionAuthorityGranted: false
      };
      if (jsonOutput) process.stderr.write(`${JSON.stringify(warning)}\n`);
      else process.stderr.write(`Runrail warning [${warning.warning.code}]: ${warning.warning.message}\n`);
    }
  } catch (error) {
    const payload = {
      ok: false,
      code: error?.code || "runrail-entitlement-invalid",
      message: String(error?.message || "Runrail entitlement validation failed."),
      authority: "none"
    };
    process.stdout.write(`${JSON.stringify(payload, null, jsonOutput ? 2 : 0)}\n`);
    process.exit(1);
  }
}

await import(pathToFileURL(path.join(distributionRoot, "scripts", "veyact-local-audit-runner.mjs")).href);
