from collections import defaultdict
from copy import copy
from onegov.core.crypto import random_password
from onegov.core.directives import query_form_class
from onegov.core.security import Secret
from onegov.core.templates import render_template
from onegov.form import merge_forms
from onegov.org import _, OrgApp
from onegov.org.forms import ManageUserForm, NewUserForm
from onegov.org.layout import DefaultMailLayout
from onegov.org.layout import UserLayout
from onegov.org.layout import UserManagementLayout
from onegov.org.new_elements import Link, LinkGroup
from onegov.ticket import TicketCollection, Ticket
from onegov.user import Auth, User, UserCollection
from onegov.user.errors import ExistingUserError
from onegov.user.forms import SignupLinkForm
from wtforms.validators import Optional


@OrgApp.html(model=UserCollection, template='usermanagement.pt',
             permission=Secret)
def view_usermanagement(self, request):
    """ Allows the management of organisation users. """

    layout = UserManagementLayout(self, request)

    users = defaultdict(list)

    for user in self.query().order_by(User.username).all():
        users[user.role].append(user)

    filters = {}

    filters['role'] = [
        Link(
            text=request.translate(title),
            active=value in self.filters.get('role', tuple()),
            url=request.link(self.for_filter(role=value))
        ) for title, value in (
            (_("Administrator"), 'admin'),
            (_("Editor"), 'editor'),
            (_("Member"), 'member'),
        )
    ]

    filters['active'] = [
        Link(
            text=request.translate(title),
            active=value in self.filters.get('active', tuple()),
            url=request.link(self.for_filter(active=value))
        ) for title, value in (
            (_("Active"), True),
            (_("Inactive"), False)
        )
    ]

    filters['tag'] = [
        Link(
            text=tag,
            active=tag in self.filters.get('tag', tuple()),
            url=request.link(self.for_filter(tag=tag))
        ) for tag in self.tags
    ]

    return {
        'layout': layout,
        'title': _("User Management"),
        'users': users,
        'filters': filters
    }


@OrgApp.form(
    model=UserCollection,
    template='signup_link.pt',
    permission=Secret,
    form=SignupLinkForm,
    name='signup-link')
def handle_create_signup_link(self, request, form):
    link = None

    if form.submitted(request):
        auth = Auth.from_app(request.app)
        auth.signup_token = form.signup_token(auth)

        link = request.link(auth, 'register')

    layout = UserManagementLayout(self, request)
    layout.breadcrumbs.append(Link(_("New Signup Link"), '#'))
    layout.editbar_links = None

    return {
        'layout': layout,
        'title': _("New Signup Link"),
        'link': link,
        'form': form
    }


@OrgApp.html(model=User, template='user.pt', permission=Secret)
def view_user(self, request):
    """ Shows all objects owned by the given user. """

    layout = UserLayout(self, request)

    linkgroups = [
        fn(request, self) for fn in request.app.config.linkgroup_registry
    ]
    linkgroups.sort(key=lambda group: request.translate(group.title))

    return {
        'layout': layout,
        'title': self.title,
        'linkgroups': linkgroups
    }


@OrgApp.userlinks()
def ticket_links(request, user):
    tickets = TicketCollection(request.session).query()
    tickets = tickets.filter_by(user_id=user.id)
    tickets = tickets.order_by(Ticket.number)
    tickets = tickets.with_entities(
        Ticket.id, Ticket.number, Ticket.handler_code)

    return LinkGroup(
        title=_("Tickets"),
        links=[
            Link(
                ticket.number,
                request.class_link(Ticket, {
                    'handler_code': ticket.handler_code,
                    'id': ticket.id
                }),
            )
            for ticket in tickets
        ]
    )


def get_manage_user_form(self, request):
    userprofile_form = query_form_class(request, self, name='userprofile')
    assert userprofile_form

    class OptionalUserprofile(userprofile_form):

        hooked = False

        def submitted(self, request):
            # fields only present on the userprofile_form are made optional
            # to make sure that we can always change the active/inactive state
            # of the user and the role the user has
            if not self.hooked:
                for name, field in self._fields.items():
                    if not hasattr(userprofile_form, name):
                        continue

                    if not field.validators:
                        continue

                    # be careful not to change the class itself
                    field.validators = copy(field.validators)
                    field.validators.insert(0, Optional())

                self.hooked = True

            return super().submitted(request)

    return merge_forms(ManageUserForm, OptionalUserprofile)


@OrgApp.form(model=User, template='form.pt', form=get_manage_user_form,
             permission=Secret, name='edit')
def handle_manage_user(self, request, form):

    # XXX the manage user form doesn't have access to the username
    # because it can't be edited, so we need to inject it here
    # for validation purposes (check for a unique yubikey)
    form.current_username = self.username

    if not request.app.enable_yubikey:
        form.delete_field('yubikey')

    if form.submitted(request):
        form.populate_obj(self)
        request.success(_("Your changes were saved"))

        return request.redirect(request.class_link(UserCollection))

    elif not request.POST:
        form.process(obj=self)

    layout = UserManagementLayout(self, request)
    layout.breadcrumbs.append(Link(self.username, '#'))

    return {
        'layout': layout,
        'title': self.username,
        'form': form
    }


@OrgApp.form(model=UserCollection, template='newuser.pt', form=NewUserForm,
             name='new', permission=Secret)
def handle_new_user(self, request, form):

    if not request.app.enable_yubikey:
        form.delete_field('yubikey')

    layout = UserManagementLayout(self, request)
    layout.breadcrumbs.append(Link(_("New User"), '#'))
    layout.editbar_links = None

    if form.submitted(request):
        password = random_password()

        if form.data.get('yubikey'):
            second_factor = {
                'type': 'yubikey',
                'data': form.data['yubikey'][:12]
            }
        else:
            second_factor = None

        try:
            user = self.add(
                username=form.username.data,
                password=password,
                role=form.role.data,
                active=form.active,
                second_factor=second_factor
            )
        except ExistingUserError:
            form.username.errors.append(
                _("A user with this e-mail address already exists"))
        else:
            if form.send_activation_email.data:
                subject = request.translate(
                    _("An account was created for you")
                )

                content = render_template('mail_new_user.pt', request, {
                    'user': user,
                    'org': request.app.org,
                    'layout': DefaultMailLayout(user, request),
                    'title': subject
                })

                request.app.send_transactional_email(
                    subject=subject,
                    receivers=(user.username, ),
                    content=content,
                )

            request.info(_("The user was created successfully"))

            return {
                'layout': layout,
                'title': _("New User"),
                'username': form.username.data,
                'password': password,
                'sent_email': form.send_activation_email.data
            }

    return {
        'layout': layout,
        'title': _("New User"),
        'form': form,
        'password': None,
        'sent_email': False
    }
