// Anonymous DAI (daily active installs) heartbeat — validation + normalization.
// See docs/CLOUD_INFRA.md §4. No PII, no IP, no account; the install ID is a
// random UUID minted client-side and kept only in the device Keychain. Because the ID is
// per-install, this counts installs, not users (the relay DAU counts users — §2).

export interface Heartbeat {
  installId: string;
  /** Primary split between the two clients. */
  platform: "macos" | "ios";
  osVersion: string;
  channel: string;
  appVersion: string;
  build: string;
  arch: string;
  locale: string;
}

const PLATFORMS = new Set(["macos", "ios"]);
// macOS channels + iOS distribution channels. Clients skip dev/debug builds, but
// accept them defensively here and let the analytics query filter them out.
const CHANNELS = new Set(["dev", "beta", "rc", "stable", "testflight", "appstore"]);
const MAX = 128;

function str(v: unknown, max = MAX): string | null {
  if (typeof v !== "string") return null;
  const s = v.trim();
  if (s.length === 0 || s.length > max) return null;
  return s;
}

export type ParseResult =
  | { ok: true; value: Heartbeat }
  | { ok: false; error: string };

/** Validate + normalize an untrusted request body into a Heartbeat. Pure; unit-tested. */
export function parseHeartbeat(body: unknown): ParseResult {
  if (typeof body !== "object" || body === null) return { ok: false, error: "not an object" };
  const b = body as Record<string, unknown>;

  const installId = str(b.installId);
  if (!installId) return { ok: false, error: "installId" };

  const platform = str(b.platform);
  if (!platform || !PLATFORMS.has(platform)) return { ok: false, error: "platform" };

  const channel = str(b.channel);
  if (!channel || !CHANNELS.has(channel)) return { ok: false, error: "channel" };

  // Remaining dimensions are best-effort: default to "unknown" rather than reject,
  // so a valid DAI is never dropped over a missing minor field.
  const osVersion = str(b.osVersion) ?? "unknown";
  const appVersion = str(b.appVersion) ?? "unknown";
  const build = str(b.build) ?? "unknown";
  const arch = str(b.arch) ?? "unknown";
  const locale = str(b.locale, 32) ?? "unknown";

  return {
    ok: true,
    value: {
      installId,
      platform: platform as "macos" | "ios",
      osVersion,
      channel,
      appVersion,
      build,
      arch,
      locale,
    },
  };
}
