package com.wishurgift.entry.controller;

import com.wishurgift.entry.validator.CommentValidator;
import com.wishurgift.helper.CommentMaker;
import com.wishurgift.helper.ControllerHelper;
import com.wishurgift.helper.UserMaker;
import com.wishurgift.service.CommentService;
import com.wishurgift.service.dto.CommentDTO;
import com.wishurgift.service.dto.ErrorDTO;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.request.CommentRequestDTO;
import com.wishurgift.utils.Constant;
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 CommentControllerTest {

    @Mock
    private CommentService commentService;

    @Mock
    private CommentValidator commentValidator;

    @InjectMocks
    private CommentController controller;

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

    @Test
    public void getCommentTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();
        CommentDTO expected = CommentMaker.makeCommentDTO();

        Mockito.when(commentService.getById(principal, id)).thenReturn(expected);

        Response response = controller.getComment(id, securityContext);
        CommentDTO actual = ControllerHelper.extractSuccessResult(response, CommentDTO.class);
        assertEquals(expected, actual);
    }

    @Test
    public void getCommentNoUserTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();
        CommentDTO expected = CommentMaker.makeCommentDTO();

        Mockito.when(commentService.getById(null, id)).thenReturn(expected);

        Response response = controller.getComment(id, securityContext);
        CommentDTO actual = ControllerHelper.extractSuccessResult(response, CommentDTO.class);
        assertEquals(expected, actual);
    }

    @Test
    public void invalidIdGetCommentTest() {
        long id = -8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();

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

    @Test
    public void failGetCommentTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();

        Mockito.when(commentService.getById(principal, id)).thenReturn(null);

        Response response = controller.getComment(id, 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().isEmpty());
    }

    @Test
    public void updateTest() {
        long id = 8;
        CommentRequestDTO request = CommentMaker.makeCommentRequestDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();
        CommentDTO expected = CommentMaker.makeCommentDTO();

        Mockito.when(commentValidator.isNotValid(request)).thenReturn(false);
        Mockito.when(commentService.put(principal, id, request)).thenReturn(expected);

        Response response = controller.update(id, request, securityContext);
        CommentDTO actual = ControllerHelper.extractSuccessResult(response, CommentDTO.class);
        assertEquals(expected, actual);
    }

    @Test
    public void invalidIdUpdateTest() {
        long id = -8;
        CommentRequestDTO request = CommentMaker.makeCommentRequestDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();

        Response response = controller.update(id, request, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertEquals(1, actual.getTrace().size());
        assertTrue(actual.getTrace().contains(Constant.INVALID_ID));
    }

    @Test
    public void invalidRequestUpdateTest() {
        long id = 8;
        CommentRequestDTO request = CommentMaker.makeCommentRequestDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        List<String> trace = new ArrayList<>();
        trace.add("trace1");
        trace.add("trace2");

        Mockito.when(commentValidator.isNotValid(request)).thenReturn(true);
        Mockito.when(commentValidator.getAllErrors()).thenReturn(trace);

        Response response = controller.update(id, request, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.INVALID_REQUEST.getCode(), actual.getCode());
        assertEquals(Result.Error.INVALID_REQUEST.getMessage(), actual.getMessage());
        assertEquals(trace.size(), actual.getTrace().size());
        trace.forEach(elt -> assertTrue(actual.getTrace().contains(elt)));
    }

    @Test
    public void userNotFoundUpdateTest() {
        long id = 8;
        CommentRequestDTO request = CommentMaker.makeCommentRequestDTO();
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

        Mockito.when(commentValidator.isNotValid(request)).thenReturn(false);

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

    @Test
    public void failUpdateTest() {
        long id = 8;
        CommentRequestDTO request = CommentMaker.makeCommentRequestDTO();
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();

        Mockito.when(commentValidator.isNotValid(request)).thenReturn(false);
        Mockito.when(commentService.put(principal, id, request)).thenReturn(null);

        Response response = controller.update(id, request, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNAUTHORIZED.getCode(), actual.getCode());
        assertEquals(Result.Error.UNAUTHORIZED.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().isEmpty());
    }

    @Test
    public void deleteTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();

        Mockito.when(commentService.delete(principal, id)).thenReturn(true);

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

    @Test
    public void invalidIdDeleteTest() {
        long id = -8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();

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

    @Test
    public void userNotFoundDeleteTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeInvalidSecurityContext();

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

    @Test
    public void failDeleteTest() {
        long id = 8;
        SecurityContext securityContext = UserMaker.makeSecurityContext();
        UserDTO principal = UserMaker.makeUserDTO();

        Mockito.when(commentService.delete(principal, id)).thenReturn(false);

        Response response = controller.delete(id, securityContext);
        ErrorDTO actual = ControllerHelper.extractErrorResult(response);
        assertEquals(Result.Error.UNAUTHORIZED.getCode(), actual.getCode());
        assertEquals(Result.Error.UNAUTHORIZED.getMessage(), actual.getMessage());
        assertTrue(actual.getTrace().isEmpty());
    }

}
