// Which platforms the bundled PostgreSQL runtime exists for — PG Task 1 of the
// bundled-Postgres milestone (docs/todo/2026-08-08-bundled-postgres-decisions.md,
// docs/todo/2026-08-05-install-and-macos.md).
//
// This module is PURE on purpose: it takes `process.platform` / `process.arch`
// as ARGUMENTS rather than reading them. Two reasons, and the second is the
// real one:
//
//  1. It can be unit-tested for every platform from whatever machine happens to
//     be running the suite. The macOS layout has to be assertable from Windows,
//     because the owner's Mac is not available yet (2026-08-05 note) and code
//     nobody can run is code nobody can check.
//  2. The maintainer-side release tooling needs to reason about a platform that
//     is NOT the one it is running on — it trims and republishes artifacts for
//     targets it will never boot. A module that reads `process` directly can
//     only ever answer "what am I", which is the smaller of the two questions.
//
// It returns a DISCRIMINATED RESULT rather than throwing. An unsupported OS is
// not an exceptional condition here — it is a person on Linux who downloaded a
// zip and deserves a sentence explaining that we do not publish one, not a
// stack trace. The caller owns the wording of the final message; this module
// owns the facts that go in it.

/** The platform ids that name a runtime artifact and a `runtime/` directory.
 *  These strings are LOAD-BEARING: they appear in the on-disk directory name
 *  (`runtime/postgresql-16.10-win32-x64/`) and in `instance.json`, so renaming
 *  one orphans an existing install's runtime directory. Add, never rename. */
export type PlatformId = "win32-x64" | "darwin-arm64" | "darwin-x64";

/** Every supported platform id, in the order they are documented. Exported so
 *  the manifest can be typed as a total Record and a missing row becomes a
 *  compile error rather than an undefined at runtime. */
export const PLATFORM_IDS: readonly PlatformId[] = ["win32-x64", "darwin-arm64", "darwin-x64"];

export function isPlatformId(value: unknown): value is PlatformId {
  return typeof value === "string" && (PLATFORM_IDS as readonly string[]).includes(value);
}

export type PlatformResult =
  | { ok: true; platform: PlatformId }
  | {
      ok: false;
      /** `unsupported-os` = we publish nothing for this operating system at all
       *  (Linux). `unsupported-arch` = we support the OS but not this CPU
       *  (Windows on ARM, 32-bit anything). They are kept apart because the
       *  honest answer differs: one is "we don't ship for your OS", the other
       *  is "we ship for your OS, just not your chip". */
      reason: "unsupported-os" | "unsupported-arch";
      /** Echoed back verbatim so the caller can put the raw values in a
       *  bug report without having to thread them through separately. */
      nodePlatform: string;
      nodeArch: string;
      /** Plain English, CLAUDE.md §9 ("Errors in English"). Complete
       *  sentences — callers render this directly. */
      message: string;
    };

/** Human names for the operating systems we actually recognise. Anything else
 *  is echoed as its raw `process.platform` value, which is uglier but true —
 *  inventing a friendly name for an OS we have never tested on would be the
 *  same unverified-claim mistake this project keeps stripping out of the site. */
function osName(nodePlatform: string): string {
  switch (nodePlatform) {
    case "win32": return "Windows";
    case "darwin": return "macOS";
    case "linux": return "Linux";
    default: return nodePlatform;
  }
}

/**
 * Maps a `process.platform` / `process.arch` pair onto one of our runtime
 * artifact platforms.
 *
 * Linux is deliberately a failure, not an omission. The bundled runtime is a
 * trimmed EnterpriseDB build that we download, checksum and run; we have never
 * built or tested a Linux one, and "it's Postgres, it'll be fine" is exactly
 * the kind of claim `2026-08-05-install-and-macos.md` says we do not make. A
 * Linux user is far better served by "install PostgreSQL from your package
 * manager" than by a runtime we cannot vouch for.
 *
 * macOS resolves successfully here even though no macOS artifact is published
 * yet — that is manifest.ts's question, not this one's. Keeping the two apart
 * means the day the Mac build lands, only one table changes.
 */
export function resolvePlatform(nodePlatform: string, nodeArch: string): PlatformResult {
  if (nodePlatform === "win32") {
    if (nodeArch === "x64") return { ok: true, platform: "win32-x64" };
    return {
      ok: false,
      reason: "unsupported-arch",
      nodePlatform,
      nodeArch,
      message:
        `Balanced Sum bundles a 64-bit PostgreSQL for Windows, and this machine reports "${nodeArch}". ` +
        "There's no build for it yet.",
    };
  }

  if (nodePlatform === "darwin") {
    if (nodeArch === "arm64") return { ok: true, platform: "darwin-arm64" };
    if (nodeArch === "x64") return { ok: true, platform: "darwin-x64" };
    return {
      ok: false,
      reason: "unsupported-arch",
      nodePlatform,
      nodeArch,
      message:
        `Balanced Sum bundles PostgreSQL for Apple Silicon and Intel Macs, and this machine reports ` +
        `"${nodeArch}". There's no build for it yet.`,
    };
  }

  return {
    ok: false,
    reason: "unsupported-os",
    nodePlatform,
    nodeArch,
    message:
      `Balanced Sum bundles PostgreSQL for Windows and macOS. It doesn't ship a build for ${osName(nodePlatform)}, ` +
      "so it can't start a database for you here. Everything else about the app works against a PostgreSQL you " +
      "install yourself — point DATABASE_URL at it and the app will use it.",
  };
}
