from collections import defaultdict
from docutils.core import publish_parts
from importlib.metadata import distribution
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version
from lxml import html
from pathlib import Path
from plone import api
from plone.memoize.view import memoize
from ploneintranet.layout.interfaces import IDiazoDashboardTemplate
from Products.CMFPlone.utils import get_installer
from Products.Five import BrowserView
from types import ModuleType
from zope.interface import implementer

import json
import ploneintranet
import quaive.resources

try:
    import quaive.app
except ImportError:
    quaive.app = None


@implementer(IDiazoDashboardTemplate)
class About(BrowserView):
    """ """

    def quaive_packages(self):
        packages = []
        if quaive.app is not None:
            supermodules = [quaive.resources, quaive.app]
        else:
            supermodules = [quaive.resources]
        for supermodule in supermodules:
            for member in dir(supermodule):
                if member.startswith("_"):
                    continue
                module = getattr(supermodule, member)
                if isinstance(module, ModuleType):
                    packages.append(module.__name__)
        return packages

    @property
    def packages(self):
        return (
            ["ploneintranet"]
            + self.quaive_packages()
            + ["pas.plugins.ldap", "Products.PloneLDAP"]
        )

    def get_version(self, package):
        try:
            return version(package)
        except PackageNotFoundError:
            return "N/A"

    @property
    @memoize
    def installer(self):
        return get_installer(self.context, self.request)

    def is_installed(self, package):
        return self.installer.is_product_installed(package)

    def versions(self):
        version_items = [
            (package, self.get_version(package)) for package in self.packages
        ]
        versions = [
            "{} {} {}".format(
                name, version, "(installed)" if self.is_installed(name) else ""
            )
            for name, version in version_items
        ]
        controlpanel = api.content.get_view(
            context=self.context, request=self.request, name="overview-controlpanel"
        )
        versions.extend(controlpanel.version_overview())
        return versions

    def integrations(self):
        registry_keys = {
            "onlyoffice API": "quaive.app.onlyoffice.api",
            "mustread DB": (
                "collective.mustread.interfaces.IMustReadSettings.connectionstring"
            ),
            "auditlog DB": (
                "collective.auditlog.interfaces.IAuditLogSettings.connectionstring"
            ),
        }
        integrations = []
        for name, key in registry_keys.items():
            url = api.portal.get_registry_record(key, default="")
            if url:
                integrations.append({"name": name, "url": url})

        return integrations

    @property
    def news_files(self):
        """We return the news files grouped by category to
        replicate what towncrier would do
        """
        # ploneintranet.__spec__.origin is like /path/to/ploneintranet/src/ploneintranet/__init__.py  # noqa: E501
        # We need to go up 3 levels to get to the folder
        # that contains the towncrier fragments
        pi_dist_location = Path(ploneintranet.__spec__.origin).parent.parent.parent

        if pi_dist_location.parent.stem != "src":
            # Assume this is not a source checkout
            # and that the changelog has everything needed
            return {}

        news_files = defaultdict(list)
        mapping = {
            ".added": "Added:",
            ".changed": "Changed:",
            ".fixed": "Technical:",
            ".technical": "Technical:",
            ".updated": "Updated:",
        }
        # for file in sorted(Path(pi_dist_location).glob("../news/[0-9]*.*")):
        news_folder = pi_dist_location / "news"
        for file in sorted(news_folder.glob("[0-9]*.*")):
            suffix = file.suffix
            category = mapping.get(suffix, suffix)
            news_files[category].append(file)
        return news_files

    def changelog(self):
        pi_dist = distribution("ploneintranet")
        metadata = str(pi_dist.metadata)
        # We extract from the metadata the changelog part,
        # which is surrounded by rubbish
        end_of_upper_rubbish = "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html\n        \n        \n        "  # noqa: E501
        start_of_lower_rubbish = "Older changelogs"
        changelog = metadata.partition(end_of_upper_rubbish)[-1].partition(
            start_of_lower_rubbish
        )[0]
        # the changelog is prefix with 8 spaces
        changelog = changelog.replace(" " * 8, "")

        # We compile the rst to html
        html_changelog = publish_parts(
            changelog,
            writer_name="html",
            settings_overrides={
                "report_level": 5,
                "halt_level": 6,
                "initial_header_level": 3,
            },
        )["whole"]

        # Then we extract the inner div from the produced html
        _html = html.fromstring(html_changelog.encode())
        el = _html.getchildren()[-1].getchildren()[0]
        return html.tostring(el)

    @property
    def total_users(self):
        pc = api.portal.get_tool("portal_catalog")
        index = pc._catalog.indexes["portal_type"]
        users = index._index.get("ploneintranet.userprofile.userprofile")
        return len(users)

    @property
    def total_creators(self):
        pc = api.portal.get_tool("portal_catalog")
        index = pc._catalog.indexes["Creator"]
        creators = index._index
        return len(creators)

    @property
    def total_pending_users(self):
        pc = api.portal.get_tool("portal_catalog")
        pending_users = pc.unrestrictedSearchResults(
            portal_type="ploneintranet.userprofile.userprofile", review_state="pending"
        )
        return len(pending_users)

    @property
    def total_enabled_users(self):
        pc = api.portal.get_tool("portal_catalog")
        enabled_users = pc.unrestrictedSearchResults(
            portal_type="ploneintranet.userprofile.userprofile", review_state="enabled"
        )
        return len(enabled_users)

    @property
    def total_disabled_users(self):
        pc = api.portal.get_tool("portal_catalog")
        disabled_users = pc.unrestrictedSearchResults(
            portal_type="ploneintranet.userprofile.userprofile", review_state="disabled"
        )
        return len(disabled_users)


class AboutJSON(About):
    """JSON version of the About view"""

    def __call__(self):
        user_statistics = {
            "total_users": self.total_users,
            "total_creators": self.total_creators,
            "total_pending_users": self.total_pending_users,
            "total_enabled_users": self.total_enabled_users,
            "total_disabled_users": self.total_disabled_users,
        }
        statistics = {
            "users": user_statistics,
        }
        self.request.response.setHeader("Content-Type", "application/json")
        return json.dumps({"statistics": statistics})
