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

import cz.muni.fi.pa165.core.company.Company;
import cz.muni.fi.pa165.core.helpers.exceptions.EntityDeletionException;
import cz.muni.fi.pa165.core.user.User;
import cz.muni.fi.pa165.model.dto.role.enums.CompanyRoleEnum;
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.opentest4j.AssertionFailedError;
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.*;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;

class CompanyRoleServiceTest {

	@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();

		Company company = Company
				.builder()
				.name("Apple")
				.build();

		CompanyRole companyRole = CompanyRole
				.builder()
				.company(company)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole.setUser(user);


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

		CompanyRole createdCompanyRole = (CompanyRole) roleService.create(companyRole);

		verify(roleRepository).save(companyRole);

		assertThat(createdCompanyRole).isEqualTo(companyRole);
	}

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

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);

		CompanyRole companyRole2 = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole2.setUser(user);



		Page<CompanyRole> page = new PageImpl<>(List.of(companyRole1, companyRole2));

		int pageNumber = 0;
		int pageSize = 10;

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

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

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

		assertThat(result).containsExactlyInAnyOrder(companyRole1, companyRole2);
	}

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

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);

		CompanyRole companyRole2 = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole2.setUser(user);

		var data = List.of(companyRole1, companyRole2);

		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();

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);

		CompanyRole companyRole2 = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole2.setUser(user);

		Page<CompanyRole> page = new PageImpl<>(List.of(companyRole1, companyRole2));

		int pageNumber = 0;
		int pageSize = 10;

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

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

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

		assertThat(result).containsExactlyInAnyOrder(companyRole1, companyRole2);
	}

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

		Company company = Company
				.builder()
				.name("Apple")
				.build();

		CompanyRole companyRole = CompanyRole
				.builder()
				.company(company)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole.setUser(user);

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

		CompanyRole result = (CompanyRole) roleService.findById(companyRole.getId());

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

		assertEquals(companyRole, result);
	}

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

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);

		CompanyRole companyRole2 = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole2.setUser(user);

		var data = List.of(companyRole1, companyRole2);

		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();

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);

		CompanyRole companyRole2 = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole2.setUser(user);

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

		try {
			roleService.deleteAllById(ids);
			verify(roleRepository).save(companyRole1);
			verify(roleRepository).save(companyRole2);
		} catch (EntityDeletionException e)
		{
			Assertions.assertThat(false).isTrue();
		}
	}

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

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		CompanyRole companyRole1 = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		companyRole1.setUser(user);


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

		// TODO
		try {
			CompanyRole deletedCompanyRole = (CompanyRole) roleService.deleteById(companyRole1.getId());
			verify(roleRepository).findById(companyRole1.getId());
			verify(roleRepository).save(companyRole1); // soft delete

			assertNotNull(deletedCompanyRole.getDeletedAt());
			assertEquals(companyRole1, deletedCompanyRole);
		}
		catch (Exception ex){
			Assertions.assertThat(false).isTrue();
		}
	}

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

		Company company1 = Company
				.builder()
				.name("Apple")
				.build();

		Company company2 = Company
				.builder()
				.name("Motorola")
				.build();

		CompanyRole originalCompanyRole = CompanyRole
				.builder()
				.company(company1)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		originalCompanyRole.setUser(user);

		CompanyRole updatedCompanyRole = CompanyRole
				.builder()
				.company(company2)
				.companyRoleEnum(CompanyRoleEnum.Employee)
				.build();
		updatedCompanyRole.setUser(user);

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

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

		CompanyRole result = (CompanyRole) roleService.update(updatedCompanyRole, originalCompanyRole.getId());

		verify(roleRepository).findById(originalCompanyRole.getId());
		verify(roleRepository).save(updatedCompanyRole);

		assertEquals(originalCompanyRole.getId(), result.getId());
		assertEquals(updatedCompanyRole.getCompany(), result.getCompany());
		assertEquals(updatedCompanyRole.getUser(), result.getUser());
		assertEquals(updatedCompanyRole.getCompanyRoleEnum(), result.getCompanyRoleEnum());
	}

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