from yta_shortcodes.tag import ShortcodeTag
from yta_shortcodes.shortcode import Shortcode
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[Shortcode]
    """
    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)

        # ChatGPT says this is more simple
        for shortcode_tag in shortcode_tags:
            def register_shortcode(pargs, kwargs, context, content = None):
                content = content if shortcode_tag.is_block_scoped else None
                shortcode_tag.handler(self.shortcodes, pargs, kwargs, context, content)
            
            self._parser.register(register_shortcode)

        # # ChatGPT says this is possible
        # self._parser.register(
        #     lambda pargs, kwargs, context, content = None:
        #     shortcode_tag.handler(self.shortcodes, pargs, kwargs, context, content if shortcode_tag.is_block_scoped else None)
        # )

        # # This code below is working for sure
        # for shortcode_tag in shortcode_tags:
        #     if shortcode_tag.is_block_scoped:
        #         self._parser.register(
        #             lambda pargs, kwargs, context, content:
        #             shortcode_tag.handler(self.shortcodes, pargs, kwargs, context, content)
        #         )
        #     else:
        #         self._parser.register(
        #             lambda pargs, kwargs, context:
        #             shortcode_tag.handler(self.shortcodes, pargs, kwargs, context, None)
        #         )

    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(' ')

        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.name == word.replace('[', '').replace(']', '').replace('/', '') and not shortcode.end_previous_word_index:
                            # Is that shortcode
                            shortcode.end_previous_word_index = index - 1
                            break
                    else:
                        # Start shortcode
                        if shortcode.tag == word.replace('[', '').replace(']', '') and not shortcode.start_previous_word_index:
                            # 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