/*
 * Copyright 2016 Red Hat, Inc. and/or its affiliates.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *    http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.uberfire.ext.security.management.client.widgets.management.editor.user;

import java.util.Set;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;

import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import org.jboss.errai.security.shared.api.Group;
import org.jboss.errai.security.shared.api.Role;
import org.jboss.errai.security.shared.api.identity.User;
import org.jboss.errai.security.shared.api.identity.UserImpl;
import org.uberfire.client.mvp.UberView;
import org.uberfire.ext.security.management.api.Capability;
import org.uberfire.ext.security.management.client.ClientUserSystemManager;
import org.uberfire.ext.security.management.client.widgets.management.editor.AssignedEntitiesEditor;
import org.uberfire.ext.security.management.client.widgets.management.editor.AssignedEntitiesExplorer;
import org.uberfire.ext.security.management.client.widgets.management.editor.acl.ACLViewer;
import org.uberfire.ext.security.management.client.widgets.management.events.ContextualEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnChangePasswordEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnDeleteEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnEditEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnShowEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnUpdateUserGroupsEvent;
import org.uberfire.ext.security.management.client.widgets.management.events.OnUpdateUserRolesEvent;

/**
 * <p>The user editor presenter.</p>
 * <p>User's groups are edited using the UserAssignedGroupsExplorer editor component. So the UserAssignedGroupsEditor works with a dummy user instance.</p>
 * @since 0.8.0
 */
@Dependent
public class UserEditor implements IsWidget,
                                   org.uberfire.ext.security.management.client.editor.user.UserEditor {

    public View view;
    ClientUserSystemManager userSystemManager;
    UserAttributesEditor userAttributesEditor;
    UserAssignedGroupsExplorer userAssignedGroupsExplorer;
    UserAssignedGroupsEditor userAssignedGroupsEditor;
    UserAssignedRolesExplorer userAssignedRolesExplorer;
    UserAssignedRolesEditor userAssignedRolesEditor;
    ACLViewer aclViewer;
    Event<OnEditEvent> onEditEvent;
    Event<OnShowEvent> onShowEvent;
    Event<OnDeleteEvent> onDeleteEvent;
    Event<OnChangePasswordEvent> onChangePasswordEvent;
    User user;
    boolean isEditMode;
    @Inject
    public UserEditor(final ClientUserSystemManager userSystemManager,
                      final UserAttributesEditor userAttributesEditor,
                      final UserAssignedGroupsExplorer userAssignedGroupsExplorer,
                      final UserAssignedGroupsEditor userAssignedGroupsEditor,
                      final UserAssignedRolesExplorer userAssignedRolesExplorer,
                      final UserAssignedRolesEditor userAssignedRolesEditor,
                      final ACLViewer aclViewer,
                      final Event<OnEditEvent> onEditEvent,
                      final Event<OnShowEvent> onShowEvent,
                      final Event<OnDeleteEvent> onDeleteEvent,
                      final Event<OnChangePasswordEvent> onChangePasswordEvent,
                      final View view) {

        this.userSystemManager = userSystemManager;
        this.userAttributesEditor = userAttributesEditor;
        this.userAssignedGroupsExplorer = userAssignedGroupsExplorer;
        this.userAssignedGroupsEditor = userAssignedGroupsEditor;
        this.userAssignedRolesExplorer = userAssignedRolesExplorer;
        this.userAssignedRolesEditor = userAssignedRolesEditor;
        this.aclViewer = aclViewer;
        this.onEditEvent = onEditEvent;
        this.onShowEvent = onShowEvent;
        this.onDeleteEvent = onDeleteEvent;
        this.onChangePasswordEvent = onChangePasswordEvent;
        this.view = view;
    }

    @PostConstruct
    public void init() {
        view.init(this);
        view.initWidgets(userAttributesEditor.view,
                         userAssignedGroupsExplorer.view,
                         userAssignedGroupsEditor.view,
                         userAssignedRolesExplorer.view,
                         userAssignedRolesEditor.view,
                         aclViewer);
    }

    @Override
    public Widget asWidget() {
        return view.asWidget();
    }

    /*  ******************************************************************************************************
                                     PUBLIC PRESENTER API 
         ****************************************************************************************************** */

    @Override
    public String identifier() {
        // Identifier is not editable, no need for any editor.
        return user.getIdentifier();
    }

    @Override
    public UserAttributesEditor attributesEditor() {
        return userAttributesEditor;
    }

    @Override
    public UserAssignedGroupsExplorer groupsExplorer() {
        return userAssignedGroupsExplorer;
    }

    @Override
    public org.uberfire.ext.security.management.client.editor.user.UserAssignedRolesExplorer rolesExplorer() {
        return userAssignedRolesExplorer;
    }

    public UserAssignedGroupsEditor groupsEditor() {
        return userAssignedGroupsEditor;
    }

    public UserAssignedRolesEditor rolesEditor() {
        return userAssignedRolesEditor;
    }

    @Override
    public void show(final User user) {
        clear();
        this.isEditMode = false;
        aclViewer.show(user);
        open(user);
        onShowEvent.fire(new OnShowEvent(UserEditor.this,
                                         user));
    }

    @Override
    public void edit(final User user) {
        clear();
        this.isEditMode = true;
        open(user);
    }

    @Override
    public void flush() {
        assert user != null;
        assert isEditMode;
        // No additional flush logic to perform here.
    }

    @Override
    public User getValue() {
        return user;
    }

    @Override
    public void setViolations(final Set<ConstraintViolation<User>> violations) {
        //  Currently no violations expected.
    }

    public void clear() {
        isEditMode = false;
        user = null;
        userAttributesEditor.clear();
        userAssignedGroupsExplorer.clear();
        userAssignedGroupsEditor.clear();
        userAssignedRolesExplorer.clear();
    }

    public UserEditor setEditButtonVisible(boolean isVisible) {
        view.setEditButtonVisible(isVisible);
        return this;
    }

    public UserEditor setDeleteButtonVisible(boolean isVisible) {
        view.setDeleteButtonVisible(isVisible);
        return this;
    }

    public UserEditor setChangePasswordButtonVisible(boolean isVisible) {
        view.setChangePasswordButtonVisible(isVisible);
        return this;
    }

    public UserEditor setAttributesEditorVisible(boolean isVisible) {
        view.setAttributesEditorVisible(isVisible);
        return this;
    }

    public UserEditor setAddToGroupsButtonVisible(boolean isVisible) {
        view.setAddToGroupsButtonVisible(isVisible);
        return this;
    }

    public UserEditor setPermissionsVisible(boolean isVisible) {
        view.setPermissionsVisible(isVisible);
        return this;
    }

    public ACLViewer getACLViewer() {
        return aclViewer;
    }

    void onEdit() {
        onEditEvent.fire(new OnEditEvent(UserEditor.this,
                                         user));
    }

    /*  ******************************************************************************************************
                                 VIEW CALLBACKS 
     ****************************************************************************************************** */

    void onDelete() {
        onDeleteEvent.fire(new OnDeleteEvent(UserEditor.this,
                                             user));
    }

    void onChangePassword() {
        onChangePasswordEvent.fire(new OnChangePasswordEvent(UserEditor.this,
                                                             user));
    }

    void onAssignGroups() {

        final User dummyUser = new UserImpl(user.getIdentifier(),
                                            userAssignedRolesExplorer.getValue(),
                                            userAssignedGroupsExplorer.getValue(),
                                            user.getProperties());

        if (isEditMode) {
            userAssignedGroupsEditor.edit(dummyUser);
        } else {
            userAssignedGroupsEditor.show(dummyUser);
        }
    }

    void onAssignRoles() {

        final User dummyUser = new UserImpl(user.getIdentifier(),
                                            userAssignedRolesExplorer.getValue(),
                                            userAssignedGroupsExplorer.getValue(),
                                            user.getProperties());

        if (isEditMode) {
            userAssignedRolesEditor.edit(dummyUser);
        } else {
            userAssignedRolesEditor.show(dummyUser);
        }
    }

    protected void open(final User user) {
        assert user != null;
        this.user = user;

        // User identifier.
        final String id = user.getIdentifier();
        view.setUsername(id);

        // Edit mode & Capabilities.
        final boolean canUpdate = canUpdate();
        final boolean canDelete = canDelete();
        final boolean canManageAttributes = canManageAttributes();
        final boolean canChangePwd = canChangePassword();
        final boolean canAssignGroups = canAssignGroups();
        final boolean canAssignRoles = canAssignRoles();
        final boolean hasAttributes = user.getProperties() != null && !user.getProperties().isEmpty();
        final boolean shouldHideAttributesEditor = !canManageAttributes && !hasAttributes;
        view.setEditButtonVisible(!isEditMode && canUpdate);
        view.setDeleteButtonVisible(isEditMode && canDelete);
        view.setChangePasswordButtonVisible(isEditMode && canChangePwd);
        view.setAddToGroupsButtonVisible(isEditMode && canAssignGroups);
        view.setAddToRolesButtonVisible(isEditMode && canAssignRoles);
        view.setAttributesEditorVisible(!shouldHideAttributesEditor);
    }
    
     /*  ******************************************************************************************************
                                 PRIVATE METHODS AND VALIDATORS
     ****************************************************************************************************** */

    boolean canUpdate() {
        final boolean canUpdate = userSystemManager.isUserCapabilityEnabled(Capability.CAN_UPDATE_USER);
        return canUpdate;
    }

    boolean canDelete() {
        final boolean canDelete = userSystemManager.isUserCapabilityEnabled(Capability.CAN_DELETE_USER);
        return canDelete;
    }

    public boolean canAssignGroups() {
        final boolean canAssignGroups = userSystemManager.isUserCapabilityEnabled(Capability.CAN_ASSIGN_GROUPS);
        return canAssignGroups;
    }

    public boolean canAssignRoles() {
        final boolean canAssignRoles = userSystemManager.isUserCapabilityEnabled(Capability.CAN_ASSIGN_ROLES);
        return canAssignRoles;
    }

    boolean canChangePassword() {
        final boolean canChangePassword = userSystemManager.isUserCapabilityEnabled(Capability.CAN_CHANGE_PASSWORD);
        return canChangePassword;
    }

    boolean canManageAttributes() {
        final boolean canManageAttributes = userSystemManager.isUserCapabilityEnabled(Capability.CAN_MANAGE_ATTRIBUTES);
        return canManageAttributes;
    }

    void onOnUserGroupsUpdatedEvent(@Observes final OnUpdateUserGroupsEvent onUpdateUserGroupsEvent) {
        if (checkEventContext(onUpdateUserGroupsEvent,
                              userAssignedGroupsEditor)) {
            userAssignedGroupsEditor.flush();
            final Set<Group> groups = userAssignedGroupsEditor.getValue();
            userAssignedGroupsExplorer.getValue().clear();
            userAssignedGroupsExplorer.getValue().addAll(groups);
            userAssignedGroupsExplorer.doShow();
        }
    }

    void onOnUserRolesUpdatedEvent(@Observes final OnUpdateUserRolesEvent onUpdateUserRolesEvent) {
        if (checkEventContext(onUpdateUserRolesEvent,
                              userAssignedRolesEditor)) {
            userAssignedRolesEditor.flush();
            final Set<Role> roles = userAssignedRolesEditor.getValue();
            userAssignedRolesExplorer.getValue().clear();
            userAssignedRolesExplorer.getValue().addAll(roles);
            userAssignedRolesExplorer.doShow();
        }
    }

    private boolean checkEventContext(final ContextualEvent contextualEvent,
                                      final Object context) {
        return contextualEvent != null && contextualEvent.getContext() != null && contextualEvent.getContext().equals(context);
    }

    public interface View extends UberView<UserEditor> {

        View initWidgets(UserAttributesEditor.View userAttributesEditorView,
                         AssignedEntitiesExplorer userAssignedGroupsExplorerView,
                         AssignedEntitiesEditor userAssignedGroupsEditorView,
                         AssignedEntitiesExplorer userAssignedRolesExplorerView,
                         AssignedEntitiesEditor userAssignedRolesEditorView,
                         ACLViewer aclViewer);

        View setUsername(String username);

        View setEditButtonVisible(boolean isVisible);

        View setDeleteButtonVisible(boolean isVisible);

        View setChangePasswordButtonVisible(boolean isVisible);

        View setAddToGroupsButtonVisible(boolean isVisible);

        View setAddToRolesButtonVisible(boolean isVisible);

        View setAttributesEditorVisible(boolean isVisible);

        View setPermissionsVisible(boolean isVisible);
    }
}
