# w.c.s. - web application for online forms
# Copyright (C) 2005-2012  Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

import sys

from django.utils.six.moves.urllib import parse as urllib
from django.utils.six.moves.urllib import parse as urlparse

from quixote import get_request, get_publisher, get_response

from ..qommon import _
from ..qommon.form import *
from wcs.workflows import WorkflowStatusItem, register_item_class
from wcs.roles import get_user_roles, Role
from ..qommon.ident.idp import is_idp_managing_user_attributes
from ..qommon.misc import http_post_request, http_delete_request
from ..qommon.publisher import get_cfg, get_logger
from wcs.api_utils import sign_url, get_secret_and_orig, MissingSecret


def roles_ws_url(role_uuid, user_uuid):
    idps = get_cfg('idp', {})
    entity_id = list(idps.values())[0]['metadata_url']
    base_url = entity_id.split('idp/saml2/metadata')[0]
    url = urlparse.urljoin(base_url, '/api/roles/%s/members/%s/' % (urllib.quote(role_uuid),
                                                                     urllib.quote(user_uuid)))
    return url


def sign_ws_url(url):
    secret, orig = get_secret_and_orig(url)
    url += '?orig=%s' % orig
    return sign_url(url, secret)


class AddRoleWorkflowStatusItem(WorkflowStatusItem):
    description = N_('Role Addition')
    key = 'add_role'
    category = 'user-action'

    role_id = None

    def get_parameters(self):
        return ('role_id', 'condition')

    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
        super(AddRoleWorkflowStatusItem, self).add_parameters_widgets(
                form, parameters, prefix=prefix, formdef=formdef)
        if 'role_id' in parameters:
            form.add(SingleSelectWidgetWithOther, '%srole_id' % prefix,
                     title=_('Role to Add'), value=str(self.role_id) if self.role_id else None,
                     options=[(None, '----', None)] + get_user_roles())

    def role_id_export_to_xml(self, item, charset, include_id=False):
        self._role_export_to_xml('role_id', item, charset,
                include_id=include_id)

    def role_id_init_with_xml(self, elem, charset, include_id=False):
        self._role_init_with_xml('role_id', elem, charset,
                include_id=include_id)

    def perform(self, formdata):
        if not self.role_id:
            return
        role_id = self.get_computed_role_id(self.role_id)
        if not role_id:
            return
        if not formdata.user_id:
            # we can't work on anonymous forms
            return
        user = get_publisher().user_class.get(formdata.user_id)
        self.perform_local(user, formdata, role_id)
        if user.name_identifiers and is_idp_managing_user_attributes():
            self.perform_idp(user, formdata, role_id)

    def perform_local(self, user, formdata, role_id):
        if not user.roles:
            user.roles = []
        if not role_id in user.roles:
            user.roles.append(role_id)
        user.store()
        request = get_request()
        if request and request.user and request.user.id == user.id:
            # if we changed the currently logged in user, we update it with the
            # changes.
            request.user = user

    def perform_idp(self, user, formdata, role_id):
        role = Role.get(role_id)
        role_uuid = role.uuid or role.slug
        user_uuid = user.name_identifiers[0]
        try:
            url = roles_ws_url(role_uuid, user_uuid)
        except MissingSecret:
            get_publisher().notify_of_exception(sys.exc_info(), context='[ROLES]')
            return
        def after_job(job=None):
            signed_url = sign_ws_url(url)
            response, status, data, auth_header = http_post_request(signed_url)
            if status != 201:
                get_logger().error('failed to add role %r to user %r',
                                   role, user)
        if get_request():
            get_response().add_after_job(
                str(N_('Adding role')),
                after_job)
        else:
            after_job()


register_item_class(AddRoleWorkflowStatusItem)


class RemoveRoleWorkflowStatusItem(WorkflowStatusItem):
    description = N_('Role Removal')
    key = 'remove_role'
    category = 'user-action'

    role_id = None

    def get_parameters(self):
        return ('role_id', 'condition')

    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
        super(RemoveRoleWorkflowStatusItem, self).add_parameters_widgets(
                form, parameters, prefix=prefix, formdef=formdef)
        if 'role_id' in parameters:
            form.add(SingleSelectWidgetWithOther, '%srole_id' % prefix,
                     title=_('Role to Remove'), value=str(self.role_id) if self.role_id else None,
                     options=[(None, '----', None)] + get_user_roles())

    def perform(self, formdata):
        if not self.role_id:
            return
        role_id = self.get_computed_role_id(self.role_id)
        if not role_id:
            return
        if not formdata.user_id:
            # we can't work on anonymous forms
            return
        user = get_publisher().user_class.get(formdata.user_id)
        self.perform_local(user, formdata, role_id)
        if user.name_identifiers and is_idp_managing_user_attributes():
            self.perform_idp(user, formdata, role_id)

    def perform_local(self, user, formdata, role_id):
        if user.roles and role_id in user.roles:
            user.roles.remove(role_id)
            user.store()
            request = get_request()
            if request and request.user and request.user.id == user.id:
                # if we changed the currently logged in user, we update it
                # with the changes.
                request.user = user

    def perform_idp(self, user, formdata, role_id):
        role = Role.get(role_id)
        role_uuid = role.uuid or role.slug
        user_uuid = user.name_identifiers[0]
        try:
            url = roles_ws_url(role_uuid, user_uuid)
        except MissingSecret:
            get_publisher().notify_of_exception(sys.exc_info(), context='[ROLES]')
            return
        def after_job(job=None):
            signed_url = sign_ws_url(url)
            response, status, data, auth_header = http_delete_request(signed_url)
            if status != 200:
                get_logger().error('failed to remove role %r from user %r',
                                   role, user)
        if get_request():
            get_response().add_after_job(
                str(N_('Removing role')),
                after_job)
        else:
            after_job()

register_item_class(RemoveRoleWorkflowStatusItem)
