package com.nonononoki.alovoa.service;

import com.nonononoki.alovoa.entity.Captcha;
import com.nonononoki.alovoa.entity.User;
import com.nonononoki.alovoa.entity.user.UserHide;
import com.nonononoki.alovoa.repo.CaptchaRepository;
import com.nonononoki.alovoa.repo.ConversationRepository;
import com.nonononoki.alovoa.repo.UserHideRepository;
import com.nonononoki.alovoa.repo.UserRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.transaction.annotation.Transactional;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;

@SpringBootTest
@ActiveProfiles("test")
@Transactional
class ScheduleServiceTest {

	@Autowired
	private CaptchaRepository captchaRepo;

	@Autowired
	private UserHideRepository userHideRepo;

	@Autowired
	private UserRepository userRepo;

	@Autowired
	private ConversationRepository conversationRepo;

	@Autowired
	private ScheduleService scheduleService;

	@Autowired
	private CaptchaService captchaService;

	@MockitoBean
	private AuthService authService;

	@MockitoBean
	private MailService mailService;

	@Value("${app.schedule.delay.captcha}")
	private long captchaDelay;

	@Value("${app.schedule.delay.hide}")
	private long hideDelay;

	@Value("${app.first-name.length-max}")
	private int firstNameLengthMax;

	@Value("${app.first-name.length-min}")
	private int firstNameLengthMin;

	@Autowired
	private CaptchaRepository catchaRepo;

	@Autowired
	private RegisterService registerService;

	@Autowired
	private UserService userService;

	private List<User> testUsers;

	@BeforeEach
	void before() throws Exception {
		Mockito.when(mailService.sendMail(Mockito.any(String.class), any(String.class), any(String.class),
				any(String.class))).thenReturn(true);
		testUsers = RegisterServiceTest.getTestUsers(captchaService, registerService, firstNameLengthMax,
				firstNameLengthMin);
	}

	@AfterEach
	void after() throws Exception {
		RegisterServiceTest.deleteAllUsers(userService, authService, captchaService, conversationRepo, userRepo);
	}

	@Test
	void test() throws Exception {

		Date currentDate = new Date();
		long currentDateTime = currentDate.getTime();

		// CAPTCHA
		
		catchaRepo.deleteAll();
		
		Captcha captchaOld = generateCaptcha("hash1");
		captchaOld.setDate(new Date(currentDateTime - captchaDelay - 1));
		catchaRepo.saveAndFlush(captchaOld);

		Captcha captchaNew = generateCaptcha("hash2");
		captchaNew.setDate(new Date(currentDateTime - captchaDelay));
		catchaRepo.saveAndFlush(captchaNew);

		assertEquals(2, catchaRepo.count());
		scheduleService.cleanCaptcha(currentDate);
		assertEquals(1, catchaRepo.count());

		// USERHIDE
		User user1 = testUsers.get(1);
		User user2 = testUsers.get(2);

		UserHide hideOld = new UserHide();
		hideOld.setDate(new Date(currentDateTime - hideDelay - 1));
		hideOld.setUserFrom(user1);
		hideOld.setUserTo(user2);
		user1.getHiddenUsers().add(hideOld);
		userRepo.saveAndFlush(user1);

		UserHide hideNew = new UserHide();
		hideNew.setDate(new Date(currentDateTime - hideDelay));
		hideNew.setUserFrom(user2);
		hideNew.setUserTo(user1);
		user2.getHiddenUsers().add(hideNew);
		userRepo.saveAndFlush(user2);

		assertEquals(2, userHideRepo.count());
		scheduleService.cleanUserHide(currentDate);
		assertEquals(1, userHideRepo.count());
	}

	@Test
	public void testScheduleLong() throws Exception {
		// CLEAN-UP NON-CONFIRMED USERS
		var mockNewUsers = RegisterServiceTest.createMockNewUsers(captchaService, registerService, 3);
		User mockUser1 = mockNewUsers.get(0);
		User mockUser2 = mockNewUsers.get(1);
		User mockUser3 = mockNewUsers.get(2);

		try {
			// Non-confirmed user with more than 15 days creation date
			mockUser1.setConfirmed(false);
			var dates = mockUser1.getDates();
			dates.setCreationDate(addUnitsToDate(dates.getCreationDate(), Calendar.DATE, -15));

			// Non-confirmed user with 14 days creation date
			mockUser2.setConfirmed(false);
			dates = mockUser2.getDates();
			dates.setCreationDate(addUnitsToDate(dates.getCreationDate(), Calendar.DATE, -14));

			// Confirmed user with more than 15 days creation date
			mockUser3.setConfirmed(true);
			dates = mockUser3.getDates();
			dates.setCreationDate(addUnitsToDate(dates.getCreationDate(), Calendar.DATE, -15));
			userRepo.saveAllAndFlush(List.of(mockUser1, mockUser2, mockUser3));
			var initialCount = userRepo.count();

			scheduleService.cleanNonConfirmedUsers(new Date());
			assertEquals(2, (initialCount - userRepo.count()));
		} finally {
			RegisterServiceTest.deleteGivenUser(mockUser3, userService, userRepo, captchaService, authService);
		}
	}

	private Captcha generateCaptcha(String hashCode) {
		Captcha captcha = new Captcha();
		captcha.setDate(new Date());
		captcha.setImage(null);
		captcha.setText("test");
		captcha.setHashCode(hashCode);
		captcha = captchaRepo.saveAndFlush(captcha);
		return captcha;
	}

	private static Date addUnitsToDate(final Date inputDate, final int unitField, final int unitAmount) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(inputDate);
		calendar.add(unitField, unitAmount);

		return calendar.getTime();
	}
}
