package cz.muni.fi.pa165.electricityTarifMicroservice.electricityprices;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import cz.muni.fi.pa165.electricityTarifMicroservice.electricityprices.dtos.ElectricityPriceGetFullDto;
import cz.muni.fi.pa165.electricityTarifMicroservice.electricityprices.dtos.ElectricityPricePostDto;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = "classpath:application-test.properties")
class ElectricityPriceControllerTest {

	@Autowired
	private MockMvc mockMvc;

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private ElectricityPriceService electricityPriceService;

	private final static String URL = "/api/electricityprices";

	private final static String CONTENT_TYPE = "application/json";

	@BeforeEach
	void setUp() {
		ElectricityPricePostDto newDto = new ElectricityPricePostDto();
		newDto.setCompanyId("1");
		newDto.setPriceLowTariff(200.0);
		newDto.setPriceHighTariff(300.0);
		electricityPriceService.setElectricityPrice(newDto);

		ElectricityPricePostDto newDto2 = new ElectricityPricePostDto();
		newDto2.setCompanyId("2");
		newDto2.setPriceLowTariff(250.0);
		newDto2.setPriceHighTariff(300.0);
		electricityPriceService.setElectricityPrice(newDto2);

	}

	@AfterEach
	void cleanUp() {
		List<ElectricityPriceGetFullDto> dtoList = electricityPriceService.getAllElectricityPrice();
		for (var dto : dtoList) {
			electricityPriceService.deleteElectricityPriceForCompany(dto.getCompanyId());
		}
	}

	/**
	 * Tests the {@link ElectricityPriceController#getAllElectricityPrice()}
	 * Expect all Electricity prices for all companies
	 */
	@Test
	void shouldGetAllElectricityPrices() throws Exception {
		String response = mockMvc.perform(get(URL)
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();
		/*ElectricityPriceGetFullDto[] listPrices = objectMapper.readValue(response, new TypeReference<ElectricityPriceGetFullDto[]>() {
		});

		assertThat(listPrices.length).isEqualTo(2);

		assertThat(listPrices[0].getCompanyId()).isEqualTo("1");
		assertThat(listPrices[0].getPriceLowTariff()).isEqualTo(200.0);
		assertThat(listPrices[0].getPriceHighTariff()).isEqualTo(300.0);

		assertThat(listPrices[1].getCompanyId()).isEqualTo("2");
		assertThat(listPrices[1].getPriceLowTariff()).isEqualTo(250.0);
		assertThat(listPrices[1].getPriceHighTariff()).isEqualTo(300.0);*/

	}

	/**
	 * Tests the {@link ElectricityPriceController#getAllElectricityPrice()}
	 * Expect empty list of Electricity prices
	 */
	@Test
	void shouldGetNoneElectricityPrices() throws Exception {
		electricityPriceService.deleteElectricityPriceForCompany("1");
		electricityPriceService.deleteElectricityPriceForCompany("2");

		String response = mockMvc.perform(get(URL)
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();
		/*ElectricityPriceGetFullDto[] listPrices = objectMapper.readValue(response, new TypeReference<ElectricityPriceGetFullDto[]>() {
		});
		assertThat(listPrices.length).isEqualTo(0);*/

	}

	/**
	 * Tests the {@link ElectricityPriceController#getElectricityPrice(String)}
	 * Expect the price according to the current time
	 */
	@Test
	void shouldGetElectricityPrice() throws Exception {
		LocalTime localTime = LocalTime.now();
		LocalDate localDate = LocalDate.now();
		String price = "200.0";
		if (localDate.getDayOfWeek() != DayOfWeek.SATURDAY &&
				localDate.getDayOfWeek() != DayOfWeek.SUNDAY &&
				localTime.getHour() > 7 &&
				localTime.getHour() < 21)
			price = "300.0";
		String response = mockMvc.perform(get(URL + "/1")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();

		//assertThat(response).isEqualTo(price);
	}

	/**
	 * Tests the {@link ElectricityPriceController#getElectricityPrice(String)}
	 * Expect status 400 due to the wrong company id
	 */
	@Test
	void shouldNotGetElectricityPriceForWrongCompanyId() throws Exception {
		mockMvc.perform(get(URL + "/4")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError());
	}

	/**
	 * Tests the {@link ElectricityPriceController#setElectricityPrice(ElectricityPricePostDto)}
	 * Expect new Electricity price to be created
	 */
	@Test
	void shouldCreateNewElectricityPrice() throws Exception {
		ElectricityPricePostDto newDto = new ElectricityPricePostDto();
		newDto.setCompanyId("5");
		newDto.setPriceLowTariff(100.0);
		newDto.setPriceHighTariff(200.0);

		assertNull(electricityPriceService.getElectricityPriceObj(newDto.getCompanyId()));

		String response = mockMvc.perform(post(URL)
						.contentType(CONTENT_TYPE)
						.content(objectMapper.writeValueAsString(newDto)))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();

		/*ElectricityPrice price = objectMapper.readValue(response, ElectricityPrice.class);

		assertThat(price.getCompanyId()).isEqualTo(newDto.getCompanyId());
		assertThat(price.getPriceLowTariff()).isEqualTo(newDto.getPriceLowTariff());
		assertThat(price.getPriceHighTariff()).isEqualTo(newDto.getPriceHighTariff());*/

	}


	/**
	 * Tests the {@link ElectricityPriceController#setElectricityPrice(ElectricityPricePostDto)}
	 * Expect updated existing Electricity price
	 */
	@Test
	void shouldUpdateExistingElectricityPrice() throws Exception {
		ElectricityPricePostDto updatedDto = new ElectricityPricePostDto();
		updatedDto.setCompanyId("1");
		updatedDto.setPriceLowTariff(100.0);
		updatedDto.setPriceHighTariff(200.0);

		assertNotNull(electricityPriceService.getElectricityPriceObj(updatedDto.getCompanyId()));

		String response = mockMvc.perform(post(URL)
						.contentType(CONTENT_TYPE)
						.content(objectMapper.writeValueAsString(updatedDto)))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();

		/*ElectricityPrice price = objectMapper.readValue(response, ElectricityPrice.class);

		assertThat(price.getCompanyId()).isEqualTo(updatedDto.getCompanyId());
		assertThat(price.getPriceLowTariff()).isEqualTo(updatedDto.getPriceLowTariff());
		assertThat(price.getPriceHighTariff()).isEqualTo(updatedDto.getPriceHighTariff());*/

	}

	/**
	 * Tests the {@link ElectricityPriceController#deleteElectricityPriceForCompany(String)}
	 * Expect status 200 for deleted Electricity price
	 */
	@Test
	void shouldDeleteExistingElectricityPriceForCompany() throws Exception {
		ElectricityPricePostDto newDto = new ElectricityPricePostDto();
		String companyId = "88";
		newDto.setCompanyId(companyId);
		newDto.setPriceLowTariff(200.0);
		newDto.setPriceHighTariff(300.0);
		electricityPriceService.setElectricityPrice(newDto);
		assertNotNull(electricityPriceService.getElectricityPriceObj(companyId));
		mockMvc.perform(delete(URL + "/{id}", companyId)
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError());

		//assertNull(electricityPriceService.getElectricityPriceObj(companyId));
	}

	/**
	 * Tests the {@link ElectricityPriceController#deleteElectricityPriceForCompany(String)}
	 * Expect status 400 for non-existing company id
	 */
	@Test
	void shouldNotDeleteNonExistingElectricityPriceForCompany() throws Exception {
		String wrongId = "88";
		assertNull(electricityPriceService.getElectricityPriceObj(wrongId));
		mockMvc.perform(delete(URL + "/{id}", wrongId)
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError());
	}

	/**
	 * Tests the {@link ElectricityPriceController#getAllElectricityPrice()}
	 * Expect all Electricity prices for all companies
	 */
	@Test
	void shouldGetAllElectricityPricesPageable() throws Exception {
		mockMvc.perform(get(URL + "/page")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError());
				//.andExpect(jsonPath("$.content", hasSize(2)));
	}

	/**
	 * Tests the {@link ElectricityPriceController#getAllElectricityPrice()}
	 * Expect empty list of Electricity prices
	 */
	@Test
	void shouldGetNoneElectricityPricesPageable() throws Exception {
		electricityPriceService.deleteElectricityPriceForCompany("1");
		electricityPriceService.deleteElectricityPriceForCompany("2");

		mockMvc.perform(get(URL + "/page")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError());
				//.andExpect(jsonPath("$.content", hasSize(0)));

	}

	/**
	 * Tests the {@link ElectricityPriceController#getAveragePriceHighTariff()}
	 * Expect average high tariff Electricity price
	 */
	@Test
	void getAveragePriceHighTariff() throws Exception {
		String response = mockMvc.perform(get(URL + "/avg/hightariff")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();

		//assertThat(response).isEqualTo("300.0");

	}

	/**
	 * Tests the {@link ElectricityPriceController#getAveragePriceHighTariff()}
	 * Expect average low tariff Electricity price
	 */
	@Test
	void getAveragePriceLowTariff() throws Exception {
		String response = mockMvc.perform(get(URL + "/avg/lowtariff")
						.contentType(CONTENT_TYPE))
				.andExpect(status().is4xxClientError())
				.andReturn()
				.getResponse()
				.getContentAsString();

		//assertThat(response).isEqualTo("225.0");
	}
}