from ralium.errors import PyHTMLSyntaxError
from ralium._util import NamedDict
import copy
import re

RE_HTML_PATTERN = r"((<.*>.*</.*>)|(<.*/>))"

class PyHTML:
    def __init__(self, webhook):
        self._html = webhook.html
        self._webhook = webhook
    
    def compile(self):
        """
        Compiles the HTML content of the webhook by processing embedded Ralium tags.

        This method extracts blocks of code enclosed by `{%ralium` and `%}`, processes these blocks,
        executes them, and replaces the original blocks with the resulting HTML content.

        :returns: The compiled HTML content as a string.
        """

        _locals = PyHTML.locals(self._webhook)
        _blocks = PyHTML.get_enclosed(self._html, "{%ralium", "%}")
        _data = self.process(_blocks)

        for code, block in zip(_data, _blocks):
            outcome = filter(lambda v: v is not None, PyHTML.get_outcome(code, copy.deepcopy(_locals)))
            self._html = self._html.replace(block, "\n".join(outcome))
        
        return self._html
    
    def process(self, blocks):
        """
        Processes blocks of Ralium code to convert HTML tags into Python code.

        This method takes a list of blocks containing Ralium code, processes each block to 
        convert HTML tags into corresponding Python code that appends the tags to a list.

        :param blocks: A list of code blocks to process.

        :returns: A list of processed code blocks as strings.
        """

        data = []

        for block in blocks:
            lines = []
            
            for line in block.split("\n")[1:-1]:
                tags = re.findall(RE_HTML_PATTERN, line)
                
                if not tags: 
                    lines.append(line)
                    continue

                for tag in tags:
                    tag = tag[0]
                    rep = tag

                    if rep.startswith('<">') and rep.endswith('</">'):
                        rep = rep.removeprefix('<">')
                        rep = rep.removesuffix('</">')

                    rep = rep.replace('"', '\\"')
                    lines.append(line.replace(tag, f'__ralium_html__.append("{rep}")'))
            
            data.append("\n".join(lines))
        
        return data
    
    @staticmethod
    def locals(webhook):
        """
        Creates a locals dictionary from a `WebHook` object.

        This method generates a dictionary of local variables that includes the functions 
        and namespaces provided by the `WebHook` object. These local variables can be used 
        when executing Python code blocks within the HTML.

        :param webhook: The `WebHook` object containing the functions and namespaces.

        :returns: A dictionary of local variables including functions and namespaces from the `WebHook`.
        """

        return {
            "__ralium_html__": [],
            **{f.__name__: f for f in webhook.functions},
            **{n.name: NamedDict(n) for n in webhook.namespaces}
        }

    @staticmethod
    def get_outcome(code, locals):
        """
        Get the outcome of a Python code block.

        This method executes a given Python code block within a specified local context and 
        collects the resulting HTML elements.

        :param code: The code block to execute.
        :param locals: The dictionary of local variables including functions and namespaces.

        :returns: A list of HTML elements generated by executing the code block.
        """

        exec(f"def __ralium_executor__():\n{code}", locals)
        eval("__ralium_executor__()", locals)

        return locals["__ralium_html__"]

    @staticmethod
    def get_enclosed(text, start, end):
        """
        Extracts all substrings from the given text that are enclosed by the specified start and end markers.

        :param text: The input string to search within.
        :param start: The starting delimiter of the enclosed substrings.
        :param end: The ending delimiter of the enclosed substrings.

        :returns: A list of substrings found within the start and end delimiters.

        :raises PyHTMLSyntaxError: If an ending delimiter is missing for a corresponding start delimiter.
        """

        found = []
        _start = 0

        while (_start := text.find(start, _start + len(start))) != -1:
            _end = text.find(end, _start + len(start))

            if _end == -1:
                raise PyHTMLSyntaxError(f"Missing ending symbol '{end}'")
            
            _end += len(end)
            found.append(text[_start:_end])
        
        return found