// Shared typography engine — turns a `typography` settings object into CSS custom
// properties and loads the needed webfonts (Google Fonts <link> + @font-face for
// uploaded custom fonts). Loaded before cms.jsx on the site, and reused by the builder.

(function () {
  const DEFAULTS = {
    heading_thai_font: 'Noto Sans Thai',
    heading_latin_font: 'Cormorant Garamond',
    body_font: 'Noto Sans Thai',
    heading_weight_thai: 400,
    heading_weight_latin: 600,
    heading_scale: 1,
    heading_scale_thai: 1,
    custom_fonts: [], // [{ family, url, format }]
  };

  // Per-spot font overrides (typography.overrides[spot]) — optional, always fall back to the
  // global setting when unset. `th`/`en` say which language variant this spot renders;
  // `weight` says whether it exposes a weight override too (a couple of spots are family-only).
  const SPOTS = {
    hero: { th: true, en: false, weight: true },
    'section-tiers': { th: true, en: true, weight: true },
    'section-gallery_teaser': { th: true, en: true, weight: true },
    'section-papers': { th: true, en: true, weight: true },
    'section-comparison': { th: true, en: true, weight: true },
    'section-blog': { th: true, en: true, weight: true },
    tierCardName: { th: true, en: true, weight: true },
    blogCardTitle: { th: true, en: true, weight: true },
    comparisonHeading: { th: false, en: true, weight: true },
    footerWordmark: { th: false, en: true, weight: false },
    lightboxCaption: { th: false, en: true, weight: false },
  };

  function fmtFromUrl(url) {
    const u = (url || '').toLowerCase();
    if (u.endsWith('.woff2')) return 'woff2';
    if (u.endsWith('.woff')) return 'woff';
    if (u.endsWith('.ttf')) return 'truetype';
    if (u.endsWith('.otf')) return 'opentype';
    return 'woff2';
  }

  // Returns { vars: {..cssVars}, googleFamilies: [names], customFonts: [{family,url,format}] }
  function resolve(typographyRaw) {
    const t = { ...DEFAULTS, ...(typographyRaw || {}) };
    const custom = Array.isArray(t.custom_fonts) ? t.custom_fonts : [];
    const customNames = new Set(custom.map((c) => c.family));

    const scale = Number(t.heading_scale) || 1;
    const scaleThai = Number(t.heading_scale_thai) || 1;

    const vars = {
      '--font-heading': `'${t.heading_latin_font}', 'Noto Sans Thai', Georgia, serif`,
      '--font-heading-thai': `'${t.heading_thai_font}', 'Noto Sans Thai', sans-serif`,
      '--font-body': `'${t.body_font}', 'Sarabun', 'Helvetica Neue', sans-serif`,
      '--weight-heading': String(t.heading_weight_latin || 600),
      '--weight-heading-thai': String(t.heading_weight_thai || 400),
      // A plain multiplier, not a resolved px value — styles.css computes the actual
      // --text-h1/h2/h3 as calc(base * this), where "base" is itself overridden by the
      // mobile media query. Setting a literal px here (the old approach) would win over
      // any stylesheet media query regardless of specificity, silently breaking mobile resize.
      '--heading-scale': String(scale),
      '--heading-scale-thai': String(scaleThai),
    };

    // Per-spot overrides — each emits its own scoped var pair, falling back to the global
    // value above when that spot has no override. Still all returned in the same flat `vars`
    // object applied once at the page's outer wrapper — no per-instance DOM scoping needed,
    // each component just references its own spot's var name instead of the bare global one.
    const overrides = (typographyRaw && typographyRaw.overrides) || {};
    Object.keys(SPOTS).forEach((spot) => {
      const cfg = SPOTS[spot];
      const o = overrides[spot] || {};
      if (cfg.th) {
        vars[`--font-heading-thai--${spot}`] = o.heading_thai_font
          ? `'${o.heading_thai_font}', 'Noto Sans Thai', sans-serif` : vars['--font-heading-thai'];
        if (cfg.weight) {
          vars[`--weight-heading-thai--${spot}`] = o.heading_weight_thai != null ? String(o.heading_weight_thai) : vars['--weight-heading-thai'];
        }
      }
      if (cfg.en) {
        vars[`--font-heading--${spot}`] = o.heading_latin_font
          ? `'${o.heading_latin_font}', 'Noto Sans Thai', Georgia, serif` : vars['--font-heading'];
        if (cfg.weight) {
          vars[`--weight-heading--${spot}`] = o.heading_weight_latin != null ? String(o.heading_weight_latin) : vars['--weight-heading'];
        }
      }
    });

    // Any chosen family that isn't a custom upload is treated as a Google Font — including
    // families picked only for a per-spot override, so their fonts actually get requested.
    const overrideFamilies = Object.values(overrides).flatMap((o) => [o.heading_thai_font, o.heading_latin_font]).filter(Boolean);
    const googleFamilies = Array.from(new Set(
      [t.heading_thai_font, t.heading_latin_font, t.body_font, ...overrideFamilies].filter((f) => f && !customNames.has(f))
    ));

    return { vars, googleFamilies, customFonts: custom };
  }

  // Inject/refresh the <link> + <style> tags that load the fonts. Idempotent.
  function loadFonts(typographyRaw, doc) {
    doc = doc || document;
    const { googleFamilies, customFonts } = resolve(typographyRaw);

    // Google Fonts — one combined stylesheet. Request weights 300–700 so weight controls work.
    let link = doc.getElementById('tw-google-fonts');
    if (!link) {
      link = doc.createElement('link');
      link.id = 'tw-google-fonts';
      link.rel = 'stylesheet';
      doc.head.appendChild(link);
    }
    if (googleFamilies.length) {
      const fams = googleFamilies
        .map((f) => 'family=' + encodeURIComponent(f) + ':wght@300;400;500;600;700')
        .join('&');
      link.href = `https://fonts.googleapis.com/css2?${fams}&display=swap`;
    } else {
      link.removeAttribute('href');
    }

    // Custom uploaded fonts — @font-face rules.
    let style = doc.getElementById('tw-custom-fonts');
    if (!style) {
      style = doc.createElement('style');
      style.id = 'tw-custom-fonts';
      doc.head.appendChild(style);
    }
    style.textContent = (customFonts || [])
      .map((c) => `@font-face{font-family:'${c.family}';src:url('${c.url}') format('${c.format || fmtFromUrl(c.url)}');font-display:swap;}`)
      .join('\n');
  }

  window.Typography = { DEFAULTS, SPOTS, resolve, loadFonts };
})();
