'use strict';

const javascript = require('./javascript.cjs');
const markup = require('./markup.cjs');

// @ts-nocheck
/**
 * @import {Refractor} from '../lib/core.js'
 */
jsx.displayName = 'jsx';
jsx.aliases = [];

/** @param {Refractor} Prism */
function jsx(Prism) {
  Prism.register(javascript);
  Prism.register(markup)
  ;(function (Prism) {
    var javascript = Prism.util.clone(Prism.languages.javascript);
    var space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
    var braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
    var spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;

    /**
     * @param {string} source
     * @param {string} [flags]
     */
    function re(source, flags) {
      source = source
        .replace(/<S>/g, function () {
          return space
        })
        .replace(/<BRACES>/g, function () {
          return braces
        })
        .replace(/<SPREAD>/g, function () {
          return spread
        });
      return RegExp(source, flags)
    }
    spread = re(spread).source;
    Prism.languages.jsx = Prism.languages.extend('markup', javascript);
    Prism.languages.jsx.tag.pattern = re(
      /<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/
        .source
    );
    Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/;
    Prism.languages.jsx.tag.inside['attr-value'].pattern =
      /=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/;
    Prism.languages.jsx.tag.inside['tag'].inside['class-name'] =
      /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
    Prism.languages.jsx.tag.inside['comment'] = javascript['comment'];
    Prism.languages.insertBefore(
      'inside',
      'attr-name',
      {
        spread: {
          pattern: re(/<SPREAD>/.source),
          inside: Prism.languages.jsx
        }
      },
      Prism.languages.jsx.tag
    );
    Prism.languages.insertBefore(
      'inside',
      'special-attr',
      {
        script: {
          // Allow for two levels of nesting
          pattern: re(/=<BRACES>/.source),
          alias: 'language-javascript',
          inside: {
            'script-punctuation': {
              pattern: /^=(?=\{)/,
              alias: 'punctuation'
            },
            rest: Prism.languages.jsx
          }
        }
      },
      Prism.languages.jsx.tag
    );

    // The following will handle plain text inside tags
    var stringifyToken = function (token) {
      if (!token) {
        return ''
      }
      if (typeof token === 'string') {
        return token
      }
      if (typeof token.content === 'string') {
        return token.content
      }
      return token.content.map(stringifyToken).join('')
    };
    var walkTokens = function (tokens) {
      var openedTags = [];
      for (var i = 0; i < tokens.length; i++) {
        var token = tokens[i];
        var notTagNorBrace = false;
        if (typeof token !== 'string') {
          if (
            token.type === 'tag' &&
            token.content[0] &&
            token.content[0].type === 'tag'
          ) {
            // We found a tag, now find its kind

            if (token.content[0].content[0].content === '</') {
              // Closing tag
              if (
                openedTags.length > 0 &&
                openedTags[openedTags.length - 1].tagName ===
                  stringifyToken(token.content[0].content[1])
              ) {
                // Pop matching opening tag
                openedTags.pop();
              }
            } else {
              if (token.content[token.content.length - 1].content === '/>') ; else {
                // Opening tag
                openedTags.push({
                  tagName: stringifyToken(token.content[0].content[1]),
                  openedBraces: 0
                });
              }
            }
          } else if (
            openedTags.length > 0 &&
            token.type === 'punctuation' &&
            token.content === '{'
          ) {
            // Here we might have entered a JSX context inside a tag
            openedTags[openedTags.length - 1].openedBraces++;
          } else if (
            openedTags.length > 0 &&
            openedTags[openedTags.length - 1].openedBraces > 0 &&
            token.type === 'punctuation' &&
            token.content === '}'
          ) {
            // Here we might have left a JSX context inside a tag
            openedTags[openedTags.length - 1].openedBraces--;
          } else {
            notTagNorBrace = true;
          }
        }
        if (notTagNorBrace || typeof token === 'string') {
          if (
            openedTags.length > 0 &&
            openedTags[openedTags.length - 1].openedBraces === 0
          ) {
            // Here we are inside a tag, and not inside a JSX context.
            // That's plain text: drop any tokens matched.
            var plainText = stringifyToken(token);

            // And merge text with adjacent text
            if (
              i < tokens.length - 1 &&
              (typeof tokens[i + 1] === 'string' ||
                tokens[i + 1].type === 'plain-text')
            ) {
              plainText += stringifyToken(tokens[i + 1]);
              tokens.splice(i + 1, 1);
            }
            if (
              i > 0 &&
              (typeof tokens[i - 1] === 'string' ||
                tokens[i - 1].type === 'plain-text')
            ) {
              plainText = stringifyToken(tokens[i - 1]) + plainText;
              tokens.splice(i - 1, 1);
              i--;
            }
            tokens[i] = new Prism.Token(
              'plain-text',
              plainText,
              null,
              plainText
            );
          }
        }
        if (token.content && typeof token.content !== 'string') {
          walkTokens(token.content);
        }
      }
    };
    Prism.hooks.add('after-tokenize', function (env) {
      if (env.language !== 'jsx' && env.language !== 'tsx') {
        return
      }
      walkTokens(env.tokens);
    });
  })(Prism);
}

module.exports = jsx;
