// Where the app is, and the only place that knows.
//
// ── Why this file is .mjs ──
//
// Every process is bun except `officer-pty`, which pm2 launches with node (see
// ecosystem.config.cjs). Node cannot import TypeScript, so a .ts module here would have left the pty
// sidecar with its own copy — which is exactly the thing this file exists to end. Plain JS is
// importable by both, and `allowJs` in tsconfig.json means the TS callers still get types.
//
// ── Why there is no default ──
//
// There were twenty-two, and they disagreed: 5000 in the app and nineteen sidecars, 9010 in
// user-instance.ts, 9000 in .env.example. Each was defensible where it was written and none was
// visible from the others.
//
// A default is a guess at a value that .env always supplies. The one case it covers — PORT genuinely
// unset — is not a machine anyone wants running: it means .env was not loaded, so POSTGRES_URL is
// missing too and nothing works anyway. What a default buys there is a process that starts, binds
// somewhere unexpected and fails later for a reason that does not name the cause.
//
// So: no default anywhere, and this throws. Same posture as jwt.ts with JWT_SECRET.

const raw = process.env.PORT;

if (!raw) {
  throw new Error(
    [
      'PORT is not set.',
      '',
      'Officer reads it from .env, which bun auto-loads from the working directory. If this is a pm2',
      'process, the `cwd` pin in ecosystem.profile.cjs is what puts it there — a PORT this empty',
      'usually means .env was never loaded at all, and POSTGRES_URL is missing too.',
      '',
      'Set PORT in .env (officer-setup writes 9000), or start from the repo root.',
    ].join('\n'),
  );
}

const parsed = Number(raw);

if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
  throw new Error(`PORT must be an integer between 1 and 65535, but it is ${JSON.stringify(raw)}`);
}

/** The port the app binds. Everything else addresses it; nothing else binds. */
export const PORT = parsed;

// The app serves HTTP and WebSocket on ONE listener, so these are the same port in two protocols.
// Sidecars append their own path — `/api/sidecar/register` for the registration socket.
//
// The env overrides are kept because they are explicit escape hatches rather than defaults: nothing
// sets either today, and a value that is present is a deliberate act rather than a guess.
export const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${PORT}`;
export const OFFICER_API_URL = process.env.OFFICER_API_URL ?? `http://127.0.0.1:${PORT}`;

// ── The Anthropic proxy: PORT + 1, derived, not configured ──
//
// This is the ONE sidecar that binds a fixed port. Every other one binds `port: 0`, lets the kernel
// choose, and reports what it got back over the registration socket — which works because their
// consumer is the platform.
//
// The proxy cannot do that. Its consumer is `claude`, spawned by a DIFFERENT pm2 process
// (officer-agent), which needs ANTHROPIC_BASE_URL at spawn time and has no channel to ask the proxy
// what port it landed on. Two independent processes with nothing between them have to agree in
// advance, so the number has to be predictable rather than discovered.
//
// It was ANTHROPIC_PROXY_PORT, defaulting to 5051 in four separate files: the one that binds it and
// three that guessed the same constant to find it. 5051 was chosen against nothing and could collide
// with anything the owner installs later, with the symptom being chat failing while the rest of the
// platform looked healthy.
//
// PORT + 1 keeps the predictability and removes both problems. There is no variable to set, no second
// number to keep in agreement with the first, and the pair moves together when the install moves.
export const ANTHROPIC_PROXY_PORT = PORT + 1;
export const ANTHROPIC_PROXY_URL = `http://127.0.0.1:${ANTHROPIC_PROXY_PORT}`;
