// Banner colour resolution. A campaign may carry two optional colour sets (see
// lib/model BannerColors): one for every theme and one that further overrides the
// dark theme. resolveBannerColors collapses them to the colours the strip paints on
// the currently-rendered theme, or null when the campaign keeps the neutral
// --ad-bg / --text-muted / --accent tokens. Pure and DOM-free, so it is unit-tested
// directly; AdBanner.svelte applies the result as inline CSS variables.

import type { BannerColors } from './model';

export type ThemeMode = 'light' | 'dark';

/** ResolvedBannerColors is the strip's colours for one theme, border already derived. */
export interface ResolvedBannerColors {
  bg: string;
  fg: string;
  link: string;
  border: string;
}

/**
 * resolveBannerColors picks the colour set for the rendered theme — dark ← dark ?? all,
 * light ← all — and returns null when there is no override for that theme (the strip then
 * keeps the neutral tokens). The border is derived from the background so it stays subtle
 * on any colour.
 */
export function resolveBannerColors(colors: BannerColors, mode: ThemeMode): ResolvedBannerColors | null {
  const set = mode === 'dark' ? (colors.dark ?? colors.all) : colors.all;
  if (!set) return null;
  return { bg: set.bg, fg: set.fg, link: set.link, border: derivedBorder(set.bg) };
}

/**
 * derivedBorder nudges the background 14% toward black on a light background and toward
 * white on a dark one — a subtle edge that suits any override colour. It is computed in
 * JS (no CSS color-mix) so it works on the old Android WebView floor (Chrome 67), and
 * mirrors the admin console preview (banner_detail.gohtml). A malformed colour is
 * returned unchanged.
 */
export function derivedBorder(bgHex: string): string {
  const rgb = hexToRgb(bgHex);
  if (!rgb) return bgHex;
  const target: RGB = luminance(rgb) > 0.5 ? [0, 0, 0] : [255, 255, 255];
  return rgbToHex(mix(rgb, target, 0.14));
}

type RGB = [number, number, number];

function hexToRgb(h: string): RGB | null {
  const m = /^#([0-9a-fA-F]{6})$/.exec(h.trim());
  if (!m) return null;
  const n = m[1];
  return [parseInt(n.slice(0, 2), 16), parseInt(n.slice(2, 4), 16), parseInt(n.slice(4, 6), 16)];
}

function mix(a: RGB, b: RGB, t: number): RGB {
  return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}

function luminance(r: RGB): number {
  return (0.2126 * r[0] + 0.7152 * r[1] + 0.0722 * r[2]) / 255;
}

function pad(x: number): string {
  return Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, '0');
}

function rgbToHex(r: RGB): string {
  return '#' + pad(r[0]) + pad(r[1]) + pad(r[2]);
}
