package cz.muni.fi.pa165.core.user.roles;

import cz.muni.fi.pa165.core.house.House;
import cz.muni.fi.pa165.core.user.User;
import cz.muni.fi.pa165.model.dto.role.enums.HouseRoleEnum;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;

class HouseRoleServiceTest {

	@InjectMocks
	private RoleService roleService;

	@Mock
	private RoleRepository roleRepository;

	@BeforeEach
	void init() {
		openMocks(this);
	}

	@Test
	void shouldCreate() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole = HouseRole
				.builder()
				.house(house)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole.setUser(user);


		when(roleRepository.save(houseRole)).thenReturn(houseRole);

		HouseRole createdHouseRole = (HouseRole) roleService.create(houseRole);

		verify(roleRepository).save(houseRole);

		assertThat(createdHouseRole).isEqualTo(houseRole);
	}

	@Test
	void shouldFindAllPageable() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		House house2 = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);

		HouseRole houseRole2 = HouseRole
				.builder()
				.house(house2)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole2.setUser(user);

		Page<HouseRole> page = new PageImpl<>(List.of(houseRole1, houseRole2));

		int pageNumber = 0;
		int pageSize = 10;

		when(roleRepository.findAll(PageRequest.of(pageNumber, pageSize))).thenReturn(page);

		Page<HouseRole> result = roleService.findAllPageable(PageRequest.of(pageNumber, pageSize));

		verify(roleRepository).findAll(PageRequest.of(pageNumber, pageSize));

		assertThat(result).containsExactlyInAnyOrder(houseRole1, houseRole2);
	}

	@Test
	void shouldFindAll() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		House house2 = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);

		HouseRole houseRole2 = HouseRole
				.builder()
				.house(house2)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole2.setUser(user);

		var data = List.of(houseRole1, houseRole2);

		when(roleRepository.findAll()).thenReturn(data);
		List<CompanyRole> result = roleService.findAll();
		verify(roleRepository).findAll();
		assertEquals(data, result);
	}

	@Test
	void shouldFindAllPageableInt() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		House house2 = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);

		HouseRole houseRole2 = HouseRole
				.builder()
				.house(house2)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole2.setUser(user);

		Page<HouseRole> page = new PageImpl<>(List.of(houseRole1, houseRole2));

		int pageNumber = 0;
		int pageSize = 10;

		when(roleRepository.findAll(PageRequest.of(pageNumber, pageSize))).thenReturn(page);

		Page<HouseRole> result = roleService.findAllPageableInt(pageNumber);

		verify(roleRepository).findAll(PageRequest.of(pageNumber, pageSize));

		assertThat(result).containsExactlyInAnyOrder(houseRole1, houseRole2);
	}

	@Test
	void shouldFindById() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		HouseRole houseRole = HouseRole
				.builder()
				.house(house)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole.setUser(user);

		when(roleRepository.findById(houseRole.getId())).thenReturn(Optional.of(houseRole));

		HouseRole result = (HouseRole) roleService.findById(houseRole.getId());

		verify(roleRepository).findById(houseRole.getId());

		assertEquals(houseRole, result);
	}

	@Test
	void shouldDeleteAll() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		House house2 = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);

		HouseRole houseRole2 = HouseRole
				.builder()
				.house(house2)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole2.setUser(user);

		var data = List.of(houseRole1, houseRole2);

		when(roleRepository.findAll()).thenReturn(data);
		roleService.deleteAll();
		verify(roleRepository).findAll();
		verify(roleRepository).saveAll(data);
	}

	@Test
	void shouldDeleteAllById() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		House house2 = House
				.builder()
				.state("CR")
				.address("lsakdj")
				.city("sdfa")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);

		HouseRole houseRole2 = HouseRole
				.builder()
				.house(house2)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole2.setUser(user);

		String[] ids = {houseRole1.getId(), houseRole2.getId()};
		when(roleRepository.findById(ids[0])).thenReturn(Optional.of(houseRole1));
		when(roleRepository.findById(ids[1])).thenReturn(Optional.of(houseRole2));

		try{
			roleService.deleteAllById(ids);
		}
		catch (Exception ex){
			Assertions.assertThat(false).isTrue();
		}

		verify(roleRepository).save(houseRole1);
		verify(roleRepository).save(houseRole2);
	}

	@Test
	void shouldDeleteById() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		HouseRole houseRole1 = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		houseRole1.setUser(user);


		when(roleRepository.findById(houseRole1.getId())).thenReturn(Optional.of(houseRole1));

		HouseRole deletedHouseRole = new HouseRole();
		try {
			deletedHouseRole = (HouseRole) roleService.deleteById(houseRole1.getId());
		}
		catch (Exception ex){
			Assertions.assertThat(false).isTrue();
		}
		verify(roleRepository).findById(houseRole1.getId());
		verify(roleRepository).save(houseRole1); // soft delete

		assertNotNull(deletedHouseRole.getDeletedAt());
		assertEquals(houseRole1, deletedHouseRole);
	}

	@Test
	void shouldUpdate() {
		User user = User
				.builder()
				.username("dsflkja")
				.password("asdfa")
				.firstName("asdf")
				.lastName("adfdf")
				.email("akjsfh@fdalsk.cz")
				.build();

		House house1 = House
				.builder()
				.state("CR")
				.address("ljsdf")
				.city("saoe")
				.build();

		HouseRole originalHouseRole = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		originalHouseRole.setUser(user);

		HouseRole updatedHouseRole = HouseRole
				.builder()
				.house(house1)
				.houseRole(HouseRoleEnum.Owner)
				.build();
		updatedHouseRole.setUser(user);

		when(roleRepository.findById(originalHouseRole.getId())).thenReturn(Optional.of(originalHouseRole));

		when(roleRepository.save(updatedHouseRole)).thenReturn(updatedHouseRole);

		HouseRole result = (HouseRole) roleService.update(updatedHouseRole, originalHouseRole.getId());

		verify(roleRepository).findById(originalHouseRole.getId());
		verify(roleRepository).save(updatedHouseRole);

		assertEquals(originalHouseRole.getId(), result.getId());
		assertEquals(updatedHouseRole.getHouse(), result.getHouse());
		assertEquals(updatedHouseRole.getUser(), result.getUser());
		assertEquals(updatedHouseRole.getHouseRole(), result.getHouseRole());
	}

	@Test
	void shouldHardDeleteAll() {
		roleService.hardDeleteAll();
		verify(roleRepository).deleteAll();
	}
}