package com.wishurgift.service;

import com.wishurgift.helper.UserMaker;
import com.wishurgift.persistence.entity.Contact;
import com.wishurgift.persistence.entity.User;
import com.wishurgift.persistence.repository.ContactRepository;
import com.wishurgift.persistence.repository.UserRepository;
import com.wishurgift.service.bean.ContactServiceBean;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.request.TagRequestDTO;
import com.wishurgift.service.dto.transformer.UserTransformer;
import com.wishurgift.wrapper.Page;
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.List;
import java.util.Optional;

import static org.junit.Assert.*;

public class ContactServiceTest {

    @Mock
    private ContactRepository contactRepository;

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private ContactServiceBean service;

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

    @Test
    public void addTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        User connected = UserMaker.makeUser();
        User other = UserMaker.makeUser();
        String[] split = tag.getTag().split("#");
        other.setId(2);
        other.setName(split[0]);
        other.setTag(split[1]);

        Mockito.when(userRepository.findById(principal.getId())).thenReturn(Optional.of(connected));
        Mockito.when(userRepository.findByTag(UserTransformer.convertTag(tag.getTag()))).thenReturn(Optional.of(other));
        Mockito.when(contactRepository.findRequest(connected, other)).thenReturn(Optional.empty());

        UserDTO actual = service.add(principal, tag);
        assertEquals(UserTransformer.entityToDto(other), actual);
    }

    @Test
    public void connectedUserNotFoundAddTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        User other = UserMaker.makeUser();

        Mockito.when(userRepository.findById(principal.getId())).thenReturn(Optional.empty());
        Mockito.when(userRepository.findByTag(UserTransformer.convertTag(tag.getTag()))).thenReturn(Optional.of(other));

        UserDTO actual = service.add(principal, tag);
        assertNull(actual);
    }

    @Test
    public void otherUserNotFoundAddTest() {
        UserDTO principal = UserMaker.makeUserDTO();
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        User connected = UserMaker.makeUser();

        Mockito.when(userRepository.findById(principal.getId())).thenReturn(Optional.of(connected));
        Mockito.when(userRepository.findByTag(UserTransformer.convertTag(tag.getTag()))).thenReturn(Optional.empty());

        UserDTO actual = service.add(principal, tag);
        assertNull(actual);
    }

    @Test
    public void contactAlreadyExistAddTest() {
        Contact contact = UserMaker.makeContact();
        UserDTO principal = UserMaker.makeUserDTO();
        TagRequestDTO tag = UserMaker.makeTagRequestDTO();
        User connected = UserMaker.makeUser();
        User other = UserMaker.makeUser();
        String[] split = tag.getTag().split("#");
        other.setId(2);
        other.setName(split[0]);
        other.setTag(split[1]);

        Mockito.when(userRepository.findById(principal.getId())).thenReturn(Optional.of(connected));
        Mockito.when(userRepository.findByTag(UserTransformer.convertTag(tag.getTag()))).thenReturn(Optional.of(other));
        Mockito.when(contactRepository.findRequest(connected, other)).thenReturn(Optional.of(contact));

        UserDTO actual = service.add(principal, tag);
        assertNull(null);
    }

    @Test
    public void isContactTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact1 = UserMaker.makeContact();
        contact1.getContact().setId(2);
        Contact contact2 = UserMaker.makeContact();
        contact2.setContact(contact1.getRequester());
        contact2.setRequester(contact2.getContact());

        Mockito.when(userRepository.findById(contact1.getRequester().getId())).thenReturn(Optional.of(contact1.getRequester()));
        Mockito.when(userRepository.findById(contact1.getContact().getId())).thenReturn(Optional.of(contact1.getContact()));
        Mockito.when(contactRepository.findRequest(contact1.getRequester(), contact1.getContact())).thenReturn(Optional.of(contact1));
        Mockito.when(contactRepository.findRequest(contact1.getContact(), contact1.getRequester())).thenReturn(Optional.of(contact2));

        assertTrue(service.isContact(user1, user2));
    }

    @Test
    public void isContactOtherTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.of(contact.getRequester()));
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.of(contact.getContact()));
        Mockito.when(contactRepository.findRequest(contact.getRequester(), contact.getContact())).thenReturn(Optional.of(contact));
        Mockito.when(contactRepository.findRequest(contact.getContact(), contact.getRequester())).thenReturn(Optional.empty());

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void isContactAnotherTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.of(contact.getRequester()));
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.of(contact.getContact()));
        Mockito.when(contactRepository.findRequest(contact.getRequester(), contact.getContact())).thenReturn(Optional.empty());
        Mockito.when(contactRepository.findRequest(contact.getContact(), contact.getRequester())).thenReturn(Optional.of(contact));

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void isContactFinalTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.of(contact.getRequester()));
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.of(contact.getContact()));
        Mockito.when(contactRepository.findRequest(contact.getRequester(), contact.getContact())).thenReturn(Optional.empty());
        Mockito.when(contactRepository.findRequest(contact.getContact(), contact.getRequester())).thenReturn(Optional.empty());

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void userNotFoundisContactTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.empty());
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.of(contact.getContact()));

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void userNotFound2isContactTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.of(contact.getRequester()));
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.empty());

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void userNotFound3isContactTest() {
        UserDTO user1 = UserMaker.makeUserDTO();
        UserDTO user2 = UserMaker.makeUserDTO();
        user2.setId(2);
        Contact contact = UserMaker.makeContact();
        contact.getContact().setId(2);

        Mockito.when(userRepository.findById(contact.getRequester().getId())).thenReturn(Optional.empty());
        Mockito.when(userRepository.findById(contact.getContact().getId())).thenReturn(Optional.empty());

        assertFalse(service.isContact(user1, user2));
    }

    @Test
    public void getRequestTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();

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

        Page<UserDTO> actual = service.getRequest(userDTO);
        assertNull(actual.getPage());
        assertNull(actual.getSize());
        assertEquals(list.size(), actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetRequestTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();

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

        Page<UserDTO> actual = service.getRequest(userDTO);
        assertNull(actual);
    }

    @Test
    public void getRequestPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();
        Integer page = 1;
        Integer size = list.size();
        long total = size + 8;

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(contactRepository.getAllRequest(page, size, user)).thenReturn(list);
        Mockito.when(contactRepository.countAllRequest(user)).thenReturn(total);

        Page<UserDTO> actual = service.getRequest(page, size, userDTO);
        assertEquals(page, actual.getPage());
        assertEquals(size, actual.getSize());
        assertEquals(total, actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetRequestPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        Integer page = 1;
        Integer size = 3;

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

        Page<UserDTO> actual = service.getRequest(page, size, userDTO);
        assertNull(actual);
    }

    @Test
    public void getPendingTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();

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

        Page<UserDTO> actual = service.getPending(userDTO);
        assertNull(actual.getPage());
        assertNull(actual.getSize());
        assertEquals(list.size(), actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetPendingTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();

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

        Page<UserDTO> actual = service.getPending(userDTO);
        assertNull(actual);
    }

    @Test
    public void getPendingPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();
        Integer page = 1;
        Integer size = list.size();
        long total = size + 8;

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(contactRepository.getAllPending(page, size, user)).thenReturn(list);
        Mockito.when(contactRepository.countAllPending(user)).thenReturn(total);

        Page<UserDTO> actual = service.getPending(page, size, userDTO);
        assertEquals(page, actual.getPage());
        assertEquals(size, actual.getSize());
        assertEquals(total, actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetPendingPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        Integer page = 1;
        Integer size = 3;

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

        Page<UserDTO> actual = service.getPending(page, size, userDTO);
        assertNull(actual);
    }

    @Test
    public void getContactTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();

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

        Page<UserDTO> actual = service.getContact(userDTO);
        assertNull(actual.getPage());
        assertNull(actual.getSize());
        assertEquals(list.size(), actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetContactTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();

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

        Page<UserDTO> actual = service.getContact(userDTO);
        assertNull(actual);
    }

    @Test
    public void getContactPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        User user = UserMaker.makeUser();
        List<User> list = makeUsers();
        Integer page = 1;
        Integer size = list.size();
        long total = size + 8;

        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));
        Mockito.when(contactRepository.getAllContact(page, size, user)).thenReturn(list);
        Mockito.when(contactRepository.countAllContact(user)).thenReturn(total);

        Page<UserDTO> actual = service.getContact(page, size, userDTO);
        assertEquals(page, actual.getPage());
        assertEquals(size, actual.getSize());
        assertEquals(total, actual.getTotal());
        assertEquals(UserTransformer.entityToDto(list), actual.getList());
    }

    @Test
    public void userNotFoundGetContactPageTest() {
        UserDTO userDTO = UserMaker.makeUserDTO();
        Integer page = 1;
        Integer size = 3;

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

        Page<UserDTO> actual = service.getContact(page, size, userDTO);
        assertNull(actual);
    }

    private List<User> makeUsers() {
        User user1 = UserMaker.makeUser();
        User user2 = UserMaker.makeUser();
        user2.setId(2);
        User user3 = UserMaker.makeUser();
        user3.setId(3);
        ArrayList<User> list = new ArrayList<>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        return list;
    }

}
