import type { TrackingCode } from "./payload";

// Klaro's own config shape (kiprotect/klaro, see node_modules/klaro's
// dist/config.js for the fully-annotated reference) — only the fields we
// actually set are typed here, not Klaro's full surface.
export type KlaroConfig = {
  version: number;
  elementID: string;
  styling: Record<string, string | string[]>;
  storageMethod: "cookie";
  cookieName: string;
  cookieExpiresAfterDays: number;
  default: boolean;
  mustConsent: boolean;
  acceptAll: boolean;
  hideDeclineAll: boolean;
  noticeAsModal: boolean;
  disablePoweredBy: boolean;
  translations: Record<string, Record<string, unknown>>;
  services: {
    name: string;
    title: string;
    purposes: string[];
    required: boolean;
    default: boolean;
    onlyOnce: boolean;
    callback: (consent: boolean) => void;
  }[];
};

// "google-maps" is a fixed name, not id-based like every other provider —
// GoogleMapsEmbed.tsx has to hardcode a `data-name` matching this exact
// string for Klaro's own contextual-consent DOM scan
// (renderContextualConsentNotices in node_modules/klaro) to find it, and
// there's no per-row distinguishing ID the way GA/FB/GTM each have one
// (a Maps embed URL is per-page, not stored on this row at all — see
// TrackingCodes.ts's own field comment). Only ever create ONE
// google-maps row in the admin; a second one would collide on this name.
export function serviceName(code: TrackingCode): string {
  if (code.provider === "google-maps") return "google-maps";
  return `tracking-code-${code.id}`;
}

function providerTitle(code: TrackingCode): string {
  switch (code.provider) {
    case "google-analytics":
      return "Google Analytics";
    case "facebook-pixel":
      return "Facebook Pixel";
    case "google-tag-manager":
      return "Google Tag Manager";
    case "google-maps":
      return "Google Maps";
    default:
      return "Sonstiges Tracking-Skript";
  }
}

// Shown per service in the modal's expanded list — without this, Klaro
// only ever shows the bare provider title (see providerTitle above),
// which is thin for real TTDSG transparency ("what does this actually
// do") and reads as less trustworthy than a CMP that explains itself.
// Generic per-provider text, not per-row — a second Google Analytics row
// would get the identical description, which is fine since the "what
// GA does" fact doesn't change between rows, only which property it's
// wired to (already visible via the row's own `name` if an admin sets a
// distinguishing one).
function providerDescription(code: TrackingCode): string {
  switch (code.provider) {
    case "google-analytics":
      return "Erfasst anonymisierte Nutzungsstatistiken (z. B. welche Seiten wie oft besucht werden), damit wir verstehen, was bei euch ankommt. Setzt die Cookies _ga (Laufzeit 2 Jahre, unterscheidet Besucher:innen) und _ga_<Container-ID> (2 Jahre, Sitzungsstatus).";
    case "facebook-pixel":
      return "Misst, ob ein Besuch von hier zu einer Anzeige bei Facebook/Instagram passt, und hilft uns, Werbung zielgerichteter zu schalten. Setzt den Cookie _fbp (3 Monate, eigene Domain); ist ein Facebook-Konto im selben Browser aktiv, setzt facebook.com zusätzlich den Cookie fr (3 Monate).";
    case "google-tag-manager":
      return "Lädt weitere Mess-/Marketing-Skripte je nach Konfiguration nach, ohne dass wir dafür jedes Mal den Code der Seite ändern müssen. Setzt selbst keine eigenen Cookies — welche Cookies letztlich gesetzt werden, hängt von den darüber geladenen Diensten ab (siehe deren eigene Einträge hier).";
    case "google-maps":
      return "Bindet eine interaktive Karte von Google ein. Beim Laden überträgt dein Browser deine IP-Adresse an Google und Google setzt eigene Cookies (u. a. NID), deren genauer Umfang außerhalb unserer Kontrolle liegt.";
    default:
      return "Ein zusätzliches Skript, das diese Seite einbindet.";
  }
}

// `onAccept` is threaded in rather than importing loadTrackingCode.ts
// directly here — this module only builds a plain config object (no DOM
// access), kept separate from the actual script-injection side effect so
// it stays trivially testable/reusable if the loading mechanism ever
// changes.
export function buildKlaroConfig(codes: TrackingCode[], onAccept: (code: TrackingCode) => void): KlaroConfig {
  const serviceTranslations: Record<string, { title: string; description: string }> = {};
  for (const code of codes) {
    serviceTranslations[serviceName(code)] = { title: providerTitle(code), description: providerDescription(code) };
  }

  return {
    version: 1,
    elementID: "klaro",
    // 'light' + 'bottom' + 'left' — a compact corner notice (Klaro's own
    // default max-width, ~400px), not a full-width bar. 'wide' (tried
    // first) stretched it across nearly the whole viewport, which read as
    // too dominant compared to a typical bottom-left corner CMP.
    // Extra keys beyond `theme` override individual CSS custom properties
    // (Klaro's injectStyles() applies any non-'theme' `styling` key as a
    // `--<key>` var, see node_modules/klaro's own utils/styling.js) — this
    // is the color pass; the border/shadow/typography pass lives in
    // KlaroConsentManager.tsx's <KlaroTheme> instead (a real CSS override,
    // needed because Klaro's own default border/spacing/font still read as
    // an obviously-bolted-on library widget even with the right colors).
    // dark1/light1 are (confusingly named) the notice's background/text
    // colors, not a dark-mode toggle. green1 colors the accept button.
    // blue1/blue2 color the "Einstellungen" (.cm-btn-info) button and
    // in-modal links — Klaro's default blue clashed with the brand just as
    // much as its default green did, easy to miss since it only shows
    // once the modal (not just the notice) is open.
    styling: {
      theme: ["light", "bottom", "left"],
      dark1: "#fffdf8", // --color-bg-paper — notice/modal background
      dark2: "#e5e0d8", // --color-border
      // NOT a light background tint despite the name — Klaro's own
      // .cm-list-description rule (each service's description text in
      // the modal) uses dark3 as a TEXT color, not a background. A near-
      // white value here (tried first) made every service description
      // almost invisible against the modal's own light background —
      // confirmed via screenshot 2026-08-02. --color-text-muted instead.
      dark3: "#6b6b69",
      light1: "#1a1a18", // --color-text-primary — notice/modal text
      light2: "#6b6b69", // --color-text-muted
      light3: "#1a1a18",
      green1: "#f6a701", // --color-brand — accept button
      green2: "#d99000",
      blue1: "#e5e0d8",
      blue2: "#6b6b69",
      "button-text-color": "#1a1a18",
      "notice-max-width": "380px",
    },
    storageMethod: "cookie",
    cookieName: "klaro-consent",
    cookieExpiresAfterDays: 180,
    default: false,
    mustConsent: false,
    acceptAll: true,
    hideDeclineAll: false,
    noticeAsModal: false,
    // Removes the "Realisiert mit Klaro!" attribution link in the modal —
    // BSD-3-Clause has no on-page-attribution requirement (only source/
    // binary copyright-notice retention), this is just the maintainers'
    // own polite ask via a dedicated config flag, not a license term.
    disablePoweredBy: true,
    translations: {
      de: {
        consentModal: {
          title: "Deine Cookie-Einstellungen",
          description: "Hier siehst du genau, was wir einsetzen — und entscheidest frei, was du erlaubst. Nichts davon läuft, bevor du zustimmst.",
        },
        consentNotice: {
          description: "Wir verwenden ein paar Cookies, damit diese Seite reibungslos läuft und wir sehen, was bei euch gut ankommt.",
          learnMore: "Einstellungen",
        },
        purposes: {
          necessary: "Notwendig",
          analytics: "Analyse",
          marketing: "Marketing",
          content: "Inhalte",
        },
        ...serviceTranslations,
      },
    },
    services: codes.map((code) => ({
      name: serviceName(code),
      title: providerTitle(code),
      purposes: [code.consentCategory],
      required: code.consentCategory === "necessary",
      default: code.consentCategory === "necessary",
      // For "google-maps", `onAccept` below is a no-op (see
      // loadTrackingCode.ts) — an embed's src isn't a script this app
      // injects, Klaro's OWN contextual-consent DOM scan
      // (renderContextualConsentNotices, runs as part of Klaro.render())
      // finds any `data-name="google-maps"` iframe on the page and
      // blanks/restores its `src` itself, purely by service name +
      // consent state. This service entry's only job is telling Klaro
      // that name exists and which purpose gates it.
      //
      // A restock/page-reload shouldn't re-fire the loader for a service
      // the visitor already accepted in an earlier session within this
      // same page load — Klaro still calls `callback` once per accepted
      // service per page load either way, `onlyOnce` just governs repeat
      // toggling within one session.
      onlyOnce: true,
      callback: (consent: boolean) => {
        if (consent) onAccept(code);
      },
    })),
  };
}
