"""
Here it is the shortcode parser, the code that
registers the different shortcode tags to find
in a given text and the handler methods to
extract the attributes of those shortcodes and
anything else that has been programmed to do
with them.

The parser registers the shortcode tags you
provide, detects those tags, extract the
information of them, minimizes the shortcodes,
gets the positions in which they are placed and,
finally, remove them to clean and leave the text
only with no shortcodes.

Any unregistered shortcode is unaccepted.
"""
from yta_shortcodes.tag import ShortcodeTag
from yta_shortcodes.shortcode import YTAShortcode
from yta_general_utils.programming.parameter_validator import PythonValidator
from yta_general_utils.programming.regular_expressions import GeneralRegularExpression
from yta_general_utils.text.transformer import fix_unseparated_periods, fix_excesive_blank_spaces, fix_separated_parenthesis, add_missing_spaces_before_and_after_parenthesis, fix_separated_square_brackets, add_missing_spaces_before_and_after_square_brackets, fix_ellipsis
from shortcodes import Parser


class ShortcodeParser:
    """
    This class detects in a text the shortcodes
    you register on it, being able to classify
    the content according to the type and what
    is defined on each shortcode you register.

    _The developer must register manually the
    shortcode parser method for each shortcode_.
    """
    
    _parser: Parser = None
    """
    The shortcode parser, for internal use only.
    """
    shortcodes: list[YTAShortcode]
    """
    The list of shortcodes that have been found
    in the last analyzed text.
    """

    def __init__(
        self,
        shortcode_tags: list[ShortcodeTag]
    ):
        """
        The 'shortcode_tags' parameters is the list of
        shortcodes we want to detect.
        """
        if any(
            not PythonValidator.is_subclass(shortcode_tag, ShortcodeTag)
            for shortcode_tag in shortcode_tags
        ):
            raise Exception('At least one of the "shortcodes" provided is not a subclass of ShortcodeTag.')

        self._parser = Parser(start = '[', end = ']', inherit_globals = False)

        # We create a function to register individually each
        # shortcode and it's handler so we don't get the
        # handlers overrided
        def register_shortcode_handler(self, shortcode_tag):
            """
            Register the shortcode handler specifically and
            individually for each shortcode.
            """
            def handler(pargs, kwargs, context, content = None):
                return shortcode_tag.handler(
                    self.shortcodes,
                    pargs,
                    kwargs,
                    context,
                    content = content if shortcode_tag.is_block_scoped else None
                )
            
            # Registra el handler individual para ese shortcode
            self._parser.register(
                handler, 
                shortcode_tag.name,
                f'/{shortcode_tag.name}' if shortcode_tag.is_block_scoped else None
            )

        for shortcode_tag in shortcode_tags:
            register_shortcode_handler(self, shortcode_tag)

    def parse(self, text: str):
        """
        Parse the provided 'text' according to the 
        shortcodes that have been registered.
        """
        if not PythonValidator.is_string(text):
            raise Exception('The provided "text" is not a valid string.')
        
        def sanitize_text(text: str):
            """
            Remove anything unwanted from the given 'text'.
            """
            return fix_excesive_blank_spaces(fix_ellipsis(fix_unseparated_periods(fix_separated_parenthesis(fix_separated_square_brackets(add_missing_spaces_before_and_after_parenthesis(add_missing_spaces_before_and_after_square_brackets(text)))))))
        
        self.text = text
        self.text_sanitized = sanitize_text(text)
        self.text_sanitized_with_simplified_shortcodes = ''
        self.text_sanitized_without_shortcodes = ''
        self.shortcodes = []

        """
        At the begining we have full shortcodes
        containing attributes but when parsing
        ([tag attribute=value]), we extract that 
        information to Shortcode objects and we
        just preserve the simple tag ([tag]) to
        know that there is a shortcode in that
        position.
        
        Once we've done that, we can iterate over
        the text again obtaining each shortcode
        position and extracting the previous word
        position in the text.
        """

        # Parse shortcodes, extract the information
        # and fulfill the 'shortcodes' attribute
        self.text_sanitized_with_simplified_shortcodes = self._parser.parse(self.text_sanitized, context = None)

        # After this parse we have 'self.shortcodes' fulfilled
        words = self.text_sanitized_with_simplified_shortcodes.split(' ')

        # Now lets look for the simplified shortcode
        # positions
        index = 0
        while index < len(words):
            word = words[index]
            if GeneralRegularExpression.SHORTCODE.parse(word):
                # TODO: Improve this to avoid the ones completed
                for shortcode in self.shortcodes:
                    if '/' in word:
                        # End shortcode
                        if shortcode.tag == word.replace('[', '').replace(']', '').replace('/', '') and shortcode.end_previous_word_index is None:
                            # Is that shortcode
                            shortcode.end_previous_word_index = index - 1
                            break
                    else:
                        # Start shortcode
                        if shortcode.tag == word.replace('[', '').replace(']', '') and shortcode.start_previous_word_index is None:
                            # Is that shortcode
                            shortcode.start_previous_word_index = index - 1
                            break

                del(words[index])
            else:
                index += 1

        self.text_sanitized_without_shortcodes = ' '.join(words)

        return self.text_sanitized_without_shortcodes