##############################################################################
#
#                        Crossbar.io
#     Copyright (C) typedef int GmbH. All rights reserved.
#
##############################################################################

import os
from copy import deepcopy
from pprint import pformat

from twisted.internet.defer import inlineCallbacks
from twisted.web import server
from twisted.web.resource import Resource
from txaio import make_logger, time_ns

import crossbar
from crossbar._util import hl
from crossbar.webservice.base import RouterWebService

__all__ = ("RouterWebServiceRegisterMe",)


class RegisterMeResource(Resource):
    """
    Web resource to register master nodes with the XBR network.

    IMPORTANT: this resource is only indented to run inside master nodes!
    """

    log = make_logger()

    register_at = "https://planet.xbr.network"

    def __init__(self, templates, worker, node_type=None):
        Resource.__init__(self)
        self._worker = worker
        self._service_realm = "com.crossbario.fabric"
        self._service_agent = self._worker.realm_by_name(self._service_realm).session

        self._page = templates.get_template("registerme.html")
        self._pid = "{}".format(os.getpid())
        self._node_type = node_type.strip().upper()

        assert self._node_type in ["MASTER"]

    def _delayedRender(self, infos, request):
        try:
            peer = request.transport.getPeer()
            peer = "{}:{}".format(peer.host, peer.port)
        except:
            peer = "?:?"

        kwargs = deepcopy(infos)

        node_time = time_ns()
        register_url = "{}/register-node?node_type={}&node_key={}&node_time={}".format(
            self.register_at, self._node_type, kwargs["node_status"]["pubkey"], node_time
        )

        kwargs["node_time"] = node_time
        kwargs["is_registered"] = False
        kwargs["cb_version"] = crossbar.__version__
        kwargs["worker_pid"] = self._pid
        kwargs["peer"] = peer
        kwargs["register_url"] = register_url
        kwargs["node_type"] = self._node_type

        s = self._page.render(**kwargs)
        request.write(s.encode("utf8"))
        request.finish()

    @inlineCallbacks
    def _do_get(self, request):
        try:
            node_status = yield self._worker.call("crossbar.get_status")
            master_status = yield self._service_agent.call("crossbarfabriccenter.domain.get_status")
            master_license = yield self._service_agent.call("crossbarfabriccenter.domain.get_license")
            master_version = yield self._service_agent.call("crossbarfabriccenter.domain.get_version")
            infos = {
                "node_status": node_status,
                "master_status": master_status,
                "master_license": master_license,
                "master_version": master_version,
            }
            self.log.debug(
                "rendering {page} page using data\n{data}", page=hl("register-master", bold=True), data=pformat(infos)
            )
            self._delayedRender(infos, request)
        except:
            self.log.failure()
            request.finish()

    def render_GET(self, request):
        self._do_get(request)
        return server.NOT_DONE_YET

    def getChild(self, path, request):
        self.log.debug(
            "{kass}.getChild(path={path}, request={request}, prepath={prepath}, postpath={postpath})",
            kass=self.__class__.__name__,
            path=path,
            prepath=request.prepath,
            postpath=request.postpath,
            request=request,
        )

        search_path = b"/".join([path] + request.postpath).decode("utf8")

        if search_path == "" or search_path.endswith("/") or search_path in ["register", "register.html"]:
            return self
        else:
            return Resource.getChild(self, path, request)


class RouterWebServiceRegisterMe(RouterWebService):
    """
    Crossbar.io "register-me" home page.

    IMPORTANT: this web service is only indented to run inside master nodes!
    """

    @staticmethod
    def check(personality, config):
        """
        Checks the configuration item. When errors are found, an
        InvalidConfigException exception is raised.

        :param personality: The node personality class.
        :param config: The Web service configuration item.
        :raises: crossbar.common.checkconfig.InvalidConfigException
        """
        pass

    @staticmethod
    def create(transport, path, config):
        personality = transport.worker.personality
        personality.WEB_SERVICE_CHECKERS["registerme"](personality, config)

        resource = RegisterMeResource(transport.templates, transport.worker, node_type=personality.NAME)

        return RouterWebServiceRegisterMe(transport, path, config, resource)
