"""Specific views for tag

:organization: Logilab
:copyright: 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""

__docformat__ = "restructuredtext en"


from cubicweb import _

from logilab.mtconverter import xml_escape

from cubicweb.predicates import (
    match_kwargs,
    match_form_params,
    match_user_groups,
    has_related_entities,
    one_etype_rset,
    is_instance,
    relation_possible,
)
from cubicweb import uilib, tags
from cubicweb_web import stdmsgs, component, facet
from cubicweb_web.views import baseviews, ajaxcontroller, xmlrss, uicfg


_afs = uicfg.autoform_section
_afs.tag_subject_of(("*", "tags", "*"), formtype="main", section="hidden")
_afs.tag_object_of(("*", "tags", "*"), formtype="main", section="hidden")

# relations displayed by some component or box below, don't display them in
# primary view
_pvs = uicfg.primaryview_section
_pvs.tag_subject_of(("*", "tags", "*"), "hidden")
_pvs.tag_object_of(("*", "tags", "*"), "hidden")
_pvs.tag_subject_of(("Tag", "tags", "*"), "relations")
_pvs.tag_attribute(("Tag", "name"), "hidden")


class TagInContextView(baseviews.InContextView):
    __regid__ = "incontext"
    __select__ = is_instance("Tag")

    def cell_call(self, row, col):
        entity = self.cw_rset.get_entity(row, col)
        self.w(
            tags.a(
                entity.view("textincontext"),
                href=entity.absolute_url(),
                title=uilib.cut(entity.dc_description(), 50),
                rel="tag",
            )
        )


class TagCloudView(baseviews.OneLineView):
    """cloud view to make a link on tagged entities appearing more or less big
    according to the number of tagged entities

    expect a result set with tag eid in the first column and number of tagged
    objects in the second column.
    expect as well:
    * `maxsize` argument, the maximum number of tagged entities by a tag in the
      cloud
    * `etype` argument, the entity type we are filtering
    """

    __regid__ = "tagcloud"
    __select__ = is_instance("Tag") & (
        match_kwargs("etype") | match_form_params("etype")
    )

    need_navigation = False
    add_div_section = False  # configure View.call behaviour

    onload_js = """
jQuery(document).tagcloud.defaults = {
  size: {start: 0.8, end: 2.5, unit: "em"},
  color: {start: "#333", end: "#FF7700"}
};
jQuery("#tagcloud a").tagcloud();
"""

    def call(self, *args, **kwargs):
        self._cw.add_js("jquery.tagcloud.js")
        self._cw.html_headers.add_onload(self.onload_js)
        self.w('<div id="tagcloud">')
        super().call(*args, **kwargs)
        self.w("</div>")

    def cell_call(self, row, col, etype=None, **kwargs):
        if etype is None:
            etype = self._cw.form["etype"]
        entity = self.cw_rset.get_entity(row, col)
        mysize = self.cw_rset[row][1]
        # generate url according to where it is called from eg. when browsing
        # blogs the tag box should point to Blogs tagged by...
        rql = f"Any X WHERE T tags X, T eid {entity.eid}, X is {etype}"
        # XXX rel=mysize, can't we set rel='tag %s' % mysize to get back the
        #     'tag' rel?
        self.w(
            tags.a(entity.name, rel=mysize, href=self._cw.build_url("view", rql=rql))
            + " "
        )


# simple boxes #################################################################


class ClosestTagsBox(component.EntityCtxComponent):
    __regid__ = "closest_tags_box"
    __select__ = component.EntityCtxComponent.__select__ & is_instance(
        "Tag",
    )
    order = 25
    title = _("closest tags")

    def init_rendering(self):
        self.closest_tags = self.entity.closest_tags_rset()
        if not self.closest_tags:
            raise component.EmptyComponent()

    def render_body(self, w):
        self._cw.view("incontext", self.closest_tags, w=w)


class SimilarityBox(component.EntityCtxComponent):
    """layout closest entities (ie. entities that share tags)"""

    __regid__ = "similarity_box"
    __select__ = component.EntityCtxComponent.__select__ & has_related_entities(
        "tags", "object", "Tag"
    )
    order = 21
    rql = (
        "Any Y,COUNT(T) GROUPBY Y ORDERBY 2 DESC %s "
        "WHERE X eid %%(x)s, T tags X, T tags Y, NOT Y eid %%(x)s"
    )

    def init_rendering(self):
        self.cw_rset = self._cw.execute(self.rql % ("LIMIT 5"), {"x": self.entity.eid})
        if not self.cw_rset:
            raise component.EmptyComponent()

    def render_title(self, w):
        if self.cw_rset.rowcount == 1:
            w(self._cw._("similar entity"))
        else:
            w(self._cw._("similar entities"))

    def render_body(self, w):
        self._cw.view("outofcontext", self.cw_rset, w=w)
        title = self._cw._("entities similar to %s") % self.entity.dc_title()
        url = self._cw.build_url("view", rql=self.rql, vtitle=title)
        w(f"<div>[{tags.a(self._cw._('see all'), href=url)}]</div>")


class TagsCloudBox(component.CtxComponent):
    """display a box with tag cloud for"""

    __regid__ = "tagcloud_box"
    __select__ = (
        component.CtxComponent.__select__
        & one_etype_rset()
        & relation_possible("tags", "object", "Tag")
    )

    visible = False  # disabled by default
    order = 30
    title = _("Tag_plural")
    context = "left"

    def init_rendering(self):
        self.etype = next(iter(self.cw_rset.column_types(0)))
        self.cw_rset = self._cw.execute(
            "Any T,COUNT(X),TN GROUPBY T,TN LIMIT 30"
            "WHERE X is %s, T tags X, T name TN" % self.etype
        )
        if not self.cw_rset:
            raise component.EmptyComponent()

    def render_body(self, w):
        self._cw.view("tagcloud", self.cw_rset, etype=self.etype, w=w)
        rql = (
            "Any T,COUNT(X),TN GROUPBY T,TN "
            "WHERE X is %s, T tags X, T name TN" % self.etype
        )
        url = xml_escape(self._cw.build_url(rql=rql, vid="tagcloud", etype=self.etype))
        w(f"<div>[{tags.a(self._cw._('see all tags'), href=url)}]</div>")


# the tags box #################################################################


class TagsBox(component.AjaxEditRelationCtxComponent):
    """the tag box: control tag of taggeable entity providing an easy way to
    add/remove tag
    """

    __regid__ = "tags_box"

    rtype = "tags"
    role = "object"
    target_etype = "Tag"

    order = 20

    added_msg = _("entity has been tagged")
    removed_msg = _("tag has been removed")

    fname_vocabulary = "unrelated_tags"
    fname_validate = "tag_entity"
    fname_remove = "untag_entity"


@ajaxcontroller.ajaxfunc(output_type="json")
def unrelated_tags(self, eid):
    """return tag unrelated to an entity"""
    rql = "Any N ORDERBY N WHERE T is Tag, T name N, NOT T tags X, X eid %(x)s"
    return [name for (name,) in self._cw.execute(rql, {"x": eid})]


@ajaxcontroller.ajaxfunc
def tag_entity(self, eid, taglist):
    execute = self._cw.execute
    # get list of tag for this entity
    tagged_by = {
        tagname
        for (tagname,) in execute(
            "Any N WHERE T name N, T tags X, X eid %(x)s", {"x": eid}
        )
    }
    for tagname in taglist:
        tagname = tagname.strip()
        if not tagname or tagname in tagged_by:
            continue
        tagrset = execute("Tag T WHERE T name %(name)s", {"name": tagname})
        if tagrset:
            rql = "SET T tags X WHERE T eid %(t)s, X eid %(x)s"
            execute(rql, {"t": tagrset[0][0], "x": eid})
        else:
            rql = "INSERT Tag T: T name %(name)s, T tags X WHERE X eid %(x)s"
            execute(rql, {"name": tagname, "x": eid})


@ajaxcontroller.ajaxfunc
def untag_entity(self, eid, tageid):
    rql = "DELETE T tags X WHERE T eid %(t)s, X eid %(x)s"
    self._cw.execute(rql, {"t": tageid, "x": eid})


# the merge tags component #####################################################


class MergeComponent(component.EntityCtxComponent):
    __regid__ = "mergetag"
    __select__ = (
        component.EntityCtxComponent.__select__
        & is_instance("Tag")
        & match_user_groups("managers")
    )
    context = "navcontentbottom"
    title = _("merge tags")

    def render_body(self, w):
        self._cw.add_js(("cubes.tag.merge.js", "cubicweb.widgets.js"))
        entity = self.entity
        w(f'<div id="tagmergeformholder{entity.eid}">')
        w(f"<h5>{self._cw._('Enter a tag name')}</h5>")
        w(f'<input  type="hidden" id="tageid" value="{entity.eid}"/>')
        w(
            '<input id="acmergetag" type="text" class="widget" cubicweb:dataurl="%s" '
            'cubicweb:loadtype="auto" cubicweb:wdgtype="RestrictedSuggestField" />'
            % xml_escape(
                self._cw.build_url("json", fname="unrelated_merge_tags", arg=entity.eid)
            )
        )
        w('<div id="tagged_entities_holder"></div>')
        w('<div id="sgformbuttons" class="hidden">')
        w(
            '<input class="validateButton" type="button" value="%s" onclick="javascript:mergeTags(%s);"/>'
            % (
                self._cw._("merge (keeping %s)") % xml_escape(entity.dc_title()),
                entity.eid,
            )
        )
        w(
            '<input class="validateButton" type="button" value="%s" onclick="javascript:cancelSelectedMergeTag(%s)"/>'
            % (self._cw._(stdmsgs.BUTTON_CANCEL[0]), entity.eid)
        )
        w("</div>")
        w("</div>")


@ajaxcontroller.ajaxfunc(output_type="json")
def unrelated_merge_tags(self, eid):
    """return tag unrelated to an entity"""
    rql = "Any N ORDERBY N WHERE T is Tag, T name N, NOT T eid %(x)s"
    return [name for (name,) in self._cw.execute(rql, {"x": eid})]


@ajaxcontroller.ajaxfunc(output_type="xhtml")
def tagged_entity_html(self, name):
    rset = self._cw.execute(
        "Any X ORDERBY X DESC LIMIT 10 WHERE T tags X, " "T name %(x)s", {"x": name}
    )
    html = []
    if rset:
        html.append('<div id="taggedEntities">')
        # FIXME - add test to go through select_view
        view = self._cw.vreg["views"].select("list", self._cw, rset=rset)
        html.append(view.render(title=self._cw._("linked entities:")))
        html.append("</div>")
        # html.append(self._cw.view('list', rset))
    else:
        html.append(f"<div>{_('no entities related to this tag')}</div>")
        view = self._cw.vreg["views"].select("null", self._cw, rset=rset)
    return " ".join(html)


@ajaxcontroller.ajaxfunc(output_type="xhtml")
def merge_tags(self, eid, mergetag_name):
    mergetag_name = mergetag_name.strip(",")  # XXX
    self._cw.execute(
        "SET T tags X WHERE T1 tags X, NOT T tags X, " "T eid %(x)s, T1 name %(name)s",
        {"x": eid, "name": mergetag_name},
    )
    self._cw.execute("DELETE Tag T WHERE T name %(name)s", {"name": mergetag_name})
    # FIXME - add test to go through select_view
    view = self._cw.vreg["views"].select(
        "primary", self._cw, rset=self._cw.eid_rset(eid)
    )
    return view.render()


# facets, ui adapter ###########################################################


class TagsFacet(facet.RelationFacet):
    __regid__ = "tags-facet"
    rtype = "tags"
    role = "object"
    target_attr = "name"


class TagIFeedAdapter(xmlrss.IFeedAdapter):
    __select__ = is_instance("Tag")

    def rss_feed_url(self):
        rql = (
            "Any X ORDERBY CD DESC LIMIT 15 WHERE "
            "T tags X, T eid %s, X modification_date CD"
        ) % self.entity.eid
        return self._cw.build_url(rql=rql, vid="rss", vtitle=self.entity.dc_title())
