package cz.muni.fi.pa165.core.house;

import cz.muni.fi.pa165.core.smartmeter.SmartMeter;
import cz.muni.fi.pa165.core.smartmeter.SmartMeterRepository;
import cz.muni.fi.pa165.core.user.User;
import cz.muni.fi.pa165.core.user.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(SpringExtension.class)
@DataJpaTest
public class HouseRepositoryTest {
	@Autowired
	private TestEntityManager entityManager;
	@Autowired
	private HouseRepository houseRepository;
	@Autowired
	private SmartMeterRepository smartMeterRepository;
	@Autowired
	private UserRepository userRepository;

	@Test
	public void createHouseTesting() {
		String house = entityManager.persistAndGetId(new House()).toString();
		// Regular expression to match UUID format
		Pattern uuidPattern = Pattern.compile(
				"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
				Pattern.CASE_INSENSITIVE);
		// Check if the string matches the UUID format
		boolean isUUID = uuidPattern.matcher(house).matches();
		assertThat(isUUID).isTrue();
	}

	@Test
	public void shouldFindNoHousesIfRepositoryIsEmpty() {
		Iterable<House> houses = houseRepository.findAll();
		assertThat(houses).isEmpty();
	}

	@Test
	public void shouldStoreAHouse() {
		House house = helperRegisterHouse();

		assertThat(house).hasFieldOrPropertyWithValue("address", "Titova 6");
		assertThat(house).hasFieldOrPropertyWithValue("city", "Sarajevo");
		assertThat(house).hasFieldOrPropertyWithValue("state", "Bosnia and Herzegovina");
		assertThat(house).hasFieldOrPropertyWithValue("zipcode", "71000");
	}

	@Test
	public void shouldFindAllHouses() {
		House house1 = new House();
		entityManager.persist(house1);

		House house2 = new House();
		entityManager.persist(house2);

		House house3 = new House();
		entityManager.persist(house3);

		Iterable houses = houseRepository.findAll();

		assertThat(houses).hasSize(3).contains(house1, house2, house3);
	}

	@Test
	public void shouldFindHouseById() {
		House house1 = new House();
		entityManager.persist(house1);
		House house2 = new House();
		entityManager.persist(house2);

		House foundHouse = houseRepository.findById(house2.getId()).get();

		assertThat(foundHouse).isEqualTo(house2);

		foundHouse = houseRepository.findById(house1.getId()).get();

		assertThat(foundHouse).isEqualTo(house1);
	}

	@Test
	public void shouldUpdateHouseById() {
		House house = new House();
		entityManager.persist(house);

		House house2 = new House();
		entityManager.persist(house2);


		House updatedHouse = houseRepository.findById(house2.getId()).get();
		updatedHouse.setAddress("Botanicka");
		updatedHouse.setCity("Brno");
		updatedHouse.setState("Czech Republic");
		updatedHouse.setZipcode("61600");
		houseRepository.save(updatedHouse);

		House checkHouse = houseRepository.findById(house2.getId()).get();

		assertThat(checkHouse.getId()).isEqualTo(house2.getId());
		assertThat(checkHouse.getAddress()).isEqualTo(house2.getAddress());
		//assertThat(checkHouse.getUser()).isEqualTo(house2.getUser());
		assertThat(checkHouse.getCity()).isEqualTo(house2.getCity());
	}

	@Test
	public void shouldDeleteById() {
		House house = new House();
		entityManager.persist(house);

		House house1 = helperRegisterHouse();
		entityManager.persist(house1);

		houseRepository.deleteById(house1.getId());

		assertThat(houseRepository.findAll()).hasSize(1).contains(house);
		assertThat(houseRepository.findAll()).hasSize(1).doesNotContain(house1);
	}

	@Test
	void shouldDeleteAll() {
		entityManager.persist(helperRegisterHouse());

		houseRepository.deleteAll();

		assertThat(houseRepository.findAll()).isEmpty();
	}

	private House helperRegisterHouse() {
		//need to register a house first
		Iterable<SmartMeter> smartMeterIterable;
		smartMeterIterable = smartMeterRepository.saveAll(List.of(SmartMeter.builder().name("sm01").build(),
				SmartMeter.builder().name("sm02").build(),
				SmartMeter.builder().name("sm03").build()));
		List<SmartMeter> list = StreamSupport.stream(smartMeterIterable.spliterator(), false)
				.collect(Collectors.toList());
		//need to save a user to the database first to asign him to the new house
		User user = userRepository.save(User.builder().username("usr01").build());
		return houseRepository.save(new House("Titova 6", "Sarajevo", "Bosnia and Herzegovina", "71000", list, new ArrayList<>()));
	}
}