package de.avatic.lcc.controller.calculation;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.avatic.lcc.dto.calculation.edit.SetDataDTO;
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.Collections;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.is;
import static org.hamcrest.Matchers.isA;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

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

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

    @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/premises-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    class SetSupplierTests {

        @BeforeEach
        void setUp() {
            premiseTestData.generateData();
        }

        @AfterEach
        void cleanUp() {
            premiseTestData.deleteAll();
        }

        @Test
        @DisplayName("PUT /api/calculation/supplier - happy path (update master data, no user node)")
        public void setSupplierTestCase1() throws Exception {

            var jj = testsHelper.getNodeIdByExternalMappingId("JJ");

            SetDataDTO dto = new SetDataDTO();
            dto.setPremiseId(Collections.singletonList(1));
            dto.setSupplierNodeId(jj);
            dto.setUserSupplierNode(false);
            dto.setUpdateMasterData(true);

            mockMvc.perform(put("/api/calculation/supplier")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk()).andDo(print())
                    .andExpect(jsonPath("$[0].supplier.external_mapping_id").value("JJ"))
                    .andExpect(jsonPath("$[0].handling_unit.length").value(1205.0))
                    .andExpect(jsonPath("$[0].handling_unit.width").value(705.0))
                    .andExpect(jsonPath("$[0].handling_unit.height").value(785.0));


        }


        @Test
        @DisplayName("PUT /api/calculation/supplier - happy path (no master data update, no user node)")
        public void setSupplierTestCase2() throws Exception {

            var jj = testsHelper.getNodeIdByExternalMappingId("JJ");

            SetDataDTO dto = new SetDataDTO();
            dto.setPremiseId(Collections.singletonList(1));
            dto.setSupplierNodeId(jj);
            dto.setUserSupplierNode(false);
            dto.setUpdateMasterData(false);

            mockMvc.perform(put("/api/calculation/supplier")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk()).andDo(print())
                    .andExpect(jsonPath("$[0].supplier.external_mapping_id").value("JJ"))
                    .andExpect(jsonPath("$[0].handling_unit.length").value(1200))
                    .andExpect(jsonPath("$[0].handling_unit.width").value(700.0))
                    .andExpect(jsonPath("$[0].handling_unit.height").value(400.0));
        }

        @Test
        @DisplayName("PUT /api/calculation/supplier - happy path (master data update, user node)")
        public void setSupplierTestCase3() throws Exception {

            var mySupplierNode1 = testsHelper.getUserNodeIdByName("My Supplier 1");

            SetDataDTO dto = new SetDataDTO();
            dto.setPremiseId(Collections.singletonList(1));
            dto.setSupplierNodeId(mySupplierNode1);
            dto.setUserSupplierNode(true);
            dto.setUpdateMasterData(true);

            mockMvc.perform(put("/api/calculation/supplier")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk()).andDo(print())
                    .andExpect(jsonPath("$[0].supplier.name").value("My Supplier 1"))
                    .andExpect(jsonPath("$[0].handling_unit.length").value(1200))
                    .andExpect(jsonPath("$[0].handling_unit.width").value(700.0))
                    .andExpect(jsonPath("$[0].handling_unit.height").value(400.0));

        }



    }

    @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/premises-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    class SetMaterialTests {

        @BeforeEach
        void setUp() {
            premiseTestData.generateData();
        }

        @AfterEach
        void cleanUp() {
            premiseTestData.deleteAll();
        }

        @Test
        @DisplayName("PUT /api/calculation/material - happy path (no master data update)")
        public void setMaterialTestCase1() throws Exception {

            var id = testsHelper.getMaterialIdByPartNumber("8222640822");

            SetDataDTO dto = new SetDataDTO();
            dto.setPremiseId(Collections.singletonList(1));
            dto.setMaterialId(id);
            dto.setUpdateMasterData(false);

            mockMvc.perform(put("/api/calculation/material")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk()).andDo(print())
                    .andExpect(jsonPath("$[0].material.part_number").value("8222640822"))
                    .andExpect(jsonPath("$[0].handling_unit.length").value(1200))
                    .andExpect(jsonPath("$[0].handling_unit.width").value(700.0))
                    .andExpect(jsonPath("$[0].handling_unit.height").value(400.0));
        }

        @Test
        @DisplayName("PUT /api/calculation/material - happy path (master data update)")
        public void setMaterialTestCase2() throws Exception {

            var id = testsHelper.getMaterialIdByPartNumber("8222640822");

            SetDataDTO dto = new SetDataDTO();
            dto.setPremiseId(Collections.singletonList(1));
            dto.setMaterialId(id);
            dto.setUpdateMasterData(true);

            mockMvc.perform(put("/api/calculation/material")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk()).andDo(print())
                    .andExpect(jsonPath("$[0].material.part_number").value("8222640822"))
                    .andExpect(jsonPath("$[0].handling_unit.length").value(1200))
                    .andExpect(jsonPath("$[0].handling_unit.width").value(800.0))
                    .andExpect(jsonPath("$[0].handling_unit.height").value(520.0))
                    .andExpect(jsonPath("$[0].handling_unit.weight").value(942.0));
        }
    }
}
