# -*- coding: utf-8 -*-
import logging
from urllib.parse import urljoin

from bs4 import BeautifulSoup
from django import template
from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.sites.shortcuts import get_current_site
from django.template import TemplateSyntaxError
from django.template.loader_tags import do_include
from django.urls import reverse
from django.urls.exceptions import NoReverseMatch
from django.utils.safestring import mark_safe

from ocyan.main.loading import get_ocyan_class
from ocyan.main.utils import resolve_entities

register = template.Library()
logger = logging.getLogger(__name__)

NonceNode = get_ocyan_class("main.utils", "NonceNode", module_prefix="ocyan")


@register.simple_tag(takes_context=True)
def is_popup(context):
    request = context.get("request")
    if not request:
        return False
    return IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET


@register.filter
def stripformatting(html):
    """
    Strip formatting attributes from the passed html

    Strips style attributes, font tags style tags and script tags and empty tags.
    Also corrects invalid html

    >>> html = '<p><span style="color: rgb(69, 69, 69); font-family: &quot;Open Sans&quot;, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">Ik heet klaas</span></p><p>hai</p>'
    >>> stripformatting(html)
    '<p><span>Ik heet klaas</span></p><p>hai</p>'
    >>> html = '<p><font face="verdana" color="green">This is some text!</font></p><p><font size="3" color="red">This is some text!</font></p>'
    >>> stripformatting(html)
    '<p>This is some text!</p><p>This is some text!</p>'
    >>> html = '<p><style>h1 { 5px; }</style><h1>hai</p>'
    >>> stripformatting(html)
    '<p></p><h1>hai</h1>'
    >>> html = '<p>henk is een dork<br/>en een sukkel</p>'
    >>> stripformatting(html)
    '<p>henk is een dork<br/>en een sukkel</p>'
    >>> html = '<p>henk is een dork<br/>en een sukkel'
    >>> stripformatting(html)
    '<p>henk is een dork<br/>en een sukkel</p>'
    >>> html = "ḷꝉɑ𝒏ммȃṋẵᶑℹҝκḙ"
    >>> stripformatting(html)
    'ḷꝉɑ𝒏ммȃṋẵᶑℹҝκḙ'
    >>> html = '<script type="text/javascript">window.botcoinbot.start()</script><p>hai ik ben onschuldig</p>'
    >>> stripformatting(html)
    '<p>hai ik ben onschuldig</p>'
    >>> html = '<p>I am &apos;&CloseCurlyDoubleQuote;&apos; > &apos;&#32;&apos; > &apos;&#x20;&apos; &gt; and I rock</p>'
    >>> stripformatting(html)
    "<p>I am '”' &gt; ' ' &gt; ' ' &gt; and I rock</p>"
    >>> html = ""
    >>> stripformatting(html)
    ''
    >>> html = None
    >>> stripformatting(html)
    ''
    >>> html = '<iframe width="490" height="344" src="https://www.youtube.com/embed/tiDp2bkj8VQ" frameborder="0" allowfullscreen></iframe>'
    >>> stripformatting(html)
    '<iframe allowfullscreen="" frameborder="0" height="344" src="https://www.youtube.com/embed/tiDp2bkj8VQ" width="490"></iframe>'
    """
    try:
        unescaped_html = resolve_entities(html).strip()
        if not unescaped_html:
            return ""

        # Skip html/body as we don't want them.
        # lxml will add them if they don't already exist.
        soup = BeautifulSoup(unescaped_html, "lxml").html.body
        for junk in soup.find_all(["style", "script"]):
            junk.decompose()

        tags = soup.find_all()
        for tag in tags:
            if tag.name in ["iframe"]:
                continue
            if tag.name == "font":
                tag.replaceWithChildren()
            if not tag.isSelfClosing and len(tag.get_text(strip=True)) == 0:
                tag.decompose()
            else:
                tag.attrs.clear()

        return mark_safe("".join(map(str, soup.contents)))
    except Exception as e:  # pylint: disable=broad-except
        # catch any exception and log so we can improve the filter.
        logger.exception(e)
        return html


@register.simple_tag(takes_context=True)
def absolute_url(context, path_or_name):
    """
    Tries to turns a path into an absolute url.

    Usage::
    >>> from django.template import RequestContext, Template, Context
    >>> from django.test import RequestFactory
    >>> from argparse import Namespace
    >>> factory = RequestFactory()
    >>> request = factory.get('/')
    >>> tpl = Template("{% load ocyan_main %}{% absolute_url path %}")
    >>> tpl.render(RequestContext(request, {"path": "/kanarie/pietje"}))
    'http://testserver/kanarie/pietje'
    >>> tpl.render(RequestContext(request, {"path": "kanarie/pietje"}))
    'http://testserver/kanarie/pietje'
    >>> tpl = Template("{% load ocyan_main %}{% absolute_url path %}")
    >>> tpl.render(Context({"path": "/example/com"}))
    'https://example.com/example/com'
    >>> tpl.render(Context({"path": "example/com"}))
    'https://example.com/example/com'
    >>> site = Namespace(domain="lol.org")
    >>> tpl = Template("{% load ocyan_main %}{% absolute_url path %}")
    >>> tpl.render(Context({"path": "/ru/?exa=true", "site": site}))
    'https://lol.org/ru/?exa=true'
    >>> tpl.render(Context({"path": "ru/?exa=true", "site": site}))
    'https://lol.org/ru/?exa=true'
    >>> tpl.render(Context({"path": "admin:index"}))
    'https://example.com/manage/'
    >>> tpl.render(Context({"path": "admin:index", "site": site}))
    'https://lol.org/manage/'
    >>> tpl.render(RequestContext(request, {"path": "admin:index"}))
    'http://testserver/manage/'
    """
    if path_or_name.startswith("http"):
        return path_or_name
    elif not path_or_name.startswith("/"):
        try:
            path = reverse(path_or_name)
        except NoReverseMatch:
            path = path_or_name
    else:
        path = path_or_name

    if "request" in context:
        request = context["request"]
        return request.build_absolute_uri(path)

    site = context.get("site", get_current_site(context.get("request")))
    return urljoin("https://%s" % site.domain, path)


@register.simple_tag(name="eval", takes_context=True)
def ocyan_eval(context, html):
    try:
        return template.Template(html).render(context)
    except TemplateSyntaxError:
        return mark_safe(html)


def do_include_only(parser, token):
    if "only" not in token.contents:
        raise TemplateSyntaxError(
            "esi_fragment MUST have isolated context, please add 'only' like the include tag: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#include"
        )
    return do_include(parser, token)


register.tag(name="esi_fragment")(do_include_only)


@register.tag
def nonce(parser, token):  # pylint: disable=unused-argument
    return NonceNode()
