// Asset loading for the QSL card renderer: designer fonts (registered as
// @font-face AND kept as CSS text so rasterization can inline them into the
// serialized SVG — an SVG drawn through an <img> cannot see document fonts),
// photos with their natural dimensions, and the country flag.

import { QSLFonts, QSLPhotoDataURL, QSLFlagDataURL } from '../../../wailsjs/go/main/App';
import type { CardTemplate, QSLFontInfo } from './qslTypes';

export interface PhotoAsset {
  url: string; // data URL
  w: number; // natural pixel size
  h: number;
}

export interface CardAssets {
  photos: Record<string, PhotoAsset>; // keyed by template photo ref
  flagUrl: string; // data URL, '' when no flag
  fontCss: string; // @font-face rules with data: sources
}

// Weight each embedded family renders at (variable fonts need an explicit
// heavy weight to match the printed-card look).
export const FONT_WEIGHTS: Record<string, number> = {
  'Baloo 2': 800,
  Oswald: 700,
};

let fontsPromise: Promise<{ css: string; fonts: QSLFontInfo[] }> | null = null;

// loadFonts fetches the embedded fonts once, registers them with the
// document and returns the @font-face CSS for SVG embedding.
export function loadFonts(): Promise<{ css: string; fonts: QSLFontInfo[] }> {
  if (!fontsPromise) {
    fontsPromise = (async () => {
      const fonts = (await QSLFonts()) as QSLFontInfo[];
      const rules = fonts.map((f) => {
        const weight = f.variable ? '100 900' : 'normal';
        return `@font-face{font-family:'${f.family}';src:url(data:font/ttf;base64,${f.data_b64}) format('truetype');font-weight:${weight};}`;
      });
      const css = rules.join('\n');
      const el = document.createElement('style');
      el.dataset.qslFonts = '1';
      el.textContent = css;
      document.head.appendChild(el);
      await document.fonts.ready;
      return { css, fonts };
    })();
  }
  return fontsPromise;
}

async function imageSize(url: string): Promise<{ w: number; h: number }> {
  const img = new Image();
  img.src = url;
  await img.decode();
  return { w: img.naturalWidth, h: img.naturalHeight };
}

// Photos are multi-MB base64 payloads; cache them so the three proposals
// (which share the same photo set) and editor re-renders fetch each once.
const photoCache = new Map<string, Promise<PhotoAsset>>();

function loadPhoto(templateId: number, ref: string): Promise<PhotoAsset> {
  const key = `${templateId}:${ref}`;
  let p = photoCache.get(key);
  if (!p) {
    p = (async () => {
      const url = await QSLPhotoDataURL(templateId, ref);
      const size = await imageSize(url);
      return { url, ...size };
    })();
    photoCache.set(key, p);
  }
  return p;
}

// loadCardAssets resolves everything a template needs to render: each
// referenced photo as a data URL with its natural size, the flag and the
// font CSS. templateId 0 reads draft photos at their original paths.
export async function loadCardAssets(template: CardTemplate, templateId: number): Promise<CardAssets> {
  const refs = new Set<string>([template.hero.photo]);
  for (const e of template.elements) {
    if (e.type === 'insert' && e.photo) refs.add(e.photo);
  }
  const photos: Record<string, PhotoAsset> = {};
  await Promise.all(
    [...refs].map(async (ref) => {
      photos[ref] = await loadPhoto(templateId, ref);
    }),
  );
  let flagUrl = '';
  const country = template.elements.find((e) => e.type === 'country');
  if (country?.flag && country.flag !== 'auto') {
    flagUrl = await QSLFlagDataURL(country.flag);
  }
  const { css } = await loadFonts();
  return { photos, flagUrl, fontCss: css };
}
