import { afterAll, beforeEach, describe, expect, test } from '@jest/globals';
import {
  getTokens,
  setupTex,
  setupTexTypeset,
  tex2mml,
  typeset2mml,
  setupComponents,
  expectTexError,
  expectTypesetError,
} from '#helpers';
import '#js/input/tex/textmacros/TextMacrosConfiguration';
import '#js/input/tex/newcommand/NewcommandConfiguration';
import '#js/input/tex/color/ColorConfiguration';
import '#js/input/tex/html/HtmlConfiguration';
import '#js/input/tex/unicode/UnicodeConfiguration';

import { Configuration } from '#js/input/tex/Configuration.js';
import { HandlerType, ConfigurationType } from '#js/input/tex/HandlerTypes.js';

/**********************************************************************************/

describe('Textmacros', () => {
  beforeEach(() => setupTex(['base', 'newcommand', 'color', 'textmacros']));

  test('Text plain', () => {
    expect(tex2mml('\\text{abc}')).toMatchSnapshot();
  });

  test('Text macro', () => {
    expect(tex2mml('\\text{abc \\large abc}')).toMatchSnapshot();
  });

  test('Text unknown macro', () => {
    expectTexError('\\text{\\xyz}').toBe('Undefined control sequence \\xyz');
  });

  test('Text substitution macro', () => {
    expect(tex2mml('\\def\\x{X} \\text{\\x}')).toMatchSnapshot();
  });

  test('Text mathmode macro', () => {
    expectTexError('\\text{\\frac{1}{2}}').toBe(
      '\\frac is only supported in math mode'
    );
  });

  test('Text internal math', () => {
    expect(tex2mml('\\text{a $\\frac{1}{2}$ b}')).toMatchSnapshot();
  });

  test('Text internal math complex', () => {
    expect(tex2mml('\\text{a $x+y$ b}')).toMatchSnapshot();
  });

  test('Textbf ', () => {
    expect(tex2mml('\\def\\x{X} \\textbf{a\\x}')).toMatchSnapshot();
  });

  test('Text ref', () => {
    expect(tex2mml('\\textbf{a \\ref{a}}')).toMatchSnapshot();
  });

  test('HBox size', () => {
    expect(tex2mml('\\scriptstyle\\hbox{a}')).toMatchSnapshot();
  });

  test('Text color', () => {
    expect(tex2mml('\\text{a\\color{red}b}')).toMatchSnapshot();
  });

  test('Text color math', () => {
    expect(tex2mml('\\text{a\\color{red}b$c$}')).toMatchSnapshot();
  });

  test('Text color math complex', () => {
    expect(tex2mml('\\text{a\\color{red}b$c+d$}')).toMatchSnapshot();
  });

  test('Unbalanced Braces', () => {
    expectTexError('\\text{a ${$ b }}').toBe(
      'Math mode is not properly terminated'
    );
  });

  test('Unbalanced Braces', () => {
    expectTexError('\\text{{a $}$ b }').toBe(
      'Extra close brace or missing open brace'
    );
  });

  test('Unbalanced Braces', () => {
    expectTexError('\\let\\x=}\\text{a \\x}').toBe(
      'Extra close brace or missing open brace'
    );
  });
});

/**********************************************************************************/

describe('Textmacros Specials', () => {
  beforeEach(() => setupTex(['base', 'textmacros']));

  test('Dollar', () => {
    expect(tex2mml('\\text{a$b$}')).toMatchSnapshot();
  });

  test('Percent', () => {
    expect(tex2mml('\\text{a%comment\nb}')).toMatchSnapshot();
  });

  test('Hat', () => {
    expectTexError('\\text{a^b}').toBe("'^' allowed only in math mode");
  });

  test('Underscore', () => {
    expectTexError('\\text{a_b}').toBe("'_' allowed only in math mode");
  });

  test('Ampersand', () => {
    expectTexError('\\text{a&b}').toBe("Misplaced '&'");
  });

  test('Hash', () => {
    expectTexError('\\text{a#b}').toBe("Misplaced '#'");
  });

  test('Tilde', () => {
    expect(tex2mml('\\text{a~b}')).toMatchSnapshot();
  });

  test('Spaces', () => {
    expect(tex2mml('\\text{a b\t c\r d\n e\u{00A0}f\\ g}')).toMatchSnapshot();
  });

  test('Braces', () => {
    expect(tex2mml('\\text{a {b} c}')).toMatchSnapshot();
  });

  test('Quotes', () => {
    expect(tex2mml("\\text{a ``b'' `c' d}")).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Textmacros Macros', () => {
  beforeEach(() =>
    setupTex(['base', 'newcommand', 'color', 'html', 'unicode', 'textmacros'])
  );

  test('Internal Math', () => {
    expect(tex2mml('\\text{a\\(b\\)}')).toMatchSnapshot();
  });

  test('Quoted Specials', () => {
    expect(tex2mml('\\text{a\\$b\\_c\\%d\\{e\\}f\\&g\\#h}')).toMatchSnapshot();
  });

  test('Newline', () => {
    expect(tex2mml('\\text{a\\\\b}')).toMatchSnapshot();
  });

  test('Accents', () => {
    expect(
      tex2mml(
        '\\text{\\\'a\\\u2019a\\`a\\\u2018a\\^a\\"a\\~a\\=a\\.a\\u a\\v a}'
      )
    ).toMatchSnapshot();
  });

  test('emph', () => {
    expect(tex2mml('\\text{a\\emph{b\\emph{c}}d}')).toMatchSnapshot();
  });

  test('rm', () => {
    expect(tex2mml('\\text{a{\\rm b}c}')).toMatchSnapshot();
  });

  test('mit', () => {
    expect(tex2mml('\\text{a{\\mit b}c}')).toMatchSnapshot();
  });

  test('oldstyle', () => {
    expect(tex2mml('\\text{0{\\oldstyle 1}2}')).toMatchSnapshot();
  });

  test('cal', () => {
    expect(tex2mml('\\text{a{\\cal B}c}')).toMatchSnapshot();
  });

  test('it', () => {
    expect(tex2mml('\\text{a{\\it b}c}')).toMatchSnapshot();
  });

  test('bf', () => {
    expect(tex2mml('\\text{a{\\bf b}c}')).toMatchSnapshot();
  });

  test('sf', () => {
    expect(tex2mml('\\text{a{\\sf b}c}')).toMatchSnapshot();
  });

  test('tt', () => {
    expect(tex2mml('\\text{a{\\tt b}c}')).toMatchSnapshot();
  });

  test('frak', () => {
    expect(tex2mml('\\text{a{\\frak b}c}')).toMatchSnapshot();
  });

  test('Bbb', () => {
    expect(tex2mml('\\text{a{\\Bbb b}c}')).toMatchSnapshot();
  });

  test('Sizes small', () => {
    expect(
      tex2mml(
        '\\text{{\\tiny a\\Tiny a \\scriptsize a \\small a\\normalsize a}a}'
      )
    ).toMatchSnapshot();
  });

  test('Sizes large', () => {
    expect(
      tex2mml('\\text{a\\large a\\Large a\\LARGE a\\huge a\\Huge a}')
    ).toMatchSnapshot();
  });

  test('Text fonts', () => {
    expect(
      tex2mml(
        '\\text{\\Bbb a\\textnormal{a}\\textup{a}\\textrm{a}\\textit{a}\\textbf{a}\\textsf{a}\\texttt{a}}'
      )
    ).toMatchSnapshot();
  });

  test('Text symbols', () => {
    expect(
      tex2mml('\\text{\\dagger\\ddagger\\S\\AA a\\ldots b\\vdots c}')
    ).toMatchSnapshot();
  });

  test('Text space macros 1', () => {
    expect(tex2mml('\\text{a\\,b\\:c\\>d\\;e\\!f}')).toMatchSnapshot();
  });

  test('Text space macros 2', () => {
    expect(
      tex2mml(
        '\\text{a\\enspace b\\quad c\\qquad d\\thinspace e\\negthinspace f}'
      )
    ).toMatchSnapshot();
  });

  test('Text spacing macros', () => {
    expect(
      tex2mml(
        '\\text{a\\hskip 1emb\\hspace{1em}c\\kern{.1em}d\\mskip{1em}e\\mspace{1em}f\\mkern{.1em}g}'
      )
    ).toMatchSnapshot();
  });

  test('Text rules and spaces', () => {
    expect(
      tex2mml(
        '\\text{\\Rule{1em}{.5em}{.5em}\\Space{1em}{.5em}{0em}\\rule[.25em]{1em}{.5em}}'
      )
    ).toMatchSnapshot();
  });

  test('Color', () => {
    expect(
      tex2mml(
        '\\text{{\\color{red}{x}}\\textcolor{yellow}{A}\\colorbox{green}{x}\\fcolorbox{blue}{orange}{x}}'
      )
    ).toMatchSnapshot();
  });

  test('HTML', () => {
    expect(
      tex2mml(
        '\\text{\\href{tmp.html}{x}\\style{padding:3px}{x}\\class{test}{x}\\data{test=1}{x}\\cssId{test}{x}}'
      )
    ).toMatchSnapshot();
  });

  test('Unicode', () => {
    expect(
      tex2mml('\\text{\\unicode{x61}\\U{3333}\\char"65}')
    ).toMatchSnapshot();
  });

  test('Ref and Eqref', () => {
    expect(tex2mml('\\text{\\ref{a}\\eqref{b}}')).toMatchSnapshot();
  });

  test('Underline', () => {
    expect(tex2mml('\\text{a \\underline{b c} d}')).toMatchSnapshot();
  });

  test('lapping', () => {
    expect(tex2mml('\\text{\\rlap{a}-- --\\llap{b}}')).toMatchSnapshot();
  });

  test('Phantoms', () => {
    expect(
      tex2mml('\\text{\\phantom{a}a\\vphantom{a}a\\hphantom{a}a\\smash{a}}')
    ).toMatchSnapshot();
  });

  test('mmlToken', () => {
    expect(tex2mml('\\text{\\mmlToken{mo}{a}}')).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Textmacros Autoload', () => {
  setupComponents({
    loader: { load: ['input/tex-base', '[tex]/textmacros', '[tex]/autoload'] },
  });

  test('Autoload not present', async () => {
    setupTexTypeset(['base', 'textmacros']);
    await expectTypesetError('\\text{\\href{tmp.html}{a}}').toBe(
      'Undefined control sequence \\href'
    );
  });

  test('Autoload', async () => {
    setupTexTypeset(['base', 'textmacros', 'autoload']);
    expect(await typeset2mml('\\text{\\href{tmp.html}{a}}')).toMatchSnapshot();
  });

  test('No Autoload', async () => {
    Configuration.create('no-autoload', {
      [ConfigurationType.FALLBACK]: {
        [HandlerType.MACRO]: () => {},
      },
    });
    setupTexTypeset(['base', 'textmacros', 'no-autoload']);
    expect(await typeset2mml('\\text{\\href{tmp.html}{a}}')).toMatchSnapshot();
  });
});

/**********************************************************************************/

afterAll(() => getTokens('text-base'));
