package com.wishurgift.entry.controller;

import com.wishurgift.entry.validator.TagValidator;
import com.wishurgift.helper.ControllerHelper;
import com.wishurgift.helper.UserMaker;
import com.wishurgift.service.ContactService;
import com.wishurgift.service.UserService;
import com.wishurgift.service.dto.ErrorDTO;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.request.TagRequestDTO;
import com.wishurgift.utils.Constant;
import com.wishurgift.wrapper.Page;
import com.wishurgift.wrapper.Result;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class ContactControllerTest {

    @Mock
    private ContactService contactService;

    @Mock
    private UserService userService;

    @Mock
    private TagValidator tagValidator;

    @InjectMocks
    private ContactController controller;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void listTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        Page<UserDTO> expected = makeListUserDTO();

        Mockito.when(contactService.getContact(user)).thenReturn(expected);

        Response response = controller.list(securityContext);
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals(expected.getSize(), actual.getSize());
        assertEquals(expected.getPage(), actual.getPage());
        assertEquals(expected.getTotal(), actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void userNotFoundListTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(contactService.getContact(user)).thenReturn(null);

        Response response = controller.list(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserListTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Response response = controller.list(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void pageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;
        long total = 8;
        Page<UserDTO> expected = makePageUserDTO(page, elt, total);

        Mockito.when(contactService.getContact(page, elt, user)).thenReturn(expected);

        Response response = controller.page(page, elt, securityContext);
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals((Integer) elt, actual.getSize());
        assertEquals((Integer) page, actual.getPage());
        assertEquals(total, actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void invalidPageTest() {
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 0;
        int elt = 3;

        Response response = controller.page(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.PAGE_NUMBER_INVALID));
    }

    @Test
    public void userNotFoundPageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;

        Mockito.when(contactService.getContact(page, elt, user)).thenReturn(null);

        Response response = controller.page(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserPageTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();
        int page = 1;
        int elt = 3;

        Response response = controller.page(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void requestListTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        Page<UserDTO> expected = makeListUserDTO();

        Mockito.when(contactService.getRequest(user)).thenReturn(expected);

        Response response = controller.requestList(securityContext);
        Result<Page<UserDTO>> result = (Result<Page<UserDTO>>) response.getEntity();
        assertTrue(result.isSuccess());
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals(expected.getSize(), actual.getSize());
        assertEquals(expected.getPage(), actual.getPage());
        assertEquals(expected.getTotal(), actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void userNotFoundRequestListTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(contactService.getRequest(user)).thenReturn(null);

        Response response = controller.requestList(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserRequestListTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Response response = controller.requestList(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void requestPageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;
        long total = 8;
        Page<UserDTO> expected = makePageUserDTO(page, elt, total);

        Mockito.when(contactService.getRequest(page, elt, user)).thenReturn(expected);

        Response response = controller.requestPage(page, elt, securityContext);
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals((Integer) elt, actual.getSize());
        assertEquals((Integer) page, actual.getPage());
        assertEquals(total, actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void invalidRequestPageTest() {
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 0;
        int elt = 3;

        Response response = controller.requestPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.PAGE_NUMBER_INVALID));
    }

    @Test
    public void userNotFoundRequestPageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;

        Mockito.when(contactService.getRequest(page, elt, user)).thenReturn(null);

        Response response = controller.requestPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserRequestPageTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();
        int page = 1;
        int elt = 3;

        Response response = controller.requestPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void pendingListTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        Page<UserDTO> expected = makeListUserDTO();

        Mockito.when(contactService.getPending(user)).thenReturn(expected);

        Response response = controller.pendingList(securityContext);
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals(expected.getSize(), actual.getSize());
        assertEquals(expected.getPage(), actual.getPage());
        assertEquals(expected.getTotal(), actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void userNotFoundPendingListTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(contactService.getPending(user)).thenReturn(null);

        Response response = controller.pendingList(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserPendingListTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Response response = controller.pendingList(securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void pendingPageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;
        long total = 8;
        Page<UserDTO> expected = makePageUserDTO(page, elt, total);

        Mockito.when(contactService.getPending(page, elt, user)).thenReturn(expected);

        Response response = controller.pendingPage(page, elt, securityContext);
        Page<UserDTO> actual = ControllerHelper.extractSuccessResult(response, Page.class);
        assertEquals((Integer) elt, actual.getSize());
        assertEquals((Integer) page, actual.getPage());
        assertEquals(total, actual.getTotal());
        assertEquals(expected.getList().size(), actual.getList().size());
        assertEquals(expected.getList(), actual.getList());
    }

    @Test
    public void invalidPendingPageTest() {
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 0;
        int elt = 3;

        Response response = controller.pendingPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.PAGE_NUMBER_INVALID));
    }

    @Test
    public void userNotFoundPendingPageTest() {
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        int page = 1;
        int elt = 3;

        Mockito.when(contactService.getPending(page, elt, user)).thenReturn(null);

        Response response = controller.pendingPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void unableToGetConnectedUserPendingPageTest() {
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();
        int page = 1;
        int elt = 3;

        Response response = controller.pendingPage(page, elt, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void addTest() {
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        UserDTO user = UserMaker.makeUserDTO();
        UserDTO res = UserMaker.makeUserDTO();
        res.setId(2);
        res.setTag("gloup#98765");
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(tagValidator.isNotValid(tag)).thenReturn(false);
        Mockito.when(contactService.add(user, tag)).thenReturn(res);

        Response response = controller.add(tag, securityContext);
        ControllerHelper.successResult(response);
    }

    @Test
    public void invalidTagAddTest() {
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        String trace1 = "trace1";
        String trace2 = "trace2";
        List<String> trace = new ArrayList<>();
        trace.add(trace1);
        trace.add(trace2);

        Mockito.when(tagValidator.isNotValid(tag)).thenReturn(true);
        Mockito.when(tagValidator.getAllErrors()).thenReturn(trace);

        Response response = controller.add(tag, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(trace1));
        assertTrue(actual.getTrace().contains(trace2));
    }

    @Test
    public void connectedUserNotFoundTest() {
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Mockito.when(tagValidator.isNotValid(tag)).thenReturn(false);

        Response response = controller.add(tag, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void contactAlreadyExistAddTest() {
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        UserDTO user = UserMaker.makeUserDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(tagValidator.isNotValid(tag)).thenReturn(false);
        Mockito.when(contactService.add(user, tag)).thenReturn(null);

        Response response = controller.add(tag, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNAUTHORIZED.getCode(), actual.getCode());
        assertEquals(Result.Error.UNAUTHORIZED.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.CONTACT_REQUEST_ALREADY_EXIST));
    }

    @Test
    public void deleteTest() {
        int userId = 2;
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO other = UserMaker.makeUserDTO();
        other.setId(userId);

        Mockito.when(userService.getById(userId)).thenReturn(other);

        Response response = controller.delete(userId, securityContext);
        ControllerHelper.successResult(response);
    }

    @Test
    public void invalidIdDeleteTest() {
        int userId = 0;
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Response response = controller.delete(userId, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.INVALID_ID));
    }

    @Test
    public void connectedUserNotFoundDeleteTest() {
        int userId = 1;
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Response response = controller.delete(userId, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getCode(), actual.getCode());
        assertEquals(Result.Error.UNABLE_TO_CONNECT.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.USER_NOT_FOUND));
    }

    @Test
    public void invalidUserDeleteTest() {
        int userId = 1;
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Mockito.when(userService.getById(userId)).thenReturn(null);

        Response response = controller.delete(userId, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().contains(Constant.INVALID_ID));
    }

    private Page<UserDTO> makeListUserDTO() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        UserDTO user3 = UserMaker.makeUserDTO();
        user2.setId(2);
        user3.setId(3);
        ArrayList<UserDTO> list = new ArrayList<>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        return Page.unpaged(list);
    }

    private Page<UserDTO> makePageUserDTO(int page, int elt, long total) {
        ArrayList<UserDTO> list = new ArrayList<>();
        for (int i = 0; i < elt; i++) {
            UserDTO user = UserMaker.makeUserDTO();
            user.setId(i + 1);
            list.add(user);
        }
        return Page.paged(page, total, list);
    }
}
