import "server-only";

/**
 * Embed allowlist + URL safety for the game player (Phase 3).
 *
 * Defense in depth: a game renders inside our site ONLY when ALL hold:
 *   1. admin set `embedAllowed = true` on the record,
 *   2. `embedUrl` is present and a safe HTTPS URL (no javascript:/data:/etc.),
 *   3. the embed host is on the `ALLOWED_EMBED_DOMAINS` allowlist.
 *
 * The allowlist is configured via environment, e.g.:
 *   ALLOWED_EMBED_DOMAINS="example-game-provider.com,authorized-game-platform.com"
 * Parent domains cover their subdomains (cdn.provider.com matches provider.com).
 * Default is DENY — an empty allowlist blocks every embed.
 */

const UNSAFE_PROTOCOLS = new Set([
  "javascript:",
  "data:",
  "vbscript:",
  "file:",
  "blob:",
]);

/** Parse the allowlist from env. Never throws. */
export function getEmbedAllowlist(
  raw: string | undefined = process.env.ALLOWED_EMBED_DOMAINS,
): string[] {
  if (!raw) return [];
  return raw
    .split(",")
    .map((s) => s.trim().toLowerCase().replace(/^\.+/, ""))
    .filter(Boolean);
}

/** Lowercase hostname of a URL, or null when unparseable. */
export function getHostname(url: string): string | null {
  try {
    return new URL(url).hostname.toLowerCase();
  } catch {
    return null;
  }
}

/** HTTPS-only, rejects dangerous protocols and malformed URLs. */
export function isSafeHttpUrl(url: string): boolean {
  let parsed: URL;
  try {
    parsed = new URL(url);
  } catch {
    return false;
  }
  if (UNSAFE_PROTOCOLS.has(parsed.protocol)) return false;
  return parsed.protocol === "https:";
}

function isHostAllowlisted(host: string, allowlist: string[]): boolean {
  return allowlist.some((domain) => host === domain || host.endsWith(`.${domain}`));
}

export type EmbedBlockReason =
  | "not-permitted"
  | "missing-url"
  | "unsafe-url"
  | "domain-not-allowlisted";

export type EmbedCheck =
  | { ok: true; embedUrl: string }
  | { ok: false; reason: EmbedBlockReason };

/**
 * Decide whether a game's embed may be rendered. Reads the allowlist from
 * env at call time so tests can inject values.
 */
export function checkEmbedAllowed(
  embedAllowed: boolean,
  embedUrl: string | null,
  allowlistRaw?: string,
): EmbedCheck {
  if (!embedAllowed) return { ok: false, reason: "not-permitted" };
  if (!embedUrl) return { ok: false, reason: "missing-url" };
  if (!isSafeHttpUrl(embedUrl)) return { ok: false, reason: "unsafe-url" };
  const host = getHostname(embedUrl);
  if (host === null) return { ok: false, reason: "unsafe-url" };
  if (!isHostAllowlisted(host, getEmbedAllowlist(allowlistRaw))) {
    return { ok: false, reason: "domain-not-allowlisted" };
  }
  return { ok: true, embedUrl };
}
