package de.avatic.lcc.controller.calculation;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.avatic.lcc.dto.calculation.DestinationDTO;
import de.avatic.lcc.dto.calculation.edit.destination.DestinationCreateDTO;
import de.avatic.lcc.dto.calculation.edit.destination.DestinationUpdateDTO;
import org.junit.jupiter.api.*;
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.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
@Import({PremiseControllerTestData.class, PremiseTestsHelper.class})
public class CalculationIntegrationTests {

    @Autowired
    protected JdbcTemplate jdbcTemplate;
    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private PremiseTestsHelper testHelper;
    @Autowired
    private PremiseControllerTestData premiseTestData;

    @Nested
    @Sql(scripts = {"classpath:master_data/alldata.sql", "classpath:master_data/users.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
    @Sql(scripts = {"classpath:master_data/users-cleanup.sql", "classpath:master_data/alldata-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_CLASS)
    @Sql(scripts = {"classpath:master_data/premises.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
    @Sql(scripts = {"classpath:master_data/job-cleanup.sql", "classpath:master_data/premises-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    @DisplayName("POST /api/calculation/start - Start Calculations Tests")
    class StartCalculationTests {

        @Test
        @DisplayName("POST /api/calculation/start - happy path")
        public void startCalculationCase1() throws Exception {

            var nodeId = testHelper.getNodeIdByExternalMappingId("AB");
            var lx = testHelper.getNodeIdByExternalMappingId("LX");

            var premisesBeforeUpdate = testHelper.getPremisesFromDb();
            var premise1 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premise3 = premisesBeforeUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            var createDto = new DestinationCreateDTO();

            var map = new HashMap<Integer, List<Integer>>();
            map.put(premise1.getId(), List.of(nodeId));
            createDto.setDestinationNodeIds(map);


            var response = mockMvc.perform(post("/api/calculation/destination")
                    .content(objectMapper.writeValueAsString(createDto))
                    .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn();

            var destinations = objectMapper.readValue(response.getResponse().getContentAsString(), new TypeReference<Map<Integer, DestinationDTO>>() {
            });
            var destinationId = destinations.get(premise1.getId()).getId();
            var routeId = destinations.get(premise1.getId()).getRoutes().getFirst().getId();

            var updateDto = new DestinationUpdateDTO();
            updateDto.setAnnualAmount(100);
            updateDto.setRouteSelectedId(routeId);

            mockMvc.perform(put("/api/calculation/destination/" + destinationId)
                            .content(objectMapper.writeValueAsString(updateDto)).contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk());


            mockMvc.perform(put("/api/calculation/start")
                            .content(objectMapper.writeValueAsString(Arrays.asList(premise1.getId(), premise3.getId())))
                            .contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk()).andDo(print());

        }
    }

    //Test unselected route.
    //modifiy premise in database with invalid values.

}
