package com.worth.ifs.user.service;

import com.worth.ifs.BaseServiceSecurityTest;
import com.worth.ifs.commons.service.ServiceResult;
import com.worth.ifs.token.domain.Token;
import com.worth.ifs.token.security.TokenLookupStrategies;
import com.worth.ifs.token.security.TokenPermissionRules;
import com.worth.ifs.user.resource.UserResource;
import com.worth.ifs.user.resource.UserRoleType;
import com.worth.ifs.user.security.UserPermissionRules;
import com.worth.ifs.user.transactional.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.access.method.P;

import java.util.List;
import java.util.Set;

import static com.worth.ifs.commons.service.ServiceResult.serviceSuccess;
import static com.worth.ifs.user.builder.UserResourceBuilder.newUserResource;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;

/**
 * Testing how the secured methods in UserService interact with Spring Security
 */
public class UserServiceSecurityTest extends BaseServiceSecurityTest<UserService> {

    private UserPermissionRules userRules;
    private TokenPermissionRules tokenRules;
    private TokenLookupStrategies tokenLookupStrategies;

    @Before
    public void lookupPermissionRules() {
        userRules = getMockPermissionRulesBean(UserPermissionRules.class);
        tokenRules = getMockPermissionRulesBean(TokenPermissionRules.class);
        tokenLookupStrategies = getMockPermissionEntityLookupStrategiesBean(TokenLookupStrategies.class);
    }

    @Test
    public void testFindAll() {
        service.findAll();
        assertViewMultipleUsersExpectations();
    }

    @Test
    public void testFindAssignableUsers() {
        service.findAssignableUsers(123L);
        assertViewMultipleUsersExpectations();
    }

    @Test
    public void testFindByEmail() {
        assertAccessDenied(() -> service.findByEmail("asdf@example.com"), () -> {
            assertViewSingleUserExpectations();
        });
    }

    @Test
    public void testGetUserById() {
        assertAccessDenied(() -> service.getUserById(123L), () -> {
            assertViewSingleUserExpectations();
        });
    }

    @Test
    public void testGetUserByUid() {

        // this method must remain unsecured because it is the way in which we get a user onto the
        // SecurityContext in the first place for permission checking
        service.getUserResourceByUid("asdf");
        verifyNoMoreInteractionsWithRules();
    }

    @Test
    public void testChangePassword() {

        Token token = new Token();
        when(tokenLookupStrategies.getTokenByHash("hash")).thenReturn(token);

        assertAccessDenied(() -> service.changePassword("hash", "newpassword"), () -> {
            verify(tokenRules).systemRegistrationUserCanUseTokensToResetPaswords(token, getLoggedInUser());
            verifyNoMoreInteractionsWithRules();
        });
    }

    @Test
    public void testSendPasswordResetNotification() {

        UserResource user = newUserResource().build();
        assertAccessDenied(() -> service.sendPasswordResetNotification(user), () -> {
            verify(userRules).usersCanChangeTheirOwnPassword(user, getLoggedInUser());
            verify(userRules).systemRegistrationUserCanChangePasswordsForUsers(user, getLoggedInUser());
            verifyNoMoreInteractionsWithRules();
        });
    }

    @Test
    public void testFindRelatedUsers() {
        service.findRelatedUsers(123L);
        assertViewMultipleUsersExpectations();
    }

    private void assertViewSingleUserExpectations() {
        assertViewXUsersExpectations(1);
    }

    private void assertViewMultipleUsersExpectations() {
        assertViewXUsersExpectations(2);
    }

    private void assertViewXUsersExpectations(int numberOfUsers) {
        verify(userRules, times(numberOfUsers)).anyUserCanViewThemselves(isA(UserResource.class), eq(getLoggedInUser()));
        verify(userRules, times(numberOfUsers)).assessorsCanViewConsortiumUsersOnApplicationsTheyAreAssessing(isA(UserResource.class), eq(getLoggedInUser()));
        verify(userRules, times(numberOfUsers)).compAdminsCanViewEveryone(isA(UserResource.class), eq(getLoggedInUser()));
        verify(userRules, times(numberOfUsers)).consortiumMembersCanViewOtherConsortiumMembers(isA(UserResource.class), eq(getLoggedInUser()));
        verify(userRules, times(numberOfUsers)).systemRegistrationUserCanViewEveryone(isA(UserResource.class), eq(getLoggedInUser()));
        verify(userRules, times(numberOfUsers)).projectFinanceUsersCanViewEveryone(isA(UserResource.class), eq(getLoggedInUser()));
        verifyNoMoreInteractionsWithRules();
    }

    private void verifyNoMoreInteractionsWithRules() {
        verifyNoMoreInteractions(tokenRules);
        verifyNoMoreInteractions(userRules);
    }

    @Override
    protected Class<? extends UserService> getServiceClass() {
        return TestUserService.class;
    }

    /**
     * Test class for use in Service Security tests.
     */
    public static class TestUserService implements UserService {

        @Override
        public ServiceResult<UserResource> getUserResourceByUid(String uid) {
            return serviceSuccess(newUserResource().build());
        }

        @Override
        public ServiceResult<UserResource> getUserById(Long id) {
            return serviceSuccess(newUserResource().build());
        }

        @Override
        public ServiceResult<List<UserResource>> findAll() {
            return serviceSuccess(newUserResource().build(2));
        }

        @Override
        public ServiceResult<List<UserResource>> findByProcessRole(UserRoleType roleType) {
            return serviceSuccess(newUserResource().build(2));
        }

        @Override
        public ServiceResult<UserResource> findByEmail(String email) {
            return serviceSuccess(newUserResource().build());
        }

        @Override
        public ServiceResult<UserResource> findInactiveByEmail(String email) {
            return serviceSuccess(newUserResource().build());
        }

        @Override
        public ServiceResult<Set<UserResource>> findAssignableUsers(Long applicationId) {
            return serviceSuccess(newUserResource().buildSet(2));
        }

        @Override
        public ServiceResult<Set<UserResource>> findRelatedUsers(Long applicationId) {
            return serviceSuccess(newUserResource().buildSet(2));
        }

        @Override
        public ServiceResult<Void> sendPasswordResetNotification(@P("user") UserResource user) {
            return null;
        }

        @Override
        public ServiceResult<Void> changePassword(@P("hash") String hash, String password) {
            return null;
        }
    }
}

@@ -4513,140 +4513,8 @@
 ));%0A
-        verify(userRules, times(numberOfUsers)).projectFinanceUsersCanViewEveryone(isA(UserResource.class), eq(getLoggedInUser()));%0A
     

remove exp not needed until merge