/**
 * Boot-time guard for the Redis connection string. Same family as
 * `check-database-url.ts` / `check-entra-config.ts` /
 * `check-session-secret.ts` — fails before NestFactory if the URL
 * is missing or obviously malformed, so the contributor sees one
 * clear message instead of a `ioredis` reconnect storm.
 *
 * v1 understands the single-instance form (`redis://[user]:pass@host:port/db`).
 * Sentinel-style configuration (`REDIS_SENTINEL_HOSTS` +
 * `REDIS_SENTINEL_NAME` per ADR-0010) lands when the production
 * deploy ADR ships; until then the validator only accepts URLs.
 */

const PLACEHOLDER_PASSWORD = 'redis_dev_change_me';

export interface RedisConfig {
  /** Original URL as configured; consumed by `ioredis`'s URL ctor. */
  readonly url: string;
  /** Parsed host (for log lines and the future readiness probe). */
  readonly host: string;
  /** Parsed port (defaults to 6379 when the URL omits it). */
  readonly port: number;
}

export function assertRedisConfig(): RedisConfig {
  const raw = process.env['REDIS_URL'];
  if (!raw) {
    throw new Error(
      'REDIS_URL is not set. Copy apps/portal-bff/.env.example to ' +
        'apps/portal-bff/.env and adjust to match infra/local/.env ' +
        '(REDIS_PASSWORD + REDIS_PORT).',
    );
  }

  let parsed: URL;
  try {
    parsed = new URL(raw);
  } catch {
    throw new Error(
      `REDIS_URL is not a valid URL. Expected ` +
        `"redis://[user]:<password>@<host>:<port>/<db>"; got: ${truncate(raw)}`,
    );
  }

  if (parsed.protocol !== 'redis:' && parsed.protocol !== 'rediss:') {
    throw new Error(
      `REDIS_URL must use the "redis://" or "rediss://" scheme; got "${parsed.protocol}".`,
    );
  }

  // The local Compose stack requires a non-default password (the
  // compose refuses to boot without it). A REDIS_URL without one
  // would only connect to a non-authenticated Redis — surface the
  // mismatch up-front rather than at the first connection attempt.
  if (!parsed.password) {
    throw new Error(
      `REDIS_URL has no password. The local dev stack requires a ` +
        `password (REDIS_PASSWORD in infra/local/.env). Add it to ` +
        `the URL's userinfo segment.`,
    );
  }

  if (parsed.password === PLACEHOLDER_PASSWORD) {
    throw new Error(
      `REDIS_URL still carries the .env.example placeholder password ` +
        `("${PLACEHOLDER_PASSWORD}"). Set a real value in ` +
        `infra/local/.env and mirror it here.`,
    );
  }

  const port = parsed.port ? Number(parsed.port) : 6379;
  if (!Number.isFinite(port) || port <= 0 || port > 65535) {
    throw new Error(`REDIS_URL has an invalid port: ${parsed.port}`);
  }

  return {
    url: raw,
    host: parsed.hostname,
    port,
  };
}

function truncate(s: string): string {
  return s.length > 32 ? `${s.slice(0, 32)}…` : s;
}
