package com.wishurgift.service;

import com.wishurgift.helper.ElementMaker;
import com.wishurgift.helper.UserMaker;
import com.wishurgift.helper.WishlistMaker;
import com.wishurgift.persistence.entity.Shared;
import com.wishurgift.persistence.entity.User;
import com.wishurgift.persistence.entity.Wishlist;
import com.wishurgift.persistence.repository.SharedRepository;
import com.wishurgift.persistence.repository.UserRepository;
import com.wishurgift.persistence.repository.WishlistRepository;
import com.wishurgift.service.bean.WishlistServiceBean;
import com.wishurgift.service.dto.*;
import com.wishurgift.service.dto.request.ElementRequestDTO;
import com.wishurgift.service.dto.request.WishlistRequestDTO;
import com.wishurgift.service.dto.transformer.UserTransformer;
import com.wishurgift.service.dto.transformer.WishlistTransformer;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.*;

public class WishlistServiceTest {

    @Mock
    WishlistRepository wishlistRepository;

    @Mock
    UserRepository userRepository;

    @Mock
    SharedRepository sharedRepository;

    @Mock
    ElementService elementService;

    @InjectMocks
    WishlistServiceBean service;

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

    @Test
    public void addTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        WishlistRequestDTO wishlistRequestDTO = WishlistMaker.makeWishlistRequestDTO();
        User user = UserMaker.makeUser();

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));

        WishlistHeaderDTO actual = service.add(userDTO, wishlistRequestDTO);
        assertEquals(wishlistRequestDTO.getName(), actual.getName());
        assertEquals(wishlistRequestDTO.isPublic(), actual.isPublic());
    }

    @Test
    public void noUserAddTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        WishlistRequestDTO wishlistRequestDTO = WishlistMaker.makeWishlistRequestDTO();

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.empty());

        WishlistHeaderDTO actual = service.add(userDTO, wishlistRequestDTO);
        assertNull(actual);
    }

    @Test
    public void updateTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();
        WishlistRequestDTO wishlistRequestDTO = WishlistMaker.makeWishlistRequestDTO();
        Wishlist expected = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(expected));

        Result<WishlistHeaderDTO> result = service.update(principal, id, wishlistRequestDTO);
        assertTrue(result.isSuccess());
        WishlistHeaderDTO actual = result.getData();
        assertEquals(expected.getId(), actual.getId());
        assertEquals(expected.getName(), actual.getName());
        assertEquals(expected.isPublic(), actual.isPublic());
    }

    @Test
    public void unableToGetDataUpdateTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();
        WishlistRequestDTO wishlistRequestDTO = WishlistMaker.makeWishlistRequestDTO();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        Result<ErrorDTO> result = service.update(principal, id, wishlistRequestDTO);
        assertFalse(result.isSuccess());
        ErrorDTO error = result.getData();
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), error.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), error.getMessage());
        assertEquals(0, error.getTrace().size());
    }

    @Test
    public void unauthorizedUpdateTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();
        principal.setId(2);
        WishlistRequestDTO wishlistRequestDTO = WishlistMaker.makeWishlistRequestDTO();
        Wishlist expected = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(expected));

        Result<ErrorDTO> result = service.update(principal, id, wishlistRequestDTO);
        assertFalse(result.isSuccess());
        ErrorDTO error = result.getData();
        assertEquals(Result.Error.UNAUTHORIZED.getCode(), error.getCode());
        assertEquals(Result.Error.UNAUTHORIZED.getMessage(), error.getMessage());
        assertEquals(0, error.getTrace().size());
    }

    @Test
    public void removeTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(sharedRepository.getAllFromWishlist(wishlist)).thenReturn(Collections.emptyList());

        Result result = service.remove(principal, id);
        assertTrue(result.isSuccess());
    }

    @Test
    public void unableToGetDataRemoveTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        Result<ErrorDTO> result = service.remove(principal, id);
        assertFalse(result.isSuccess());
        ErrorDTO error = result.getData();
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getCode(), error.getCode());
        assertEquals(Result.Error.UNABLE_TO_GET_DATA.getMessage(), error.getMessage());
        assertEquals(0, error.getTrace().size());
    }

    @Test
    public void unauthorizedRemoveTest() {
        long id = 8;
        UserDTO principal = UserMaker.makeUserDTO();
        principal.setId(2);
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));

        Result<ErrorDTO> result = service.remove(principal, id);
        assertFalse(result.isSuccess());
        ErrorDTO error = result.getData();
        assertEquals(Result.Error.UNAUTHORIZED.getCode(), error.getCode());
        assertEquals(Result.Error.UNAUTHORIZED.getMessage(), error.getMessage());
        assertEquals(0, error.getTrace().size());
    }

    @Test
    public void getByIdTest() {
        Wishlist expected = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.getById(expected.getId())).thenReturn(expected);

        WishlistDTO actual = service.getById(expected.getId());
        assertNotNull(actual);
        assertEquals(WishlistTransformer.entityToDto(expected), actual);
    }

    @Test
    public void notFoundGetByIdTest() {
        long id = 8;
        Mockito.when(wishlistRepository.getById(id)).thenReturn(null);

        WishlistDTO actual = service.getById(id);
        assertNull(actual);
    }

    @Test
    public void getHeaderByIdTest() {
        Wishlist expected = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.getById(expected.getId())).thenReturn(expected);

        WishlistHeaderDTO actual = service.getHeaderById(expected.getId());
        assertNotNull(actual);
        assertEquals(WishlistTransformer.entityToHeaderDto(expected), actual);
    }

    @Test
    public void notFoundGetHeaderByIdTest() {
        long id = 8;
        Mockito.when(wishlistRepository.getById(id)).thenReturn(null);

        WishlistHeaderDTO actual = service.getHeaderById(id);
        assertNull(actual);
    }

    @Test
    public void shareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        User user = UserMaker.makeUser();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.of(user));
        Mockito.when(sharedRepository.findShared(wishlist, user)).thenReturn(Optional.empty());

        assertTrue(service.share(principal, id, idUser));
    }

    @Test
    public void wishlistNotFoundShareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        assertFalse(service.share(principal, id, idUser));
    }

    @Test
    public void unauthorizedShareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        principal.setId(2);
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));

        assertFalse(service.share(principal, id, idUser));
    }

    @Test
    public void userNotFoudShareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.empty());

        assertFalse(service.share(principal, id, idUser));
    }

    @Test
    public void alreadyExistShareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        User user = UserMaker.makeUser();
        Shared shared = WishlistMaker.makeShared();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.of(user));
        Mockito.when(sharedRepository.findShared(wishlist, user)).thenReturn(Optional.of(shared));

        assertFalse(service.share(principal, id, idUser));
    }

    @Test
    public void unshareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        User user = UserMaker.makeUser();
        Shared shared = WishlistMaker.makeShared();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.of(user));
        Mockito.when(sharedRepository.findShared(wishlist, user)).thenReturn(Optional.of(shared));

        assertTrue(service.unshare(principal, id, idUser));
    }

    @Test
    public void wishlistNotFoundUnshareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        assertFalse(service.unshare(principal, id, idUser));
    }

    @Test
    public void unauthorizedUnshareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        principal.setId(2);
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));

        assertFalse(service.unshare(principal, id, idUser));
    }

    @Test
    public void userNotFoudUnshareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.empty());

        assertFalse(service.unshare(principal, id, idUser));
    }

    @Test
    public void notExistUnshareTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        long id = 8;
        long idUser = 6;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        User user = UserMaker.makeUser();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(userRepository.findById(idUser)).thenReturn(Optional.of(user));
        Mockito.when(sharedRepository.findShared(wishlist, user)).thenReturn(Optional.empty());

        assertFalse(service.unshare(principal, id, idUser));
    }

    @Test
    public void getWishlistTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        wishlist.getUser().setId(2);

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(wishlistRepository.findWishlist(id, user)).thenReturn(Optional.of(wishlist));

        WishlistDTO actual = service.getWishlist(id, userDTO);
        assertNotNull(actual);
        assertEquals(WishlistTransformer.entityToDto(wishlist), actual);
    }

    @Test
    public void getWishlistOwnerTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        List<User> list = new ArrayList<>();
        list.add(UserMaker.makeUser());
        list.add(UserMaker.makeUser());
        list.get(1).setId(2);
        list.get(1).setName("other");
        WishlistDTO expected = WishlistTransformer.entityToDto(wishlist);
        expected.setShared(UserTransformer.entityToLightDto(list));

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(wishlistRepository.findWishlist(id, user)).thenReturn(Optional.of(wishlist));
        Mockito.when(wishlistRepository.sharedWith(wishlist)).thenReturn(list);

        WishlistDTO actual = service.getWishlist(id, userDTO);
        assertNotNull(actual);
        assertEquals(expected, actual);
    }

    @Test
    public void getWishlistOwnerSharedFailTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        WishlistDTO expected = WishlistTransformer.entityToDto(wishlist);

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(wishlistRepository.findWishlist(id, user)).thenReturn(Optional.of(wishlist));
        Mockito.when(wishlistRepository.sharedWith(wishlist)).thenReturn(null);

        WishlistDTO actual = service.getWishlist(id, userDTO);
        assertNotNull(actual);
        assertEquals(expected, actual);
    }

    @Test
    public void getWishlistNoUserTest() {
        long id = 8;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        wishlist.setPublic(true);

        Mockito.when(wishlistRepository.getById(id)).thenReturn(wishlist);

        WishlistDTO actual = service.getWishlist(id, null);
        assertNotNull(actual);
        assertEquals(WishlistTransformer.entityToDto(wishlist), actual);
    }

    @Test
    public void userNotFoundGetWishlistTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.empty());

        WishlistDTO actual = service.getWishlist(id, userDTO);
        assertNull(actual);
    }

    @Test
    public void wishlistNotFoundGetWishlistTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(wishlistRepository.findWishlist(id, user)).thenReturn(Optional.empty());

        WishlistDTO actual = service.getWishlist(id, userDTO);
        assertNull(actual);
    }

    @Test
    public void wishlistNotFoundGetWishlistNoUserTest() {
        long id = 8;

        Mockito.when(wishlistRepository.getById(id)).thenReturn(null);

        WishlistDTO actual = service.getWishlist(id, null);
        assertNull(actual);
    }

    @Test
    public void wishlistPrivateGetWishlistNoUserTest() {
        long id = 8;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        wishlist.setPublic(false);

        Mockito.when(wishlistRepository.getById(id)).thenReturn(wishlist);

        WishlistDTO actual = service.getWishlist(id, null);
        assertNull(actual);
    }

    @Test
    public void addElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        ElementDTO expected = ElementMaker.makeElementDTO();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(elementService.add(wishlist, request)).thenReturn(expected);

        ElementDTO actual = service.addElement(userDTO, id, request);
        assertEquals(expected, actual);
    }

    @Test
    public void wishlistNotFoundAddElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        ElementDTO actual = service.addElement(userDTO, id, request);
        assertNull(actual);
    }

    @Test
    public void nullElementAddElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(elementService.add(wishlist, request)).thenReturn(null);

        ElementDTO actual = service.addElement(userDTO, id, request);
        assertNull(actual);
    }

    @Test
    public void sharedWithTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        List<User> users = new ArrayList<>();
        users.add(UserMaker.makeUser());
        users.add(UserMaker.makeUser());
        users.get(1).setId(2);
        users.get(1).setName("other");

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));
        Mockito.when(wishlistRepository.sharedWith(wishlist)).thenReturn(users);

        Page<UserLightDTO> actual = service.sharedWith(userDTO, id);
        assertEquals(users.size(), actual.getTotal());
        assertEquals(UserTransformer.entityToLightDto(users), actual.getList());
    }

    @Test
    public void notFoundSharedWithTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.empty());

        Page<UserLightDTO> actual = service.sharedWith(userDTO, id);
        assertNull(actual);
    }

    @Test
    public void unauthorizedSharedWithTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        userDTO.setId(2);
        Wishlist wishlist = WishlistMaker.makeWishlist();

        Mockito.when(wishlistRepository.findById(id)).thenReturn(Optional.of(wishlist));

        Page<UserLightDTO> actual = service.sharedWith(userDTO, id);
        assertNull(actual);
    }

}
