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

import cz.muni.fi.pa165.core.smartmeter.SmartMeter;
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 org.springframework.data.domain.Pageable;

import java.util.ArrayList;
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.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;
public class HouseServiceTest {
    @InjectMocks
    private HouseService houseService;

    @Mock
    private HouseRepository houseRepository;

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

    public House helperNewHouse() {
        List<SmartMeter> list = List.of(new SmartMeter(), new SmartMeter(), new SmartMeter());
        return new House("Titova 6", "Sarajevo", "Bosnia and Herzegovina", "71000", list, new ArrayList<>());
    }

    @Test
    public void shouldFindHouseById() {

        House house = House
                .builder()
                .city("Sarajevo")
                .state("Bosnia and Herzegovina")
                .zipcode("71000")
                .ownerList(new ArrayList<>())
                .address("Titova 6")
                .build();

        when(houseRepository.findById(house.getId())).thenReturn(Optional.of(house));

        House result = houseService.findById(house.getId());

        verify(houseRepository).findById(house.getId());

        assertEquals(house, result);
    }
    @Test
    public void shouldCreateNewHouse() {
        House house = helperNewHouse();
        when(houseRepository.save(house)).thenReturn(house);

        House house1 = houseService.create(house);

        verify(houseRepository).save(house);

        assertThat(house1).isEqualTo(house);
    }

    @Test
    void shouldFindAllHousesPageableInt() {
        House h1 = helperNewHouse();
        House h2 = helperNewHouse();

        Page<House> page = new PageImpl<>(List.of(h1, h2));

        int pageNumber = 0;
        int pageSize = 10;

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

        Page<House> result = houseService.findAllPageableInt(pageNumber);

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

        assertThat(result).containsExactlyInAnyOrder(h1, h2);
    }

    @Test
    void shouldUpdateHouse() {
        House h1 = helperNewHouse();
        House h2 = helperNewHouse();

        when(houseRepository.findById(h1.getId())).thenReturn(Optional.of(h1));

        when(houseRepository.save(h2)).thenReturn(h2);

        House result = houseService.update(h2, h1.getId());

        verify(houseRepository).findById(h1.getId());
        verify(houseRepository).save(h2);

        assertEquals(h1.getId(), result.getId());
        assertEquals(h2.getAddress(), result.getAddress());
    }

    @Test
    void shouldDeleteAllHouses() {
        var data = List.of(
                helperNewHouse(), helperNewHouse(), helperNewHouse()
        );
        when(houseRepository.findAll()).thenReturn(data);
        houseService.deleteAll();
        verify(houseRepository).findAll();
        verify(houseRepository).saveAll(data);
    }

    @Test
    void shouldDeleteAllCompaniesByIds() {
        House h1 = helperNewHouse();
        House h2 = helperNewHouse();
        String[] ids = {h1.getId(), h2.getId()};
        when(houseRepository.findById(h1.getId())).thenReturn(Optional.of(h1));
        when(houseRepository.findById(h2.getId())).thenReturn(Optional.of(h2));

        try {
            houseService.deleteAllById(ids);
            verify(houseRepository).save(h1);
            verify(houseRepository).save(h2);
        }catch(Exception e){
            Assertions.assertThat(false).isTrue();
        }
    }

    @Test
    void shouldFindAllHouses() {
        List<House> houses = List.of(
                helperNewHouse(), helperNewHouse(), helperNewHouse()
        );

        when(houseRepository.findAll()).thenReturn(houses);
        List<House> result = houseService.findAll();
        verify(houseRepository).findAll();
        assertEquals(houses, result);
    }

    @Test
    void shouldFindAllHousesPageable() {
        int pageNumber = 0;
        int pageSize = 10;
        Pageable pageable = PageRequest.of(pageNumber, pageSize);

        List<House> houses = List.of(
                helperNewHouse(), helperNewHouse(), helperNewHouse()
        );

        Page<House> pageResult = new PageImpl<>(houses, pageable, houses.size());
        when(houseRepository.findAll(pageable)).thenReturn(pageResult);

        Page<House> result = houseService.findAllPageable(pageable);
        verify(houseRepository).findAll(pageable);

        assertEquals(pageResult, result);
    }
}
