import logging

import flask
from werkzeug.local import LocalProxy

from pillar.api.utils import authorization
from pillar.extension import PillarExtension

EXTENSION_NAME = 'cloud'
ROLES_TO_BE_SUBSCRIBER = {'demo', 'subscriber', 'admin'}  # TODO: get rid of this, use 'subscriber' cap


class CloudExtension(PillarExtension):
    has_context_processor = True

    def __init__(self):
        self._log = logging.getLogger('%s.CloudExtension' % __name__)

    @property
    def name(self):
        return EXTENSION_NAME

    def flask_config(self):
        """Returns extension-specific defaults for the Flask configuration.

        Use this to set sensible default values for configuration settings
        introduced by the extension.

        :rtype: dict
        """

        # Just so that it registers the management commands.
        from . import cli

        return {}

    def eve_settings(self):
        """Returns extensions to the Eve settings.

        Currently only the DOMAIN key is used to insert new resources into
        Eve's configuration.

        :rtype: dict
        """

        return {}

    def blueprints(self):
        """Returns the list of top-level blueprints for the extension.

        These blueprints will be mounted at the url prefix given to
        app.load_extension().

        :rtype: list of flask.Blueprint objects.
        """
        from . import routes
        import cloud.stats.routes
        return [
            routes.blueprint,
            cloud.stats.routes.blueprint,
        ]

    @property
    def template_path(self):
        import os.path
        return os.path.join(os.path.dirname(__file__), 'templates')

    @property
    def static_path(self):
        import os.path
        return os.path.join(os.path.dirname(__file__), 'static')

    def context_processor(self):
        return {
            'current_user_is_subscriber': authorization.user_has_cap('subscriber')
        }

    def setup_app(self, app):
        """Links certain roles to the subscriber role.

        This means that users who get the subscriber role also get this linked
        role, and when the subscriber role is revoked, the linked role is also
        revoked.
        """

        from pillar.api.service import signal_user_changed_role

        signal_user_changed_role.connect(self._user_changed_role)

        from . import routes
        routes.setup_app(app)

    def _user_changed_role(self, sender, user: dict):
        from pillar.api import service

        linked_roles = {'flamenco-user', 'attract-user'}
        link_to = {'subscriber', 'demo'}
        user_roles = set(user.get('roles', []))

        # Determine what to do
        has_linked_roles = not (linked_roles - user_roles)
        has_link_to = bool(link_to.intersection(user_roles))
        action = ''
        if has_link_to and not has_linked_roles:
            self._log.info('Granting roles %s to user %s', linked_roles, user['_id'])
            action = 'grant'
        elif not has_link_to and has_linked_roles:
            self._log.info('Revoking roles %s from user %s', linked_roles, user['_id'])
            action = 'revoke'

        if not action:
            return

        # Avoid infinite loops while we're changing the user's roles.
        service.signal_user_changed_role.disconnect(self._user_changed_role)
        try:
            if linked_roles:
                service.do_badger(action, roles=linked_roles, user_id=user['_id'])
        finally:
            service.signal_user_changed_role.connect(self._user_changed_role)


def _get_current_cloud():
    """Returns the Cloud extension of the current application."""
    return flask.current_app.pillar_extensions[EXTENSION_NAME]


current_cloud = LocalProxy(_get_current_cloud)
"""Cloud extension of the current app."""
