import { MathfieldElement } from 'mathlive';

let configured = false;

export type MathLiveField = MathfieldElement;

export function configureLearnitiveMathLive(
  fontsDirectory = '/mathlive-fonts/',
): void {
  if (configured) return;
  MathfieldElement.fontsDirectory = fontsDirectory;
  configured = true;
}

export function createLearnitiveMathField(options: {
  parent: HTMLElement;
  value: string;
  readOnly?: boolean;
  ariaLabel?: string;
  className?: string;
  onInput?: (value: string) => void;
  onEscape?: () => void;
}): MathfieldElement {
  configureLearnitiveMathLive();
  let field: MathfieldElement;
  try {
    field = new MathfieldElement();
  } catch (constructorError) {
    // Some hardened/custom-element runtimes reject direct HTMLElement-derived
    // constructors even though the registered <math-field> element is fully
    // usable. Fall back to the standards-based creation path before declaring
    // MathLive unavailable.
    try {
      if (!customElements.get('math-field')) {
        customElements.define('math-field', MathfieldElement);
      }
      const created = document.createElement('math-field');
      // A production bundle can already have an equivalent MathLive custom
      // element registered by another chunk. In that case `instanceof` the
      // local imported constructor is not a reliable usability check. Accept
      // the registered element by its MathLive surface instead.
      if (!('value' in created) || !('executeCommand' in created)) {
        throw constructorError;
      }
      field = created as MathfieldElement;
    } catch {
      throw constructorError;
    }
  }
  field.value = options.value;
  field.readOnly = options.readOnly ?? false;
  field.className = options.className ?? '';
  field.setAttribute('aria-label', options.ariaLabel ?? 'Equation editor');
  field.addEventListener('input', () => options.onInput?.(field.value));
  field.addEventListener('keydown', (event) => {
    if (event.key === 'Escape' && options.onEscape) {
      event.preventDefault();
      event.stopPropagation();
      options.onEscape();
    }
  });
  // Use the Learnitive MathLive split: read-only preview widgets hide the
  // native menu, while editable fields retain MathLive's own menu/menu-toggle.
  // `menuItems` is mount-sensitive, so only touch it after connection and only
  // for previews. R14.22.2 cleared it for every field, leaving editable menu
  // buttons visible but with no menu to open.
  const configureMountedUi = () => {
    if (!field.isConnected || !field.readOnly) return;
    try {
      field.menuItems = [];
    } catch {
      // Preview rendering remains valid if optional menu customization fails.
    }
  };
  field.addEventListener('mount', configureMountedUi, { once: true });
  options.parent.appendChild(field);
  if (field.readOnly) queueMicrotask(configureMountedUi);
  return field;
}

export function insertMathLiveCommand(
  field: MathfieldElement,
  command: string,
): void {
  field.executeCommand(['insert', command]);
  field.focus();
}

/** Searchable LaTeX symbol catalogue used by the Learnitive math editor. */
export type MathLiveSymbol = { label: string; command: string; keywords: string };

export const MATHLIVE_SYMBOLS: readonly MathLiveSymbol[] = [
  { label: '½', command: '\\frac{}{}', keywords: 'fraction frac half' },
  { label: '√', command: '\\sqrt{}', keywords: 'sqrt square root radical' },
  { label: 'α', command: '\\alpha', keywords: 'alpha greek' },
  { label: 'β', command: '\\beta', keywords: 'beta greek' },
  { label: 'γ', command: '\\gamma', keywords: 'gamma greek' },
  { label: 'δ', command: '\\delta', keywords: 'delta greek' },
  { label: 'ε', command: '\\epsilon', keywords: 'epsilon greek' },
  { label: 'θ', command: '\\theta', keywords: 'theta greek angle' },
  { label: 'λ', command: '\\lambda', keywords: 'lambda greek' },
  { label: 'μ', command: '\\mu', keywords: 'mu greek' },
  { label: 'π', command: '\\pi', keywords: 'pi greek' },
  { label: 'ρ', command: '\\rho', keywords: 'rho greek' },
  { label: 'σ', command: '\\sigma', keywords: 'sigma greek' },
  { label: 'φ', command: '\\phi', keywords: 'phi greek' },
  { label: 'ω', command: '\\omega', keywords: 'omega greek' },
  { label: 'Γ', command: '\\Gamma', keywords: 'Gamma greek uppercase' },
  { label: 'Δ', command: '\\Delta', keywords: 'Delta greek uppercase' },
  { label: 'Σ', command: '\\sum_{}^{}', keywords: 'sum sigma summation' },
  { label: 'Π', command: '\\prod_{}^{}', keywords: 'product prod pi' },
  { label: '∫', command: '\\int_{}^{}', keywords: 'integral int calculus' },
  { label: '∮', command: '\\oint_{}^{}', keywords: 'contour integral oint' },
  { label: '∂', command: '\\partial', keywords: 'partial derivative' },
  { label: '∇', command: '\\nabla', keywords: 'nabla gradient del' },
  { label: '∞', command: '\\infty', keywords: 'infinity infinite' },
  { label: '±', command: '\\pm', keywords: 'plus minus' },
  { label: '×', command: '\\times', keywords: 'times multiply cross' },
  { label: '÷', command: '\\div', keywords: 'divide division' },
  { label: '·', command: '\\cdot', keywords: 'dot multiply cdot' },
  { label: '≤', command: '\\leq', keywords: 'less equal leq inequality' },
  { label: '≥', command: '\\geq', keywords: 'greater equal geq inequality' },
  { label: '≠', command: '\\neq', keywords: 'not equal neq inequality' },
  { label: '≈', command: '\\approx', keywords: 'approximately approx' },
  { label: '≡', command: '\\equiv', keywords: 'equivalent identity' },
  { label: '∝', command: '\\propto', keywords: 'proportional propto' },
  { label: '∈', command: '\\in', keywords: 'element in set membership' },
  { label: '∉', command: '\\notin', keywords: 'not element set membership' },
  { label: '⊂', command: '\\subset', keywords: 'subset set' },
  { label: '⊆', command: '\\subseteq', keywords: 'subset equal set' },
  { label: '∪', command: '\\cup', keywords: 'union set cup' },
  { label: '∩', command: '\\cap', keywords: 'intersection set cap' },
  { label: '∀', command: '\\forall', keywords: 'forall universal quantifier' },
  { label: '∃', command: '\\exists', keywords: 'exists existential quantifier' },
  { label: '¬', command: '\\neg', keywords: 'not negation logic' },
  { label: '∧', command: '\\land', keywords: 'and wedge logic' },
  { label: '∨', command: '\\lor', keywords: 'or vee logic' },
  { label: '→', command: '\\rightarrow', keywords: 'right arrow to' },
  { label: '←', command: '\\leftarrow', keywords: 'left arrow' },
  { label: '↔', command: '\\leftrightarrow', keywords: 'left right arrow iff' },
  { label: '⇒', command: '\\Rightarrow', keywords: 'implies right double arrow' },
  { label: '⇔', command: '\\Leftrightarrow', keywords: 'iff equivalent double arrow' },
  { label: '↦', command: '\\mapsto', keywords: 'mapsto map arrow' },
  { label: 'ℝ', command: '\\mathbb{R}', keywords: 'real numbers reals R blackboard' },
  { label: 'ℕ', command: '\\mathbb{N}', keywords: 'natural numbers N blackboard' },
  { label: 'ℤ', command: '\\mathbb{Z}', keywords: 'integers Z blackboard' },
  { label: 'ℚ', command: '\\mathbb{Q}', keywords: 'rationals Q blackboard' },
  { label: 'ℂ', command: '\\mathbb{C}', keywords: 'complex numbers C blackboard' },
  { label: '⌈⌉', command: '\\left\\lceil \\right\\rceil', keywords: 'ceiling ceil delimiters' },
  { label: '⌊⌋', command: '\\left\\lfloor \\right\\rfloor', keywords: 'floor delimiters' },
  { label: '⟨⟩', command: '\\left\\langle \\right\\rangle', keywords: 'angle brackets inner product' },
];

export function searchMathLiveSymbols(query: string, limit = 30): readonly MathLiveSymbol[] {
  const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
  if (terms.length === 0) return MATHLIVE_SYMBOLS.slice(0, limit);
  return MATHLIVE_SYMBOLS.filter((symbol) => {
    const haystack = `${symbol.label} ${symbol.command} ${symbol.keywords}`.toLowerCase();
    return terms.every((term) => haystack.includes(term));
  }).slice(0, limit);
}
