/**
 * Shared font registry — the single source of truth for which typefaces YeetPDF
 * can render and embed, and how a document's font name maps to one of them.
 *
 * This module has NO runtime imports so it can be shared by the mupdf worker
 * bundle (native text editing), the main thread (overlay/properties preview via
 * FontFace), and the pdf-lib exporter — the same constraint mupdf-types.ts keeps.
 *
 * Font strategy = metric-matched substitution (the industry approach; even
 * Acrobat substitutes rather than reusing a document's subsetted embedded font).
 * The three "core" families are the base-14 PDF fonts: every PDF viewer has them,
 * so they need NO embedded bytes and cover Arial/Times/Courier documents for
 * free. The rest are bundled OFL TTFs, fetched on demand from /fonts/ only when
 * actually used.
 */

export type FontVariant = 'regular' | 'bold' | 'italic' | 'boldItalic';
export type FontCategory = 'sans' | 'serif' | 'mono';

export interface FontFamilyDef {
  /** Stable id, stored in TextAnno.fontFamily and passed to the worker. */
  id: string;
  label: string;
  category: FontCategory;
  /** base-14 PDF font → no embedding, rendered by any viewer. */
  core?: boolean;
  /** Name handed to `new mupdf.Font(psName, bytes)` (webfonts only). */
  psName?: string;
  /** CSS family name registered via FontFace (webfonts only). */
  cssName?: string;
  /** Full CSS font stack for on-screen rendering. */
  cssStack: string;
  /** TTF basenames under /fonts/ (webfonts only). */
  files?: Partial<Record<FontVariant, string>>;
  license?: 'OFL-1.1' | 'Apache-2.0';
}

export const FONT_FAMILIES: FontFamilyDef[] = [
  // ---- core (base-14, no embedding) ----
  { id: 'Helvetica', label: 'Helvetica', category: 'sans', core: true, cssStack: 'Arial, Helvetica, sans-serif' },
  { id: 'Times New Roman', label: 'Times New Roman', category: 'serif', core: true, cssStack: 'Georgia, "Times New Roman", Times, serif' },
  { id: 'Courier', label: 'Courier', category: 'mono', core: true, cssStack: '"Courier New", Courier, monospace' },
  // ---- bundled OFL webfonts (on-demand /fonts/*.ttf) ----
  {
    id: 'Carlito', label: 'Carlito (Calibri)', category: 'sans', psName: 'Carlito', cssName: 'Carlito',
    cssStack: 'Carlito, Calibri, "Segoe UI", sans-serif', license: 'OFL-1.1',
    files: { regular: 'carlito-regular.ttf', bold: 'carlito-bold.ttf', italic: 'carlito-italic.ttf', boldItalic: 'carlito-bolditalic.ttf' },
  },
  {
    id: 'PT Serif', label: 'PT Serif', category: 'serif', psName: 'PTSerif', cssName: 'PT Serif',
    cssStack: '"PT Serif", Georgia, "Times New Roman", serif', license: 'OFL-1.1',
    files: { regular: 'ptserif-regular.ttf', bold: 'ptserif-bold.ttf', italic: 'ptserif-italic.ttf', boldItalic: 'ptserif-bolditalic.ttf' },
  },
  {
    id: 'PT Sans', label: 'PT Sans', category: 'sans', psName: 'PTSans', cssName: 'PT Sans',
    cssStack: '"PT Sans", "Segoe UI", Arial, sans-serif', license: 'OFL-1.1',
    files: { regular: 'ptsans-regular.ttf', bold: 'ptsans-bold.ttf', italic: 'ptsans-italic.ttf', boldItalic: 'ptsans-bolditalic.ttf' },
  },
  {
    id: 'Lato', label: 'Lato', category: 'sans', psName: 'Lato', cssName: 'Lato',
    cssStack: 'Lato, "Helvetica Neue", Arial, sans-serif', license: 'OFL-1.1',
    files: { regular: 'lato-regular.ttf', bold: 'lato-bold.ttf', italic: 'lato-italic.ttf', boldItalic: 'lato-bolditalic.ttf' },
  },
  {
    id: 'Poppins', label: 'Poppins', category: 'sans', psName: 'Poppins', cssName: 'Poppins',
    cssStack: 'Poppins, "Century Gothic", sans-serif', license: 'OFL-1.1',
    files: { regular: 'poppins-regular.ttf', bold: 'poppins-bold.ttf', italic: 'poppins-italic.ttf', boldItalic: 'poppins-bolditalic.ttf' },
  },
  {
    id: 'Gelasio', label: 'Gelasio (Georgia)', category: 'serif', psName: 'Gelasio', cssName: 'Gelasio',
    cssStack: 'Gelasio, Georgia, "Times New Roman", serif', license: 'OFL-1.1',
    files: { regular: 'gelasio-regular.ttf', bold: 'gelasio-bold.ttf', italic: 'gelasio-italic.ttf', boldItalic: 'gelasio-bolditalic.ttf' },
  },
  {
    id: 'Caladea', label: 'Caladea (Cambria)', category: 'serif', psName: 'Caladea', cssName: 'Caladea',
    cssStack: 'Caladea, Cambria, Georgia, serif', license: 'OFL-1.1',
    files: { regular: 'caladea-regular.ttf', bold: 'caladea-bold.ttf', italic: 'caladea-italic.ttf', boldItalic: 'caladea-bolditalic.ttf' },
  },
  {
    id: 'Selawik', label: 'Selawik (Segoe UI)', category: 'sans', psName: 'Selawik', cssName: 'Selawik',
    cssStack: 'Selawik, "Segoe UI", Arial, sans-serif', license: 'OFL-1.1',
    // no italic master is published upstream — fontFileUrl falls back to regular/bold
    files: { regular: 'selawik-regular.ttf', bold: 'selawik-bold.ttf' },
  },
  {
    id: 'Roboto', label: 'Roboto', category: 'sans', psName: 'Roboto', cssName: 'Roboto',
    cssStack: 'Roboto, "Helvetica Neue", Arial, sans-serif', license: 'OFL-1.1',
    files: { regular: 'roboto-regular.ttf', bold: 'roboto-bold.ttf', italic: 'roboto-italic.ttf', boldItalic: 'roboto-bolditalic.ttf' },
  },
  {
    id: 'Open Sans', label: 'Open Sans', category: 'sans', psName: 'OpenSans', cssName: 'Open Sans',
    cssStack: '"Open Sans", "Segoe UI", Arial, sans-serif', license: 'OFL-1.1',
    files: { regular: 'opensans-regular.ttf', bold: 'opensans-bold.ttf', italic: 'opensans-italic.ttf', boldItalic: 'opensans-bolditalic.ttf' },
  },
  {
    id: 'Montserrat', label: 'Montserrat', category: 'sans', psName: 'Montserrat', cssName: 'Montserrat',
    cssStack: 'Montserrat, "Century Gothic", sans-serif', license: 'OFL-1.1',
    files: { regular: 'montserrat-regular.ttf', bold: 'montserrat-bold.ttf', italic: 'montserrat-italic.ttf', boldItalic: 'montserrat-bolditalic.ttf' },
  },
];

export function familyById(id: string): FontFamilyDef | undefined {
  return FONT_FAMILIES.find((f) => f.id === id);
}

export function variantFor(bold?: boolean, italic?: boolean): FontVariant {
  return bold && italic ? 'boldItalic' : bold ? 'bold' : italic ? 'italic' : 'regular';
}

/** `/fonts/<file>` for an embeddable family, or null for a core / unknown one. */
export function fontFileUrl(id: string, variant: FontVariant): string | null {
  const fam = familyById(id);
  if (!fam || fam.core || !fam.files) return null;
  const file = fam.files[variant] ?? fam.files.regular;
  return file ? `/fonts/${file}` : null;
}

/**
 * Map a raw PDF font name (possibly subset-prefixed like "ABCDEE+ArialMT",
 * "TimesNewRomanPSMT", "Calibri-Bold") to the best bundled substitute + the
 * variant to draw. Never throws — falls back by the serif/mono/bold/italic
 * flags to a core font, which always renders.
 */
export function resolveDocFont(
  rawName: string,
  flags: { serif?: boolean; mono?: boolean; bold?: boolean; italic?: boolean },
): { id: string; variant: FontVariant } {
  const raw = (rawName || '').replace(/^[A-Z]{6}\+/, ''); // strip subset prefix
  const lower = raw.toLowerCase();
  const bold = !!flags.bold || /bold|black|heavy|semibold|demibold|demi/.test(lower);
  const italic = !!flags.italic || /italic|oblique/.test(lower);
  const norm = lower.replace(/[^a-z]/g, ''); // letters only, for substring matching
  const variant = variantFor(bold, italic);
  const pick = (id: string) => ({ id, variant });

  if (/arial|helvetica|liberationsans|nimbussans|arimo/.test(norm)) return pick('Helvetica');
  if (/timesnewroman|liberationserif|nimbusroman|tinos|times/.test(norm)) return pick('Times New Roman');
  if (/couriernew|liberationmono|consolas|cousine|courier/.test(norm)) return pick('Courier');
  if (/calibri|carlito/.test(norm)) return pick('Carlito');
  // Georgia and Cambria are visually distinct serifs (old-style vs transitional) —
  // each gets its own purpose-built metric-compatible substitute rather than
  // sharing one approximation.
  if (/georgia|gelasio/.test(norm)) return pick('Gelasio');
  if (/cambria|caladea/.test(norm)) return pick('Caladea');
  if (/ptserif/.test(norm)) return pick('PT Serif');
  if (/ptsans/.test(norm)) return pick('PT Sans');
  if (/lato/.test(norm)) return pick('Lato');
  if (/poppins/.test(norm)) return pick('Poppins');
  // Segoe UI is the default Windows/Office UI font — extremely common in
  // Word/PowerPoint-exported PDFs. Selawik is Microsoft's own open, metric-
  // compatible substitute for it.
  if (/segoeui|selawik/.test(norm)) return pick('Selawik');
  if (/roboto/.test(norm)) return pick('Roboto');
  if (/opensans/.test(norm)) return pick('Open Sans');
  if (/montserrat/.test(norm)) return pick('Montserrat');

  if (flags.mono) return pick('Courier');
  if (flags.serif) return pick('Times New Roman');
  return pick('Helvetica');
}
