# copyright 2003-2024 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact https://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <https://www.gnu.org/licenses/>.
"""management and error screens"""

from time import strftime, localtime

from logilab.mtconverter import xml_escape

from cubicweb import _
from cubicweb.predicates import none_rset, match_user_groups
from cubicweb_web.view import StartupView
from cubicweb_web.views import actions, tabs


def dict_to_html(w, dict):
    # XHTML doesn't allow emtpy <ul> nodes
    if dict:
        w("<ul>")
        for key in sorted(dict):
            w("<li><span>%s</span>: <span>%s</span></li>", str(key), repr(dict[key]))
        w("</ul>")


class SiteInfoAction(actions.ManagersAction):
    __regid__ = "siteinfo"
    __select__ = match_user_groups("users", "managers")
    title = _("Site information")
    category = "manage"
    order = 1000


class SiteInfoView(tabs.TabsMixin, StartupView):
    __regid__ = "siteinfo"
    title = _("Site information")
    tabs = [_("info"), _("registry"), _("gc")]
    default_tab = "info"

    def call(self, **kwargs):
        """The default view representing the instance's management"""
        self.w("<h1>%s</h1>", self._cw._(self.title))
        self.render_tabs(self.tabs, self.default_tab)


class ProcessInformationView(StartupView):
    """display various web server /repository information"""

    __regid__ = "info"
    __select__ = none_rset() & match_user_groups("managers", "users")

    title = _("server information")
    cache_max_age = 0

    def call(self, **kwargs):
        req = self._cw
        dtformat = req.property_value("ui.datetime-format")
        _ = req._
        repo = req.cnx.repo
        # generic instance information
        self.w("<h2>%s</h2>", _("Instance"))
        pyvalue = (
            (_("config type"), self._cw.vreg.config.name),
            (_("config mode"), self._cw.vreg.config.mode),
            (_("instance home"), self._cw.vreg.config.apphome),
        )
        self.wview("pyvaltable", pyvalue=pyvalue, header_column_idx=0)
        vcconf = repo.get_versions()
        self.w("<h3>%s</h3>", _("versions configuration"))
        missing = _("no version information")
        pyvalue = [("CubicWeb", vcconf.get("cubicweb", missing))]
        pyvalue += [
            (cube, vcconf.get(cube, missing))
            for cube in sorted(self._cw.vreg.config.cubes())
        ]
        self.wview("pyvaltable", pyvalue=pyvalue, header_column_idx=0)
        # repository information
        self.w("<h2>%s</h2>", _("Repository"))
        self.w("<h3>%s</h3>", _("resources usage"))
        stats = self._cw.call_service("repo_stats")
        stats["threads"] = ", ".join(sorted(stats["threads"]))
        for k in stats:
            if k == "type_cache_size":
                continue
            if k.endswith("_cache_size"):
                stats[k] = "{} / {}".format(stats[k]["size"], stats[k]["maxsize"])

        def format_stat(sname, sval):
            return "{} {}".format(
                xml_escape(str(sval)),
                sname.endswith("percent") and "%" or "",
            )

        pyvalue = [
            (sname, format_stat(sname, sval)) for sname, sval in sorted(stats.items())
        ]
        self.wview("pyvaltable", pyvalue=pyvalue, header_column_idx=0)
        # web server information
        self.w("<h2>%s</h2>", _("Web server"))
        pyvalue = (
            (_("base url"), req.base_url()),
            (_("data directory url"), req.datadir_url),
        )
        self.wview("pyvaltable", pyvalue=pyvalue, header_column_idx=0)
        from cubicweb_web.application import SESSION_MANAGER

        if SESSION_MANAGER is not None and req.user.is_in_group("managers"):
            sessions = SESSION_MANAGER.current_sessions()
            self.w("<h3>%s</h3>", _("opened web sessions"))
            if sessions:
                self.w("<ul>")
                for session in sessions:
                    last_usage_time = session.mtime
                    self.w(
                        "<li>%s (%s: %s)<br/>",
                        session.sessionid,
                        _("last usage"),
                        strftime(dtformat, localtime(last_usage_time)),
                    )
                    dict_to_html(self.w, session.data)
                    self.w("</li>")
                self.w("</ul>")
            else:
                self.w("<p>%s</p>", _("no web sessions found"))


class RegistryView(StartupView):
    """display vregistry content"""

    __regid__ = "registry"
    __select__ = StartupView.__select__ & match_user_groups("managers")
    title = _("registry")
    cache_max_age = 0

    def call(self, **kwargs):
        self.w("<h2>%s</h2>", self._cw._("Registry's content"))
        keys = sorted(self._cw.vreg)
        self.w(
            "<p>%s</p>\n",
            " - ".join(f'<a href="#{key}">{key}</a>' for key in keys),
            escape=False,
        )
        for key in keys:
            if key in ("boxes", "contentnavigation"):  # those are bw compat registries
                continue
            self.w('<h3 id="%s">%s</h3>', key, key)
            if self._cw.vreg[key]:
                values = sorted(self._cw.vreg[key].items())
                self.wview(
                    "pyvaltable",
                    pyvalue=[(key, xml_escape(repr(val))) for key, val in values],
                )
            else:
                self.w("<p>Empty</p>\n")


class GCView(StartupView):
    """display garbage collector information"""

    __regid__ = "gc"
    __select__ = StartupView.__select__ & match_user_groups("managers")
    title = _("memory leak debugging")
    cache_max_age = 0

    def call(self, **kwargs):
        stats = self._cw.call_service("repo_gc_stats")
        self.w("<h2>%s</h2>", _("Garbage collection information"))
        self.w("<h3>%s</h3>", self._cw._("Looked up classes"))
        self.wview("pyvaltable", pyvalue=stats["lookupclasses"])
        self.w("<h3>%s</h3>", self._cw._("Most referenced classes"))
        self.wview("pyvaltable", pyvalue=stats["referenced"])
        if stats["unreachable"]:
            self.w("<h3>%s</h3>", self._cw._("Unreachable objects"))
            values = [xml_escape(val) for val in stats["unreachable"]]
            self.wview("pyvallist", pyvalue=values)
