package de.avatic.lcc.controller.calculation;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.avatic.lcc.dto.calculation.create.CreatePremiseDTO;
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
import de.avatic.lcc.dto.calculation.edit.masterData.MaterialUpdateDTO;
import de.avatic.lcc.dto.calculation.edit.masterData.PackagingUpdateDTO;
import de.avatic.lcc.dto.calculation.edit.masterData.PriceUpdateDTO;
import de.avatic.lcc.dto.generic.DimensionDTO;
import de.avatic.lcc.model.db.premises.Premise;
import de.avatic.lcc.model.db.utils.DimensionUnit;
import de.avatic.lcc.model.db.utils.WeightUnit;
import org.assertj.core.api.InstanceOfAssertFactories;
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.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 PremiseControllerIntegrationTest {


    @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)
    @DisplayName("GET /api/calculation/view - View Calculations Tests")
    class ViewPremisesTests {

        // Test for GET /api/calculation/view
        @Test
        @DisplayName("GET /api/calculation/view - happy path (no filter)")
        public void viewMaterialTest() throws Exception {
            mockMvc.perform(get("/api/calculation/view")).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$", isA(List.class)));

            //TODO user login not implemented. user with id 1 is returned at the moment
        }


        @Test
        @DisplayName("GET /api/calculation/view - happy path (filtering part number)")
        public void viewMaterialFilterTest() throws Exception {
            mockMvc.perform(get("/api/calculation/view").param("filter", "28152640129")).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$", isA(List.class))).andExpect(jsonPath("$[*].material.part_number", everyItem(equalTo("28152640129"))));

            //TODO user login not implemented. user with id 1 is returned at the moment
        }

        @Test
        @DisplayName("GET /api/calculation/view - happy path (filtering supplier name)")
        public void viewMaterialFilterSupplierTest() throws Exception {
            mockMvc.perform(get("/api/calculation/view").param("filter", "My Supplier 1")).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$", isA(List.class))).andExpect(jsonPath("$[*].supplier.name").value(everyItem(equalTo("My Supplier 1"))));

            //TODO user login not implemented. user with id 1 is returned at the moment
        }

        //TODO filter done, archived, deleted, etc.
        // filter user id (check that this only works for admin)
        // material name
        // pagination
        // none happy path: negative pages, user id as non admin
    }

    @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)
    @DisplayName("GET /api/calculation/search - Search Tests")
    class SearchMaterialTests {

        // Test for GET /api/calculation/search
        @Test
        @DisplayName("GET /api/calculation/search - happy path (single)")
        public void searchSingleMaterialTest() throws Exception {
            mockMvc.perform(get("/api/calculation/search").param("search", "28152640129")).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$.materials", isA(List.class))).andExpect(jsonPath("$.materials", hasSize(1))).andExpect(jsonPath("$.supplier", hasSize(1))).andExpect(jsonPath("$.user_supplier", hasSize(1))).andExpect(jsonPath("$.user_supplier[?(@.name == 'My Supplier 1')]").exists()).andExpect(jsonPath("$.user_supplier[?(@.name == 'My Supplier 2')]").isEmpty());
        }


        @Test
        @DisplayName("GET /api/calculation/search - happy path (muliple)")
        public void searchMultiMaterialTest() throws Exception {
            mockMvc.perform(get("/api/calculation/search").param("search", "28152640129 Material 4222640803 bla4222640104bla bla")).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$.materials", isA(List.class))).andExpect(jsonPath("$.materials", hasSize(2))).andExpect(jsonPath("$.supplier", hasSize(1))).andExpect(jsonPath("$.user_supplier", hasSize(1))).andExpect(jsonPath("$.user_supplier[?(@.name == 'My Supplier 1')]").exists()).andExpect(jsonPath("$.supplier[?(@.name == 'Linde (China) Forklift Truck (Supplier)')]").exists()).andExpect(jsonPath("$.materials[?(@.part_number == '28152640129')]").exists()).andExpect(jsonPath("$.materials[?(@.part_number == '4222640803')]").exists());
        }

        //TODO Test with faulty param

    }

    /**
     * <h2>Testcases</h2>
     * <table>
     *         <thead>
     *             <tr>
     *                 <th>Testfall #</th>
     *                 <th>Descr.</th>
     *                 <th>Params</th>
     *                 <th>User</th>
     *                 <th>Result</th>
     *             </tr>
     *         </thead>
     *         <tbody>
     *             <tr>
     *                 <td>1</td>
     *                 <td>Copies COMPLETED with lowest age</td>
     *                 <td>material_ids: 1, supplier_ids: 1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>New Premise with Data from Premise #1</td>
     *             </tr>
     *             <tr>
     *                 <td>2</td>
     *                 <td>Recycles DRAFT from same user</td>
     *                 <td>material_ids: 2, supplier_ids: 1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>Returns Premise # 3</td>
     *             </tr>
     *             <tr>
     *                 <td>3</td>
     *                 <td>Recycles COMPLETED from other user</td>
     *                 <td>material_ids: 3, supplier_ids: 1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>New Premise with Data from Premise #7</td>
     *             </tr>
     *             <tr>
     *                 <td>4</td>
     *                 <td>Creates new Premise if no old data</td>
     *                 <td>material_ids: 4, supplier_ids: 1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>New Premise without Data</td>
     *             </tr>
     *             <tr>
     *                 <td>5</td>
     *                 <td>Uses UserSupplier DRAFT</td>
     *                 <td>material_ids: 1, supplier_ids: null, user_supplier_ids: 1, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>Returns Premise # 8</td>
     *             </tr>
     *             <tr>
     *                 <td>6</td>
     *                 <td>Access Violation UserSupplier</td>
     *                 <td>material_ids: 1, supplier_ids: null, user_supplier_ids: 2, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>Returns access violation</td>
     *             </tr>
     *             <tr>
     *                 <td>7</td>
     *                 <td>From Scratch behavior test</td>
     *                 <td>material_ids: [1,2,4], supplier_ids: 1, user_supplier_ids: null, from_scretch: true</td>
     *                 <td>1</td>
     *                 <td>Resets/deletes Premise #3, Creates new Premise for Material 1</td>
     *             </tr>
     *             <tr>
     *                 <td>8</td>
     *                 <td>Faulty material id</td>
     *                 <td>material_ids: -1, supplier_ids: 1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>400</td>
     *             </tr>
     *             <tr>
     *                 <td>9</td>
     *                 <td>Faulty supplier id</td>
     *                 <td>material_ids: 1, supplier_ids: -1, user_supplier_ids: null, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>400</td>
     *             </tr>
     *             <tr>
     *                 <td>10</td>
     *                 <td>Faulty supplier id</td>
     *                 <td>material_ids: 1, supplier_ids: null, user_supplier_ids: -1, from_scretch: false</td>
     *                 <td>1</td>
     *                 <td>400</td>
     *             </tr>
     *         </tbody>
     *     </table>
     *
     *
     *  <h2>Preconfigured Premises</h2>
     *     <table>
     * <tr>
     * <th>Premise #</th>
     * <th>Material ID</th>
     * <th>Node ID</th>
     * <th>User Node ID</th>
     * <th>State</th>
     * <th>Age</th>
     * <th>User</th>
     * </tr>
     * <tr>
     * <td>1</td>
     * <td>28152640129</td>
     * <td>LX</td>
     * <td></td>
     * <td>COMPLETED</td>
     * <td>1 Month</td>
     * <td>John Doe</td>
     * </tr>
     * <tr>
     * <td>2</td>
     * <td>28152640129</td>
     * <td>LX</td>
     * <td></td>
     * <td>COMPLETED</td>
     * <td>2 Month</td>
     * <td>John Doe</td>
     * </tr>
     * <tr>
     * <td>3</td>
     * <td>8222640822</td>
     * <td>LX</td>
     * <td></td>
     * <td>DRAFT</td>
     * <td>1 Month</td>
     * <td>John Doe</td>
     * </tr>
     * <tr>
     * <td>4</td>
     * <td>8222640822</td>
     * <td>LX</td>
     * <td></td>
     * <td>COMPLETED</td>
     * <td>1 Month</td>
     * <td>John Doe</td>
     * </tr>
     * <tr>
     * <td>5</td>
     * <td>8222640822</td>
     * <td>LX</td>
     * <td></td>
     * <td>DRAFT</td>
     * <td>1 Month</td>
     * <td>Sarah Smith</td>
     * </tr>
     * <tr>
     * <td>6</td>
     * <td>3064540201</td>
     * <td>LX</td>
     * <td></td>
     * <td>DRAFT</td>
     * <td>1 Month</td>
     * <td>Sarah Smith</td>
     * </tr>
     * <tr>
     * <td>7</td>
     * <td>3064540201</td>
     * <td>LX</td>
     * <td></td>
     * <td>COMPLETED</td>
     * <td>1 Month</td>
     * <td>Sarah Smith</td>
     * </tr>
     * <tr>
     * <td>8</td>
     * <td>28152640129</td>
     * <td></td>
     * <td>My Supplier 1</td>
     * <td>DRAFT</td>
     * <td>1 Month</td>
     * <td>John Doe</td>
     * </tr>
     * <tr>
     * <td>9</td>
     * <td>28152640129</td>
     * <td></td>
     * <td>My Supplier 2</td>
     * <td>COMPLETED</td>
     * <td>1 Month</td>
     * <td>Sarah Smith</td>
     * </tr>
     * </table>
     */
    @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)
    @DisplayName("POST /api/calculation/create - Create Calculations Tests")
    class CreatePremiseTests {

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

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


        @Test
        @DisplayName("POST /api/calculation/create - Copies COMPLETED with lowest age")
        public void createPremiseCase1() throws Exception {


            var supplierId = testsHelper.getNodeIdByExternalMappingId("LX");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("28152640129");

            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setSupplierIds(List.of(supplierId));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='28152640129')].handling_unit.content_unit_count", hasItem(2))) // content_unit_count is in the newest premise set to 2
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesBeforeCreate.stream().map(Premise::getId).toList()).asInstanceOf(InstanceOfAssertFactories.LIST).doesNotContain(dtos[0].getId());
            assertThat(premisesAfterCreate.stream().map(Premise::getId).toList()).asInstanceOf(InstanceOfAssertFactories.LIST).contains(dtos[0].getId());

            assertThat(premisesAfterCreate.stream().map(Premise::getId).toList()).asInstanceOf(InstanceOfAssertFactories.LIST).containsAll(premisesBeforeCreate.stream().map(Premise::getId).toList());
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size() + 1);


            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations().getFirst().getDestinationNode().getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(2).getName()).isEqualTo("Hamburg (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(2).getName()).isEqualTo("Zeebrugge (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");

        }

        @Test
        @DisplayName("POST /api/calculation/create - Recycles DRAFT from same user")
        public void createPremiseCase2() throws Exception {

            var supplierId = testsHelper.getNodeIdByExternalMappingId("LX");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("8222640822");


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setSupplierIds(List.of(supplierId));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='8222640822')].handling_unit.content_unit_count", hasItem(3))) // content_unit_count is in the correct permise set to 3
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesAfterCreate.size());

            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations().getFirst().getDestinationNode().getName()).isEqualTo("Châtellerault (KION plant)");

        }

        @Test
        @DisplayName("POST /api/calculation/create - Recycles COMPLETED from other user")
        public void createPremiseCase3() throws Exception {
            var supplierId = testsHelper.getNodeIdByExternalMappingId("LX");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("3064540201");


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setSupplierIds(List.of(supplierId));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='3064540201')].handling_unit.content_unit_count", hasItem(4))) // content_unit_count is in the correct premise set to 4
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size() + 1);

            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();
        }

        @Test
        @DisplayName("POST /api/calculation/create - Creates new Premise if no old data")
        public void createPremiseCase4() throws Exception {
            var supplierId = testsHelper.getNodeIdByExternalMappingId("LX");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("8212640113");


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setSupplierIds(List.of(supplierId));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='8212640113')].handling_unit.content_unit_count", hasItem(54))) // content_unit_count is the data from the packaging database.
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size() + 1);

            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();

        }

        @Test
        @DisplayName("POST /api/calculation/create - Uses UserSupplier DRAFT")
        public void createPremiseCase5() throws Exception {
            var supplierId = testsHelper.getUserNodeIdByName("My Supplier 1");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("28152640129");


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setUserSupplierIds(List.of(supplierId));
            dto.setSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='28152640129')].handling_unit.content_unit_count", hasItem(8))) // content_unit_count is the data from the packaging database.
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size());

            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();
        }

        @Test
        @DisplayName("POST /api/calculation/create - Access Violation UserSupplier")
        public void createPremiseCase6() throws Exception {
            var supplierId = testsHelper.getUserNodeIdByName("My Supplier 2");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();
            var materialId = getMaterialIdByPartNumber("28152640129");


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(materialId));
            dto.setUserSupplierIds(List.of(supplierId));
            dto.setSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isForbidden()).andDo(print()).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size());

        }

        @Test
        @DisplayName("POST /api/calculation/create - From Scratch behavior test")
        public void createPremiseCase7() throws Exception {
            var supplierId = testsHelper.getNodeIdByExternalMappingId("LX");
            var materialId1 = getMaterialIdByPartNumber("28152640129");
            var materialId2 = getMaterialIdByPartNumber("8222640822");
            var materialId4 = getMaterialIdByPartNumber("8212640113");
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(Arrays.asList(materialId1, materialId2, materialId4));
            dto.setSupplierIds(List.of(supplierId));
            dto.setUserSupplierIds(Collections.emptyList());
            dto.setCreateEmpty(true);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='28152640129')].handling_unit.content_unit_count", hasItem(1))) // content_unit_count is the data from the packaging database.
                    .andExpect(jsonPath("$[?(@.material.part_number=='8222640822')].handling_unit.content_unit_count", hasItem(630))) // content_unit_count is the data from the packaging database.
                    .andExpect(jsonPath("$[?(@.material.part_number=='8212640113')].handling_unit.content_unit_count", hasItem(54))) // content_unit_count is the data from the packaging database.
                    .andExpect(jsonPath("$", hasSize(3))).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);
            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size() + 2);

            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(1);
            assertThat(dtos[1].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();
            assertThat(dtos[2].getDestinations()).asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();
        }

        @Test
        @DisplayName("POST /api/calculation/create - Faulty material id")
        public void createPremiseCase8() throws Exception {

            var premisesBeforeCreate = testsHelper.getPremisesFromDb();


            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(-1));
            dto.setSupplierIds(List.of(1));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isBadRequest()).andDo(print()).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size());


        }

        @Test
        @DisplayName("POST /api/calculation/create - Faulty supplier id")
        public void createPremiseCase9() throws Exception {

            var premisesBeforeCreate = testsHelper.getPremisesFromDb();

            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(1));
            dto.setSupplierIds(List.of(-1));
            dto.setUserSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isBadRequest()).andDo(print()).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size());


        }

        @Test
        @DisplayName("POST /api/calculation/create - Faulty user supplier id")
        public void createPremiseCase10() throws Exception {
            var premisesBeforeCreate = testsHelper.getPremisesFromDb();

            var dto = new CreatePremiseDTO();
            dto.setMaterialIds(List.of(-1));
            dto.setUserSupplierIds(List.of(1));
            dto.setSupplierIds(null);


            var result = mockMvc.perform(post("/api/calculation/create").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(dto))).andExpect(status().isBadRequest()).andDo(print()).andReturn();

            var premisesAfterCreate = testsHelper.getPremisesFromDb();

            assertThat(premisesAfterCreate).asInstanceOf(InstanceOfAssertFactories.LIST).hasSize(premisesBeforeCreate.size());

        }


        private Integer getMaterialIdByPartNumber(String partNumber) {
            String query = "SELECT id FROM material WHERE part_number = ? LIMIT 1";
            return jdbcTemplate.queryForObject(query, Integer.class, partNumber);
        }

        private List<Integer> getPremisesWithSupplierId(Integer id) {
            String query = "SELECT id FROM premise WHERE supplier_node_id = ?";
            return jdbcTemplate.queryForList(query, Integer.class, id);
        }

        private Integer getSupplierIdByName(String name) {
            String query = "SELECT id FROM node WHERE name = ? LIMIT 1";
            return jdbcTemplate.queryForObject(query, Integer.class, name);
        }


    }

    @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)
    @DisplayName("GET /api/calculation/edit - Get Calculations Tests")
    class GetPremiseTests {
        @BeforeEach
        void setUp() {
            premiseTestData.generateData();
        }

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

        @Test
        @DisplayName("GET /api/calculation/edit - Get Single Premiss")
        public void getPremiseCase1() throws Exception {

            var premises = testsHelper.getPremisesFromDb();

            var premise1 = premises.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();


            var result = mockMvc.perform(get("/api/calculation/edit").content(Collections.singletonList(premise1.getId()).toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='28152640129')].handling_unit.content_unit_count", hasItem(2))) // content_unit_count is in the newest premise set to 2
                    .andExpect(jsonPath("$", hasSize(1))).andReturn();


            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);


            // check that destinations are copied from completed.
            assertThat(dtos[0].getDestinations().getFirst().getDestinationNode().getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(2).getName()).isEqualTo("Hamburg (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(2).getName()).isEqualTo("Zeebrugge (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");

        }

        @Test
        @DisplayName("GET /api/calculation/edit - Get Multiple Premisses")
        public void getPremiseCase2() throws Exception {

            var premises = testsHelper.getPremisesFromDb();

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

            var result = mockMvc.perform(get("/api/calculation/edit").content(Arrays.asList(premise1.getId(), premise3.getId()).toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[?(@.material.part_number=='28152640129')].handling_unit.content_unit_count", hasItem(2))).andExpect(jsonPath("$[?(@.material.part_number=='8222640822')].handling_unit.content_unit_count", hasItem(3))).andExpect(jsonPath("$", hasSize(2))).andReturn();


            var dtos = objectMapper.readValue(result.getResponse().getContentAsString(), PremiseDetailDTO[].class);

            assertThat(dtos[0].getDestinations().getFirst().getDestinationNode().getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().getFirst().getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(2).getName()).isEqualTo("Hamburg (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(0).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");

            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(0).getName()).isEqualTo("Linde (China) Forklift Truck (Supplier)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(1).getName()).isEqualTo("Xiamen (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(2).getName()).isEqualTo("Zeebrugge (Port)");
            assertThat(dtos[0].getDestinations().getFirst().getRoutes().get(1).getTransitNodes().get(3).getName()).isEqualTo("Aschaffenburg (KION plant)");


            assertThat(dtos[0].getDestinations().get(1).getDestinationNode().getName()).isEqualTo("Stříbro (KION plant)");
            assertThat(dtos[1].getDestinations().getFirst().getDestinationNode().getName()).isEqualTo("Châtellerault (KION plant)");

        }

    }


    @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)
    @DisplayName("POST /api/calculation/material - Update Material tests")
    class UpdateMaterialTests {


        @Test
        @DisplayName("POST /api/calculation/material - happy path (update tariff_rate)")
        public void updateMaterialCase1() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setTariffRate(13.5);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }

        @Test
        @DisplayName("POST /api/calculation/material - happy path (update hs_code)")
        public void updateMaterialCase2() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setHsCode("12345678");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo("12345678");
            assertThat(premiseAfter3.getHsCode()).isEqualTo("12345678");
            assertThat(premiseAfter8.getHsCode()).isEqualTo("12345678");

        }

        @Test
        @DisplayName("POST /api/calculation/material - happy path (update both)")
        public void updateMaterialCase3() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setTariffRate(13.5);
            dto.setHsCode("12345678");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(BigDecimal.valueOf(13.5));

            assertThat(premiseAfter1.getHsCode()).isEqualTo("12345678");
            assertThat(premiseAfter3.getHsCode()).isEqualTo("12345678");
            assertThat(premiseAfter8.getHsCode()).isEqualTo("12345678");

        }


        @Test
        @DisplayName("POST /api/calculation/material - error (invalid hs_code - to long)")
        public void updateMaterialCase4() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setHsCode("123456123456");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }

        @Test
        @DisplayName("POST /api/calculation/material - error (invalid hs_code - to short)")
        public void updateMaterialCase5() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setHsCode("12345");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }

        @Test
        @DisplayName("POST /api/calculation/material - error (invalid hs_code - invalid characters)")
        public void updateMaterialCase6() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setHsCode("123abc45");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }


        @Test
        @DisplayName("POST /api/calculation/material - error (invalid hs_code - negative number)")
        public void updateMaterialCase7() throws Exception {

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

            var dto = new MaterialUpdateDTO();
            dto.setHsCode("-12345678");
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }

        @Test
        @DisplayName("POST /api/calculation/material - error (invalid tariff_rate values)")
        public void updateMaterialCase8() throws Exception {

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

            // to many digits
            var dto = new MaterialUpdateDTO();
            dto.setTariffRate(13.54321);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            // negative
            dto = new MaterialUpdateDTO();
            dto.setTariffRate(-13.5);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            // to big
            dto = new MaterialUpdateDTO();
            dto.setTariffRate(13333.5);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            mockMvc.perform(post("/api/calculation/material").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getTariffRate()).isEqualByComparingTo(premise1.getTariffRate());
            assertThat(premiseAfter3.getTariffRate()).isEqualByComparingTo(premise3.getTariffRate());
            assertThat(premiseAfter8.getTariffRate()).isEqualByComparingTo(premise8.getTariffRate());

            assertThat(premiseAfter1.getHsCode()).isEqualTo(premise1.getHsCode());
            assertThat(premiseAfter3.getHsCode()).isEqualTo(premise3.getHsCode());
            assertThat(premiseAfter8.getHsCode()).isEqualTo(premise8.getHsCode());

        }


    }

    @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)
    @DisplayName("POST /api/calculation/packaging - Update Packaging tests")
    class UpdatePackagingTests {

        @Test
        @DisplayName("POST /api/calculation/packaging - happy path (update mixable)")
        public void updatePackagingCase1() throws Exception {

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

            var dto = new PackagingUpdateDTO();
            dto.setMixable(false);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            var result = mockMvc.perform(post("/api/calculation/packaging").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andReturn();


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getHuMixable()).isEqualTo(false);
            assertThat(premiseAfter3.getHuMixable()).isEqualTo(false);
            assertThat(premiseAfter8.getHuMixable()).isEqualTo(false);

            assertThat(premiseAfter1.getHuStackable()).isEqualTo(premise1.getHuStackable());
            assertThat(premiseAfter3.getHuStackable()).isEqualTo(premise3.getHuStackable());
            assertThat(premiseAfter8.getHuStackable()).isEqualTo(premise8.getHuStackable());

            assertThat(premiseAfter1.getIndividualHuWeight()).isEqualTo(premise1.getIndividualHuWeight());
            assertThat(premiseAfter1.getIndividualHuLength()).isEqualTo(premise1.getIndividualHuLength());
            assertThat(premiseAfter1.getIndividualHuWidth()).isEqualTo(premise1.getIndividualHuWidth());
            assertThat(premiseAfter1.getIndividualHuHeight()).isEqualTo(premise1.getIndividualHuHeight());
            assertThat(premiseAfter1.getHuDisplayedDimensionUnit()).isEqualTo(premise1.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter1.getHuDisplayedWeightUnit()).isEqualTo(premise1.getHuDisplayedWeightUnit());

            assertThat(premiseAfter3.getIndividualHuWeight()).isEqualTo(premise3.getIndividualHuWeight());
            assertThat(premiseAfter3.getIndividualHuLength()).isEqualTo(premise3.getIndividualHuLength());
            assertThat(premiseAfter3.getIndividualHuWidth()).isEqualTo(premise3.getIndividualHuWidth());
            assertThat(premiseAfter3.getIndividualHuHeight()).isEqualTo(premise3.getIndividualHuHeight());
            assertThat(premiseAfter3.getHuDisplayedDimensionUnit()).isEqualTo(premise3.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter3.getHuDisplayedWeightUnit()).isEqualTo(premise3.getHuDisplayedWeightUnit());

            assertThat(premiseAfter8.getIndividualHuWeight()).isEqualTo(premise8.getIndividualHuWeight());
            assertThat(premiseAfter8.getIndividualHuLength()).isEqualTo(premise8.getIndividualHuLength());
            assertThat(premiseAfter8.getIndividualHuWidth()).isEqualTo(premise8.getIndividualHuWidth());
            assertThat(premiseAfter8.getIndividualHuHeight()).isEqualTo(premise8.getIndividualHuHeight());
            assertThat(premiseAfter8.getHuDisplayedDimensionUnit()).isEqualTo(premise8.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter8.getHuDisplayedWeightUnit()).isEqualTo(premise8.getHuDisplayedWeightUnit());

        }

        @Test
        @DisplayName("POST /api/calculation/packaging - happy path (update stackable)")
        public void updatePackagingCase2() throws Exception {

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

            var dto = new PackagingUpdateDTO();
            dto.setStackable(!premise1.getHuStackable());
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            var result = mockMvc.perform(post("/api/calculation/packaging").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andReturn();


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getHuStackable()).isEqualTo(dto.getStackable());
            assertThat(premiseAfter3.getHuStackable()).isEqualTo(dto.getStackable());
            assertThat(premiseAfter8.getHuStackable()).isEqualTo(dto.getStackable());

            assertThat(premiseAfter1.getHuMixable()).isEqualTo(premise1.getHuMixable());
            assertThat(premiseAfter3.getHuMixable()).isEqualTo(premise3.getHuMixable());
            assertThat(premiseAfter8.getHuMixable()).isEqualTo(premise8.getHuMixable());

            assertThat(premiseAfter1.getIndividualHuWeight()).isEqualTo(premise1.getIndividualHuWeight());
            assertThat(premiseAfter1.getIndividualHuLength()).isEqualTo(premise1.getIndividualHuLength());
            assertThat(premiseAfter1.getIndividualHuWidth()).isEqualTo(premise1.getIndividualHuWidth());
            assertThat(premiseAfter1.getIndividualHuHeight()).isEqualTo(premise1.getIndividualHuHeight());
            assertThat(premiseAfter1.getHuDisplayedDimensionUnit()).isEqualTo(premise1.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter1.getHuDisplayedWeightUnit()).isEqualTo(premise1.getHuDisplayedWeightUnit());

            assertThat(premiseAfter3.getIndividualHuWeight()).isEqualTo(premise3.getIndividualHuWeight());
            assertThat(premiseAfter3.getIndividualHuLength()).isEqualTo(premise3.getIndividualHuLength());
            assertThat(premiseAfter3.getIndividualHuWidth()).isEqualTo(premise3.getIndividualHuWidth());
            assertThat(premiseAfter3.getIndividualHuHeight()).isEqualTo(premise3.getIndividualHuHeight());
            assertThat(premiseAfter3.getHuDisplayedDimensionUnit()).isEqualTo(premise3.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter3.getHuDisplayedWeightUnit()).isEqualTo(premise3.getHuDisplayedWeightUnit());

            assertThat(premiseAfter8.getIndividualHuWeight()).isEqualTo(premise8.getIndividualHuWeight());
            assertThat(premiseAfter8.getIndividualHuLength()).isEqualTo(premise8.getIndividualHuLength());
            assertThat(premiseAfter8.getIndividualHuWidth()).isEqualTo(premise8.getIndividualHuWidth());
            assertThat(premiseAfter8.getIndividualHuHeight()).isEqualTo(premise8.getIndividualHuHeight());
            assertThat(premiseAfter8.getHuDisplayedDimensionUnit()).isEqualTo(premise8.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter8.getHuDisplayedWeightUnit()).isEqualTo(premise8.getHuDisplayedWeightUnit());

        }

        @Test
        @DisplayName("POST /api/calculation/packaging - happy path (update dimensions)")
        public void updatePackagingCase3() throws Exception {

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

            var dimensions = new DimensionDTO();
            dimensions.setDimensionUnit(DimensionUnit.CM);
            dimensions.setWeightUnit(WeightUnit.KG);
            dimensions.setHeight(100.5);
            dimensions.setWidth(125.0);
            dimensions.setLength(70.0);
            dimensions.setWeight(55.5);

            var dto = new PackagingUpdateDTO();
            dto.setDimensions(dimensions);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            var result = mockMvc.perform(post("/api/calculation/packaging").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andReturn();

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getHuStackable()).isEqualTo(premise1.getHuStackable());
            assertThat(premiseAfter3.getHuStackable()).isEqualTo(premise3.getHuStackable());
            assertThat(premiseAfter8.getHuStackable()).isEqualTo(premise8.getHuStackable());

            assertThat(premiseAfter1.getHuMixable()).isEqualTo(premise1.getHuMixable());
            assertThat(premiseAfter3.getHuMixable()).isEqualTo(premise3.getHuMixable());
            assertThat(premiseAfter8.getHuMixable()).isEqualTo(premise8.getHuMixable());

            assertThat(premiseAfter1.getIndividualHuWeight()).isEqualTo(55500);
            assertThat(premiseAfter1.getIndividualHuLength()).isEqualTo(700);
            assertThat(premiseAfter1.getIndividualHuWidth()).isEqualTo(1250);
            assertThat(premiseAfter1.getIndividualHuHeight()).isEqualTo(1005);
            assertThat(premiseAfter1.getHuDisplayedDimensionUnit()).isEqualTo(DimensionUnit.CM);
            assertThat(premiseAfter1.getHuDisplayedWeightUnit()).isEqualTo(WeightUnit.KG);

            assertThat(premiseAfter3.getIndividualHuWeight()).isEqualTo(55500);
            assertThat(premiseAfter3.getIndividualHuLength()).isEqualTo(700);
            assertThat(premiseAfter3.getIndividualHuWidth()).isEqualTo(1250);
            assertThat(premiseAfter3.getIndividualHuHeight()).isEqualTo(1005);
            assertThat(premiseAfter3.getHuDisplayedDimensionUnit()).isEqualTo(DimensionUnit.CM);
            assertThat(premiseAfter3.getHuDisplayedWeightUnit()).isEqualTo(WeightUnit.KG);

            assertThat(premiseAfter8.getIndividualHuWeight()).isEqualTo(55500);
            assertThat(premiseAfter8.getIndividualHuLength()).isEqualTo(700);
            assertThat(premiseAfter8.getIndividualHuWidth()).isEqualTo(1250);
            assertThat(premiseAfter8.getIndividualHuHeight()).isEqualTo(1005);
            assertThat(premiseAfter8.getHuDisplayedDimensionUnit()).isEqualTo(DimensionUnit.CM);
            assertThat(premiseAfter8.getHuDisplayedWeightUnit()).isEqualTo(WeightUnit.KG);

        }

        @Test
        @DisplayName("POST /api/calculation/packaging - error (partial update dimensions)")
        public void updatePackagingCase4() throws Exception {

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

            var dimensions = new DimensionDTO();
            dimensions.setDimensionUnit(DimensionUnit.M);
            dimensions.setWeightUnit(WeightUnit.G);
            dimensions.setHeight(1.005);
            dimensions.setWidth(1.250);
            dimensions.setLength(null);
            dimensions.setWeight(55500.0);

            var dto = new PackagingUpdateDTO();
            dto.setDimensions(dimensions);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId(), premise8.getId()));


            var result = mockMvc.perform(post("/api/calculation/packaging").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print()).andReturn();

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();
            var premiseAfter8 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 8).findFirst().orElseThrow();

            assertThat(premiseAfter1.getHuStackable()).isEqualTo(premise1.getHuStackable());
            assertThat(premiseAfter3.getHuStackable()).isEqualTo(premise3.getHuStackable());
            assertThat(premiseAfter8.getHuStackable()).isEqualTo(premise8.getHuStackable());

            assertThat(premiseAfter1.getHuMixable()).isEqualTo(premise1.getHuMixable());
            assertThat(premiseAfter3.getHuMixable()).isEqualTo(premise3.getHuMixable());
            assertThat(premiseAfter8.getHuMixable()).isEqualTo(premise8.getHuMixable());

            assertThat(premiseAfter1.getIndividualHuWeight()).isEqualTo(premise1.getIndividualHuWeight());
            assertThat(premiseAfter1.getIndividualHuLength()).isEqualTo(premise1.getIndividualHuLength());
            assertThat(premiseAfter1.getIndividualHuWidth()).isEqualTo(premise1.getIndividualHuWidth());
            assertThat(premiseAfter1.getIndividualHuHeight()).isEqualTo(premise1.getIndividualHuHeight());
            assertThat(premiseAfter1.getHuDisplayedDimensionUnit()).isEqualTo(premise1.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter1.getHuDisplayedWeightUnit()).isEqualTo(premise1.getHuDisplayedWeightUnit());

            assertThat(premiseAfter3.getIndividualHuWeight()).isEqualTo(premise3.getIndividualHuWeight());
            assertThat(premiseAfter3.getIndividualHuLength()).isEqualTo(premise3.getIndividualHuLength());
            assertThat(premiseAfter3.getIndividualHuWidth()).isEqualTo(premise3.getIndividualHuWidth());
            assertThat(premiseAfter3.getIndividualHuHeight()).isEqualTo(premise3.getIndividualHuHeight());
            assertThat(premiseAfter3.getHuDisplayedDimensionUnit()).isEqualTo(premise3.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter3.getHuDisplayedWeightUnit()).isEqualTo(premise3.getHuDisplayedWeightUnit());

            assertThat(premiseAfter8.getIndividualHuWeight()).isEqualTo(premise8.getIndividualHuWeight());
            assertThat(premiseAfter8.getIndividualHuLength()).isEqualTo(premise8.getIndividualHuLength());
            assertThat(premiseAfter8.getIndividualHuWidth()).isEqualTo(premise8.getIndividualHuWidth());
            assertThat(premiseAfter8.getIndividualHuHeight()).isEqualTo(premise8.getIndividualHuHeight());
            assertThat(premiseAfter8.getHuDisplayedDimensionUnit()).isEqualTo(premise8.getHuDisplayedDimensionUnit());
            assertThat(premiseAfter8.getHuDisplayedWeightUnit()).isEqualTo(premise8.getHuDisplayedWeightUnit());

        }

        @Test
        @DisplayName("POST /api/calculation/packaging - happy path (API read-back)")
        public void updatePackagingCase5() throws Exception {

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


            var dimensions = new DimensionDTO();
            dimensions.setDimensionUnit(DimensionUnit.M);
            dimensions.setWeightUnit(WeightUnit.G);
            dimensions.setHeight(1.005);
            dimensions.setWidth(1.250);
            dimensions.setLength(.70);
            dimensions.setWeight(55500.0);

            var dto = new PackagingUpdateDTO();
            dto.setDimensions(dimensions);
            dto.setPremiseIds(Collections.singletonList(premise1.getId()));


            mockMvc.perform(post("/api/calculation/packaging").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andReturn();

            mockMvc.perform(get("/api/calculation/edit").content(Collections.singletonList(premise1.getId()).toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print()).andExpect(jsonPath("$[0].handling_unit.weight").value(55500.0)).andExpect(jsonPath("$[0].handling_unit.height").value(1.005)).andExpect(jsonPath("$[0].handling_unit.width").value(1.250)).andExpect(jsonPath("$[0]['handling_unit']['length']").value(0.7));

        }


    }

    @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)
    @DisplayName("POST /api/calculation/price - Update price tests")
    class UpdatePriceTests {

        @Test
        @DisplayName("POST /api/calculation/price - happy path (update price only)")
        public void updatePriceCase1() throws Exception {

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


            var dto = new PriceUpdateDTO();
            dto.setPrice(12.34);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(BigDecimal.valueOf(12.34));
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(BigDecimal.valueOf(12.34));

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(premise1.getOverseaShare());
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(premise3.getOverseaShare());

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(premise1.getFcaEnabled());
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(premise3.getFcaEnabled());


        }

        @Test
        @DisplayName("POST /api/calculation/price - error case (set invalid prices)")
        public void updatePriceCase2() throws Exception {

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


            // to many fractions
            var dto = new PriceUpdateDTO();
            dto.setPrice(12.3456);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));

            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            // negative price
            dto = new PriceUpdateDTO();
            dto.setPrice(-12.34);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));

            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            // price == 0
            dto = new PriceUpdateDTO();
            dto.setPrice(0.00);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));

            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());

            // check if database is untouched
            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(premise1.getMaterialCost());
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(premise3.getMaterialCost());

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(premise1.getOverseaShare());
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(premise3.getOverseaShare());

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(premise1.getFcaEnabled());
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(premise3.getFcaEnabled());

        }

        @Test
        @DisplayName("POST /api/calculation/price - happy path (update oversea share only)")
        public void updatePriceCase4() throws Exception {

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


            var dto = new PriceUpdateDTO();
            dto.setOverseaShare(60.06);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(premise1.getMaterialCost());
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(premise3.getMaterialCost());

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(BigDecimal.valueOf(60.06));
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(BigDecimal.valueOf(60.06));

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(premise1.getFcaEnabled());
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(premise3.getFcaEnabled());


        }

        @Test
        @DisplayName("POST /api/calculation/price - error case (set invalid oversea share)")
        public void updatePriceCase5() throws Exception {

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


            // negative value
            var dto = new PriceUpdateDTO();
            dto.setOverseaShare(-60.06);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());


            //value to high
            dto = new PriceUpdateDTO();
            dto.setOverseaShare(60000.06);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()).andDo(print());


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(premise1.getMaterialCost());
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(premise3.getMaterialCost());

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(premise1.getOverseaShare());
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(premise3.getOverseaShare());

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(premise1.getFcaEnabled());
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(premise3.getFcaEnabled());


        }

        @Test
        @DisplayName("POST /api/calculation/price - happy path (update include fca fee)")
        public void updatePriceCase6() throws Exception {

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

            var dto = new PriceUpdateDTO();
            dto.setIncludeFcaFee(true);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));

            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());

            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(premise1.getMaterialCost());
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(premise3.getMaterialCost());

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(premise1.getOverseaShare());
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(premise3.getOverseaShare());

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(true);
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(true);

            dto = new PriceUpdateDTO();
            dto.setIncludeFcaFee(false);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());


            premisesAfterUpdate = testsHelper.getPremisesFromDb();

            premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(premise1.getMaterialCost());
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(premise3.getMaterialCost());

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(premise1.getOverseaShare());
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(premise3.getOverseaShare());

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(false);
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(false);


        }

        @Test
        @DisplayName("POST /api/calculation/price - happy path (update all values)")
        public void updatePriceCase7() throws Exception {

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


            var dto = new PriceUpdateDTO();
            dto.setPrice(12.34);
            dto.setOverseaShare(60.06);
            dto.setIncludeFcaFee(true);
            dto.setPremiseIds(Arrays.asList(premise1.getId(), premise3.getId()));


            mockMvc.perform(post("/api/calculation/price").content(objectMapper.writeValueAsString(dto)).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andDo(print());


            var premisesAfterUpdate = testsHelper.getPremisesFromDb();

            var premiseAfter1 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 2).findFirst().orElseThrow();
            var premiseAfter3 = premisesAfterUpdate.stream().filter(p -> p.getHuUnitCount() == 3).findFirst().orElseThrow();

            assertThat(premiseAfter1.getMaterialCost()).isEqualTo(BigDecimal.valueOf(12.34));
            assertThat(premiseAfter3.getMaterialCost()).isEqualTo(BigDecimal.valueOf(12.34));

            assertThat(premiseAfter1.getOverseaShare()).isEqualByComparingTo(BigDecimal.valueOf(60.06));
            assertThat(premiseAfter3.getOverseaShare()).isEqualByComparingTo(BigDecimal.valueOf(60.06));

            assertThat(premiseAfter1.getFcaEnabled()).isEqualTo(true);
            assertThat(premiseAfter3.getFcaEnabled()).isEqualTo(true);


        }

    }




//    @Nested
//    class claudeTests {
//
//    // Test for GET /api/calculation/view
//    @Test
//    @Sql(scripts = {"classpath:master_data/alldata.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:master_data/alldata-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testListPremises_WithValidFilter_ReturnsFilteredResults() throws Exception {
//        mockMvc.perform(get("/api/calculation/view")
//                        .param("filter", "test")
//                        .param("page", "1")
//                        .param("limit", "10")
//                        .param("deleted", "false")
//                        .param("archived", "false")
//                        .param("done", "true"))
//                .andExpect(status().isOk())
//                .andExpect(header().exists("X-Total-Count"))
//                .andExpect(header().exists("X-Page-Count"))
//                .andExpect(header().string("X-Current-Page", "1"))
//                .andExpect(jsonPath("$", isA(List.class)))
//                .andExpect(jsonPath("$[0].id").exists())
//                .andExpect(jsonPath("$[0].material").exists())
//                .andExpect(jsonPath("$[0].supplier").exists())
//                .andExpect(jsonPath("$[0][?(@.state in ['draft', 'completed', 'archived', 'deleted'])]").exists());
//    }
//
//    @Test
//    public void testListPremises_WithMinimalParameters_ReturnsResults() throws Exception {
//        mockMvc.perform(get("/api/calculation/view")
//                        .param("filter", ""))
//                .andExpect(status().isOk())
//                .andExpect(header().exists("X-Total-Count"))
//                .andExpect(jsonPath("$", isA(List.class)));
//    }
//
//    @Test
//    public void testListPremises_WithInvalidPageParameter_ReturnsBadRequest() throws Exception {
//        mockMvc.perform(get("/api/calculation/view")
//                        .param("filter", "test")
//                        .param("page", "0")) // Min value is 1
//                .andExpect(status().isBadRequest());
//    }
//
//    // Test for GET /api/calculation/search
//    @Test
//    @Sql(scripts = {"classpath:test-data/material-supplier-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/material-supplier-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testSearchMaterialsAndSuppliers_WithValidSearch_ReturnsResults() throws Exception {
//        mockMvc.perform(get("/api/calculation/search")
//                        .param("search", "test material"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$.materials", isA(List.class)))
//                .andExpect(jsonPath("$.supplier", isA(List.class)))
//                .andExpect(jsonPath("$.user_supplier", isA(List.class)));
//    }
//
//    @Test
//    public void testSearchMaterialsAndSuppliers_WithEmptySearch_ReturnsEmptyResults() throws Exception {
//        mockMvc.perform(get("/api/calculation/search")
//                        .param("search", ""))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$.materials", empty()))
//                .andExpect(jsonPath("$.supplier", empty()))
//                .andExpect(jsonPath("$.user_supplier", empty()));
//    }
//
//    // Test for POST /api/calculation/create
//    @Test
//    @Sql(scripts = {"classpath:test-data/create-premise-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/create-premise-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testCreatePremises_WithValidData_ReturnsCreatedPremises() throws Exception {
//        mockMvc.perform(post("/api/calculation/create")
//                        .param("material", "1", "2")
//                        .param("supplier", "1", "2")
//                        .param("user_supplier", "1")
//                        .param("from_scratch", "false"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$", isA(List.class)))
//                .andExpect(jsonPath("$[0].id").exists())
//                .andExpect(jsonPath("$[0].material").exists())
//                .andExpect(jsonPath("$[0].supplier").exists());
//    }
//
//    @Test
//    public void testCreatePremises_FromScratch_ReturnsEmptyPremises() throws Exception {
//        mockMvc.perform(post("/api/calculation/create")
//                        .param("material", "")
//                        .param("supplier", "")
//                        .param("user_supplier", "")
//                        .param("from_scratch", "true"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$", isA(List.class)));
//    }
//
//    // Test for GET /api/calculation/edit
//    @Test
//    @Sql(scripts = {"classpath:test-data/premise-detail-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/premise-detail-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testGetPremises_WithValidIds_ReturnsDetailedPremises() throws Exception {
//        mockMvc.perform(get("/api/calculation/edit")
//                        .param("premiss_ids", "1", "2"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$", hasSize(2)))
//                .andExpect(jsonPath("$[0].id").exists())
//                .andExpect(jsonPath("$[0].material.hs_code").exists())
//                .andExpect(jsonPath("$[0].supplier.location").exists())
//                .andExpect(jsonPath("$[0].destinations").exists())
//                .andExpect(jsonPath("$[0].hu").exists());
//    }
//
//    @Test
//    public void testGetPremises_WithNonExistentIds_ReturnsEmptyList() throws Exception {
//        mockMvc.perform(get("/api/calculation/edit")
//                        .param("premiss_ids", "999999"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$", empty()));
//    }
//
//    // Test for PUT /api/calculation/start
//    @Test
//    @Sql(scripts = {"classpath:test-data/calculation-start-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/calculation-start-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testStartCalculation_WithValidIds_ReturnsProcessingId() throws Exception {
//        List<Integer> premiseIds = Arrays.asList(1, 2, 3);
//
//        mockMvc.perform(put("/api/calculation/start")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(premiseIds)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isNumber());
//    }
//
//    // Test for GET /api/calculation/status/{processing_id}
//    @Test
//    @Sql(scripts = {"classpath:test-data/calculation-status-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/calculation-status-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testGetCalculationStatus_WithValidId_ReturnsStatus() throws Exception {
//        mockMvc.perform(get("/api/calculation/status/1"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$.status").exists())
//                .andExpect(jsonPath("$.progress").exists());
//    }
//
//    @Test
//    public void testGetCalculationStatus_WithInvalidId_ReturnsNotFound() throws Exception {
//        mockMvc.perform(get("/api/calculation/status/999999"))
//                .andExpect(status().isNotFound());
//    }
//
//    // Test for PUT /api/calculation/packaging
//    @Test
//    @Sql(scripts = {"classpath:test-data/packaging-update-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/packaging-update-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testUpdatePackaging_WithValidData_ReturnsSuccess() throws Exception {
//        PackagingUpdateDTO packagingDTO = new PackagingUpdateDTO();
//
//        DimensionDTO dimension = new DimensionDTO();
//        dimension.setLength(100.0);
//        dimension.setWidth(80.0);
//        dimension.setHeight(60.0);
//        dimension.setDimensionUnit(DimensionUnit.CM);
//        dimension.setWeight(10.0);
//        dimension.setWeightUnit(WeightUnit.KG);
//        dimension.setContentUnitCount(20);
//
//        packagingDTO.setDimensions(dimension);
//        packagingDTO.setPremiseIds(Arrays.asList(1, 2));
//        packagingDTO.setMixable(true);
//        packagingDTO.setMixable(true);
//
//        mockMvc.perform(put("/api/calculation/packaging")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(packagingDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isMap());
//    }
//
//    // Test for POST /api/calculation/material (update)
//    @Test
//    @Sql(scripts = {"classpath:test-data/material-update-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/material-update-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testUpdateMaterial_WithValidData_ReturnsSuccess() throws Exception {
//        MaterialUpdateDTO materialUpdateDTO = new MaterialUpdateDTO();
//        materialUpdateDTO.setPremiseIds(Arrays.asList(1, 2));
//
//        materialUpdateDTO.setHsCode("12345678");
//        materialUpdateDTO.setTariffRate(new BigDecimal("0.05"));
//
//
//        mockMvc.perform(post("/api/calculation/material")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(materialUpdateDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isMap());
//    }
//
//    // Test for PUT /api/calculation/price
//    @Test
//    @Sql(scripts = {"classpath:test-data/price-update-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/price-update-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testUpdatePrice_WithValidData_ReturnsSuccess() throws Exception {
//        PriceUpdateDTO priceUpdateDTO = new PriceUpdateDTO();
//        priceUpdateDTO.setPremiseIds(Arrays.asList(1, 2));
//
//
//        priceUpdateDTO.setPrice(new BigDecimal("100.50"));
//        priceUpdateDTO.setOverseaShare(new BigDecimal("0.25"));
//        priceUpdateDTO.setIncludeFcaFee(true);
//
//
//        mockMvc.perform(put("/api/calculation/price")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(priceUpdateDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isMap());
//    }
//
//    // Test for POST /api/calculation/destination
//    @Test
//    @Sql(scripts = {"classpath:test-data/destination-create-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/destination-create-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testCreateDestination_WithValidData_ReturnsCreatedDestinations() throws Exception {
//        DestinationCreateDTO destinationCreateDTO = new DestinationCreateDTO();
//        destinationCreateDTO.setPremiseId(Arrays.asList(1, 2));
//        destinationCreateDTO.setDestinationNodeId(10);
//
//        mockMvc.perform(post("/api/calculation/destination")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(destinationCreateDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isMap())
//                .andExpect(jsonPath("$.1").exists())
//                .andExpect(jsonPath("$.1.destination_node").exists())
//                .andExpect(jsonPath("$.1.routes").isArray());
//    }
//
//    // Test for GET /api/calculation/destination/{id}
//    @Test
//    @Sql(scripts = {"classpath:test-data/destination-get-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/destination-get-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testGetDestination_WithValidId_ReturnsDestination() throws Exception {
//        mockMvc.perform(get("/api/calculation/destination/1"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$.id").value("1"))
//                .andExpect(jsonPath("$.country").exists())
//                .andExpect(jsonPath("$.name").exists())
//                .andExpect(jsonPath("$.routes").isArray());
//    }
//
//    @Test
//    public void testGetDestination_WithInvalidId_ReturnsNotFound() throws Exception {
//        mockMvc.perform(get("/api/calculation/destination/999999"))
//                .andExpect(status().isNotFound());
//    }
//
//    // Test for PUT /api/calculation/destination/{id}
//    @Test
//    @Sql(scripts = {"classpath:test-data/destination-update-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/destination-update-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testUpdateDestination_WithValidData_ReturnsSuccess() throws Exception {
//        DestinationUpdateDTO updateDTO = new DestinationUpdateDTO();
//        updateDTO.setRepackingCost(new BigDecimal("50.00"));
//        updateDTO.setHandlingCost(new BigDecimal("25.00"));
//        updateDTO.setDisposalCost(new BigDecimal("10.00"));
//        updateDTO.setRouteSelectedId(1);
//
//        mockMvc.perform(put("/api/calculation/destination/1")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(updateDTO)))
//                .andExpect(status().isOk());
//    }
//
//    // Test for DELETE /api/calculation/destination/{id}
//    @Test
//    @Sql(scripts = {"classpath:test-data/destination-delete-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/destination-delete-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testDeleteDestination_WithValidId_ReturnsSuccess() throws Exception {
//        mockMvc.perform(delete("/api/calculation/destination/1"))
//                .andExpect(status().isOk());
//    }
//
//    @Test
//    public void testDeleteDestination_WithInvalidId_ReturnsNotFound() throws Exception {
//        mockMvc.perform(delete("/api/calculation/destination/999999"))
//                .andExpect(status().isNotFound());
//    }
//
//    // Test for PUT /api/calculation/supplier
//    @Test
//    @Sql(scripts = {"classpath:test-data/supplier-set-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/supplier-set-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testSetSupplier_WithValidData_ReturnsUpdatedPremises() throws Exception {
//        SetDataDTO setSupplierDTO = new SetDataDTO();
//        setSupplierDTO.setPremiseId(Arrays.asList(1, 2));
//        setSupplierDTO.setUpdateMasterData(true);
//        setSupplierDTO.setSupplierNodeId(5);
//
//        mockMvc.perform(put("/api/calculation/supplier")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(setSupplierDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isArray())
//                .andExpect(jsonPath("$[0].supplier.id").value("5"));
//    }
//
//    // Test for PUT /api/calculation/material (set)
//    @Test
//    @Sql(scripts = {"classpath:test-data/material-set-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/material-set-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testSetMaterial_WithValidData_ReturnsUpdatedPremises() throws Exception {
//        SetDataDTO setMaterialDTO = new SetDataDTO();
//        setMaterialDTO.setPremiseId(Arrays.asList(1, 2));
//        setMaterialDTO.setUpdateMasterData(false);
//        setMaterialDTO.setMaterialId(3);
//
//        mockMvc.perform(put("/api/calculation/material")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content(objectMapper.writeValueAsString(setMaterialDTO)))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").isArray())
//                .andExpect(jsonPath("$[0].material.id").value("3"));
//    }
//
//    // Edge Cases and Error Handling Tests
//    @Test
//    public void testEndpoint_WithMissingRequiredParameter_ReturnsBadRequest() throws Exception {
//        mockMvc.perform(get("/api/calculation/view"))
//                // Missing required 'filter' parameter
//                .andExpect(status().isBadRequest());
//    }
//
//    @Test
//    public void testEndpoint_WithInvalidJSON_ReturnsBadRequest() throws Exception {
//        mockMvc.perform(put("/api/calculation/start")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content("invalid json"))
//                .andExpect(status().isBadRequest());
//    }
//
//    @Test
//    public void testEndpoint_WithEmptyList_ReturnsAppropriateResponse() throws Exception {
//        mockMvc.perform(put("/api/calculation/start")
//                        .contentType(MediaType.APPLICATION_JSON)
//                        .content("[]"))
//                .andExpect(status().isOk())
//                .andExpect(jsonPath("$").value(0));
//    }
//
//    // Performance Test (simplified)
//    @Test
//    @Sql(scripts = {"classpath:test-data/performance-test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
//    @Sql(scripts = {"classpath:test-data/performance-cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
//    public void testListPremises_WithLargeDataset_PerformsWithinAcceptableTime() throws Exception {
//        long startTime = System.currentTimeMillis();
//
//        mockMvc.perform(get("/api/calculation/view")
//                        .param("filter", "")
//                        .param("page", "1")
//                        .param("limit", "100"))
//                .andExpect(status().isOk());
//
//        long endTime = System.currentTimeMillis();
//        long duration = endTime - startTime;
//
//        // Assert that the request completes within 2 seconds
//        assert duration < 2000 : "Request took too long: " + duration + "ms";
//    }
//}
}