from AccessControl import Unauthorized
from lxml import etree
from plone.app.uuid.utils import uuidToCatalogBrain
from ttp import ttp


def link_tags(text="", base_url="", tags=None):
    """Link/Add tags in the text.

    Replace all elements with a data-tag attribute with a link to the
    tagstream (new style) or append tag links at the end of the text
    (old style).
    """

    tpl = """
      <a href="{base_url}/@@tagstream/{tag}#app-space"
         class="follow tag"
         data-tag="{tag}">{tag_text}</a>
    """

    # Note: Using the HTMLParser always wraps the markup in ``<html><body>``
    #       structure.
    #       Using no explicit parser fails with multiple top-level nodes, which
    #       is a common case for a inline text editor.
    #       Therefore we're using the HTMLParser and afterwards removing the
    #       ``<html><body>`` structure again.
    parsed = etree.fromstring(text, etree.HTMLParser()) if text else None
    linked_tags = parsed.xpath("//a[@data-tag]") if parsed is not None else None

    if linked_tags:
        # New-style tags, directly linked in the text.
        for el in linked_tags:
            tag = el.attrib["data-tag"]

            el_html = tpl.format(
                base_url=base_url,
                tag=tag,
                tag_text=el.text,
            )

            # Replace old element with new, parsed one.
            #
            # The following does not only replace the element, but all the
            # element's following text within the current parent.
            # ``el.getparent().replace(el, etree.fromstring(el_html))``
            # So instead we recreate the ``<a>`` element like so:

            # Parse new element markup into an lxml structure,
            new_el = etree.fromstring(el_html)
            # Remove all attributes from old element
            for attr in el.attrib:
                del el.attrib[attr]
            # Add new attributes from new element
            for key, val in new_el.attrib.items():
                el.attrib[key] = val

        text = etree.tostring(parsed)
        # Now removing the ``<html><body>`` wrapper
        text = (
            text.decode("utf-8")
            .replace("<html>", "")
            .replace("<body>", "")
            .replace("</body>", "")
            .replace("</html>", "")
        )

    elif tags:
        # Old-style tags, where tags were stored on the context.
        text += " &mdash;"
        for tag in tags:
            text += tpl.format(
                base_url=base_url,
                tag=tag,
                tag_text=f"#{tag}",
            )

    return text


def link_users(text="", base_url="", mentions=None, pas_view=None):
    """Link/Add mentions in the text.

    Replace all elements with a data-mention attribute with a link to
    the user profile, group page or workspace (new style) or append
    mention links at the end of the text (old style).
    """

    tmpl_user = """
      <a href="{mention_url}#app-space"
         class="follow user-{mention_id} pat-inject pat-switch"
         data-mention="{mention}"
         data-pat-inject="history: record"
         data-pat-switch="#app-space state-off state-on">{mention_text}</a>
    """

    tmpl_group = """
      <a href="{mention_url}#app-space"
         class="follow group-{mention_id} pat-inject pat-switch"
         data-mention="{mention}"
         data-pat-inject="history: record"
         data-pat-switch="#app-space state-off state-on">{mention_text}</a>
    """

    tmpl_workspace = """
      <a href="{mention_url}#app-space"
         class="follow workspace-{mention_id} pat-inject pat-switch"
         data-mention="{mention}"
         data-pat-inject="history: record"
         data-pat-switch="#app-space state-off state-on">{mention_text}</a>
    """

    # Note: Using the HTMLParser always wraps the markup in ``<html><body>``
    #       structure.
    #       Using no explicit parser fails with multiple top-level nodes, which
    #       is a common case for a inline text editor.
    #       Therefore we're using the HTMLParser and afterwards removing the
    #       ``<html><body>`` structure again.
    parsed = etree.fromstring(text, etree.HTMLParser()) if text else None
    linked_mentions = parsed.xpath("//a[@data-mention]") if parsed is not None else None

    if linked_mentions:
        # New-style mentions, directly linked in the text.
        for el in linked_mentions:
            mention = el.attrib["data-mention"]
            sep = ":"
            mention_type, sep, mention_uid = mention.partition(":")

            mention_id = None
            mention_url = None
            if mention_type == "workspace":
                # Special case "Workspace"
                brain = uuidToCatalogBrain(mention_uid)
                mention_id = brain.id
                mention_url = brain.getURL()
            else:
                # names = pas_view.uids2principalnames([mention])
                # if not names:
                #     continue
                # mention_name = names.pop()  # There is only one match.
                try:
                    mention_ob = pas_view.resolvepasuid(mention)
                except Unauthorized:
                    # user was disabled, don't actually link
                    mention_ob = None
                except KeyError:
                    # user or workgroup was deleted
                    mention_ob = None
                if mention_ob:
                    mention_id = mention_ob.id
                    mention_url = mention_ob.absolute_url()

            tpl = tmpl_user
            if mention_type == "workgroup":
                tpl = tmpl_group
            elif mention_type == "workspace":
                tpl = tmpl_workspace

            if mention and mention_id and mention_url:
                el_html = tpl.format(
                    base_url=base_url,
                    mention=mention,
                    mention_id=mention_id,
                    mention_text=el.text,
                    mention_url=mention_url,
                )
            else:
                el_html = f"""<span class="inactive">{el.text}</span>"""
            # Replace old element with new, parsed one.
            new_el = etree.fromstring(el_html)
            # make sure to also transplant the text after the element,
            # until the next one.
            new_el.tail = el.tail
            el.getparent().replace(el, new_el)

        text = etree.tostring(parsed)
        # Now removing the ``<html><body>`` wrapper
        text = (
            text.decode("utf-8")
            .replace("<html>", "")
            .replace("<body>", "")
            .replace("</body>", "")
            .replace("</html>", "")
        )

    elif mentions:
        # Old-style mentions, where mentions were stored on the context.
        text += " &mdash;"
        for user_id, fullname in mentions.items():
            mention_url = f"{base_url}/profiles/{user_id}#app-space"
            text += tmpl_user.format(
                base_url=base_url,
                mention="",
                mention_id=user_id,
                mention_text=f"@{fullname}",
                mention_url=mention_url,
            )

    return text


def link_urls(text):
    """Enriches urls in the comment text with an anchor."""
    parser = ttp.Parser(max_url_length=40)
    parser._urls = []
    return ttp.URL_REGEX.sub(parser._parse_urls, text)
