package com.braintree.strategy.impl;

import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.AbstractOrderEntryModel;
import de.hybris.platform.core.model.order.AbstractOrderModel;
import de.hybris.platform.ordersplitting.model.ConsignmentModel;
import de.hybris.platform.ordersplitting.model.WarehouseModel;
import de.hybris.platform.ordersplitting.strategy.impl.OrderEntryGroup;
import de.hybris.platform.store.BaseStoreModel;
import de.hybris.platform.storelocator.model.PointOfServiceModel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class BrainTreeSplitByWarehouseTest {

    private static final String WAREHOUSE_LIST_NAME = "WAREHOUSE_LIST";

    @Spy
    @InjectMocks
    private BrainTreeSplitByWarehouse unit;

    @Mock
    private OrderEntryGroup orderEntries;
    @Mock
    private List<WarehouseModel> warehouseModels;
    @Mock
    private WarehouseModel warehouseModel;
    @Mock
    private ConsignmentModel consignmentModel;
    @Mock
    private AbstractOrderEntryModel orderEntry;
    @Mock
    private AbstractOrderModel orderModel;
    @Mock
    private BaseStoreModel baseStoreModel;
    @Mock
    private PointOfServiceModel pointOfServiceModel;

    @Test
    public void shouldChooseBestWarehouse() {
        //given
        when(orderEntries.getParameter(WAREHOUSE_LIST_NAME)).thenReturn(warehouseModels);
        when(warehouseModels.get(anyInt())).thenReturn(warehouseModel);
        when(warehouseModels.size()).thenReturn(1);

        //when
        WarehouseModel result = unit.chooseBestWarehouse(orderEntries);

        //then
        assertEquals(warehouseModel, result);
    }

    @Test
    public void shouldReturnNullIfWarehouseListIsEmpty_WhenChooseBestWarehouse() {
        //given
        when(orderEntries.getParameter(WAREHOUSE_LIST_NAME)).thenReturn(warehouseModels);
        when(warehouseModels.isEmpty()).thenReturn(true);

        //when
        WarehouseModel result = unit.chooseBestWarehouse(orderEntries);

        //then
        assertNull(result);
    }

    @Test
    public void shouldReturnNullIfWarehouseListIsNull_WhenChooseBestWarehouse() {
        //given
        when(orderEntries.getParameter(WAREHOUSE_LIST_NAME)).thenReturn(null);

        //when
        WarehouseModel result = unit.chooseBestWarehouse(orderEntries);

        //then
        assertNull(result);
    }

    @Test
    public void shouldSortOrderEntryBeforeWarehouseSplitting() {
        //when
        OrderEntryGroup result = unit.sortOrderEntryBeforeWarehouseSplitting(orderEntries);

        //then
        assertEquals(orderEntries, result);
    }

    @Test
    public void shouldExecuteAfterSplitting() {
        //given
        doReturn(warehouseModel).when(unit).chooseBestWarehouse(orderEntries);

        //when
        unit.afterSplitting(orderEntries, consignmentModel);

        //then
        verify(consignmentModel).setWarehouse(warehouseModel);
    }

    @Test
    public void shouldPerform() {
        //given
        doReturn(List.of(orderEntries)).when(unit).splitForWarehouses(orderEntries);

        //when
        List<OrderEntryGroup> result = unit.perform(List.of(orderEntries));

        //then
        assertNotNull(result);
        assertFalse(result.isEmpty());
        assertEquals(1, result.size());
        assertEquals(orderEntries, result.get(0));
    }

    @Test
    public void shouldGetPossibleWarehousesFromStore() {
        //given
        List<WarehouseModel> warehouseModelList = List.of(warehouseModel);

        when(orderEntry.getOrder()).thenReturn(orderModel);
        when(orderModel.getStore()).thenReturn(baseStoreModel);
        when(baseStoreModel.getWarehouses()).thenReturn(warehouseModelList);

        //when
        List<WarehouseModel> result = unit.getPossibleWarehouses(orderEntry);

        //then
        assertEquals(warehouseModelList, result);
    }

    @Test
    public void shouldGetPossibleWarehousesFromDeliveryPointOfService() {
        //given
        List<WarehouseModel> warehouseModelList = List.of(warehouseModel);

        when(orderEntry.getOrder()).thenReturn(orderModel);
        when(orderModel.getStore()).thenReturn(baseStoreModel);
        when(orderEntry.getDeliveryPointOfService()).thenReturn(pointOfServiceModel);
        when(pointOfServiceModel.getWarehouses()).thenReturn(warehouseModelList);

        //when
        List<WarehouseModel> result = unit.getPossibleWarehouses(orderEntry);

        //then
        assertEquals(warehouseModelList, result);
    }

    @Test
    public void shouldReturnEmptyListIfStoreIsNullWhenGetPossibleWarehouses() {
        //given
        when(orderEntry.getOrder()).thenReturn(orderModel);

        //when
        List<WarehouseModel> result = unit.getPossibleWarehouses(orderEntry);

        //then
        assertNotNull(result);
        assertTrue(result.isEmpty());
    }

    @Test
    public void shouldSplitForWarehousesWhenNoWarehouseCanSolveOrderEntry() {
        //given
        OrderEntryGroup workingOrderEntryList = new OrderEntryGroup();
        workingOrderEntryList.add(orderEntry);

        OrderEntryGroup todoEntryList = new OrderEntryGroup();
        OrderEntryGroup emptyOrderEntryList = new OrderEntryGroup();

        when(orderEntries.getEmpty()).thenReturn(todoEntryList, emptyOrderEntryList);
        doReturn(workingOrderEntryList).when(unit).sortOrderEntryBeforeWarehouseSplitting(orderEntries);
        doReturn(Collections.EMPTY_LIST).when(unit).getPossibleWarehouses(orderEntry);

        //when
        List<OrderEntryGroup> result = unit.splitForWarehouses(orderEntries);

        //then
        assertNotNull(result);
        assertEquals(2, result.size());
    }

    @Test
    public void shouldSplitForWarehouses() {
        //given
        List<WarehouseModel> warehouseModelList = List.of(warehouseModel);

        OrderEntryGroup workingOrderEntryList = new OrderEntryGroup();
        workingOrderEntryList.add(orderEntry);

        OrderEntryGroup todoEntryList = new OrderEntryGroup();
        OrderEntryGroup emptyOrderEntryList = new OrderEntryGroup();

        when(orderEntries.getEmpty()).thenReturn(todoEntryList, emptyOrderEntryList);
        doReturn(workingOrderEntryList).when(unit).sortOrderEntryBeforeWarehouseSplitting(orderEntries);
        doReturn(warehouseModelList).when(unit).getPossibleWarehouses(orderEntry);

        //when
        List<OrderEntryGroup> result = unit.splitForWarehouses(orderEntries);

        //then
        assertNotNull(result);
        assertEquals(2, result.size());
    }

}
