package com.braintree.customersupportbackoffice.widgets.order.adjustauthorization;

import com.braintree.command.result.BrainTreeAdjustedAuthorizationResult;
import com.braintree.facade.backoffice.BraintreeBackofficeAdjustAuthorizationFacade;
import com.braintree.facade.backoffice.BraintreeBackofficeMultiCaptureFacade;
import com.braintree.transaction.service.BrainTreePaymentTransactionService;
import com.hybris.cockpitng.engine.WidgetInstanceManager;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.basecommerce.model.site.BaseSiteModel;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.core.model.user.UserModel;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.servicelayer.session.SessionExecutionBody;
import de.hybris.platform.servicelayer.session.SessionService;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Textbox;

import java.math.BigDecimal;
import java.util.List;

import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.OrderManagementActions.ADJUST_AUTHORIZATION_ERROR;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.OrderManagementActions.ADJUST_AUTHORIZATION_SUCCESS;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.OrderManagementActions.ADJUST_AUTHORIZATION_TITLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class BrainTreeAdjustAuthorizationControllerTest {
    private static final String ADJUSTAUTHORIZATION_CONFIRM_TITLE = "braintreecustomersupportbackoffice.adjustauthorization.confirm.title";
    private static final String ADJUSTAUTHORIZATION_CONFIRM_TITLE_LABEL = "ADJUSTAUTHORIZATION_CONFIRM_TITLE_LABEL";
    private static final String TEST_COMBOITEM_LABEL = "TEST_COMBOITEM_LABEL";
    private static final String ORDER_AMOUNT_ERROR_EMPTY = "bt.customersupport.order.adjust.authorization.amount.error.empty";
    private static final String ORDER_AMOUNT_ERROR_EMPTY_LABEL = "ORDER_AMOUNT_ERROR_EMPTY_LABEL";
    private static final String ORDER_AMOUNT_ERROR_ZERO = "bt.customersupport.order.adjust.authorization.amount.error.zero";
    private static final String ORDER_AMOUNT_ERROR_ZERO_LABEL = "ORDER_AMOUNT_ERROR_ZERO_LABEL";
    private static final String ADJUST_AUTHORIZATION_SUCCESS_LABEL = "ADJUST_AUTHORIZATION_SUCCESS_LABEL";
    private static final String ADJUST_AUTHORIZATION_TITLE_LABEL = "ADJUST_AUTHORIZATION_TITLE_LABEL";
    private static final String ORDER_ADJUST_AUTHORIZATION_AMOUNT_ERROR_EQUAL = "bt.customersupport.order.adjust.authorization.amount.error.equal";
    private static final String ORDER_ADJUST_AUTHORIZATION_AMOUNT_ERROR_EQUAL_LABEL = "ORDER_ADJUST_AUTHORIZATION_AMOUNT_ERROR_LABEL";
    private static final String ORDER_AMOUNT_ERROR_NUMBER_FORMAT = "bt.customersupport.order.adjust.authorization.amount.error.number.format";
    private static final String ORDER_AMOUNT_ERROR_NUMBER_FORMAT_LABEL = "ORDER_AMOUNT_ERROR_NUMBER_FORMAT_LABEL";
    private static final String ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE_LABEL = "ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE_LABEL";
    private static final String ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE = "bt.customersupport.widget.order.adjust.authorization.error.title";
    private static final String TEST_ERROR_CODE = "TEST_ERROR_CODE";
    private static final String TEST_ADJUSTED_ERR_MSG = "TEST_ADJUSTED_ERR_MSG";
    private static final String TEST_ORDER_CODE = "TEST_ORDER_CODE";
    private static final String TEST_DISPLAY_NAME = "TEST_DISPLAY_NAME";
    private final String TEST_ORIGINAL_TRANSACTION_AMOUNT = "5";
    private final String TEST_ADJUSTED_AMOUNT = "7";
    private final String TEST_ADJUSTED_FORMATED_AMOUNT = "7.00";
    private static final String OUT_MODIFIED_ITEM = "modifiedItem";
    private static final String ERROR_CODE = "Error code ";
    private final int ZERO_INDEX = 0;
    @Mock
    private SessionService sessionService;
    @Mock
    private WidgetInstanceManager widgetInstanceManager;
    @Mock
    private Comboitem comboitem;
    @Spy
    private OrderModel mockedOrder;
    @Mock
    private OrderModel order;
    @Spy
    private UserModel mockedUser;
    @Mock
    private BraintreeBackofficeAdjustAuthorizationFacade braintreeBackofficeAdjustAuthorizationFacade;
    @Mock
    private Combobox transactions;
    @Mock
    private Textbox amount;
    @Mock
    private Textbox customer;
    @Mock
    private Textbox orderCode;
    @Mock
    private Textbox originalAuthorizationAmount;
    @Mock
    private BaseSiteModel baseSiteModel;
    @Mock
    private BraintreeBackofficeMultiCaptureFacade braintreeBackofficeMultiCaptureFacade;
    @Mock
    private Comboitem selectedItem;
    @Mock
    private PaymentTransactionEntryModel paymentTransactionEntryModel;
    @Mock
    private BrainTreePaymentTransactionService brainTreePaymentTransactionService;
    @Mock
    private BrainTreeAdjustedAuthorizationResult brainTreeAdjustedAuthorizationResult;
    @Spy
    @InjectMocks
    private BrainTreeAdjustAuthorizationController unit;

    @Test
    public void shouldInitCreateAdjustAuthorizationFormWhenOriginalAuthorizationAmountNotNull() {
        when(braintreeBackofficeAdjustAuthorizationFacade.getOriginalAmountByTransactionId(any(), any())).thenReturn(new BigDecimal(TEST_ORIGINAL_TRANSACTION_AMOUNT));
        testInitCreateAdjustAuthorizationFormWhenOrderHaveAuthorizedTransactions();
    }

    @Test
    public void shouldConfirmWhenAmountIsEmpty() {
        beforeConfirm();
        when(amount.getValue()).thenReturn(StringUtils.EMPTY);
        testBlankAmount(ORDER_AMOUNT_ERROR_EMPTY, ORDER_AMOUNT_ERROR_EMPTY_LABEL);
    }

    @Test
    public void shouldConfirmWhenAmountIsWhitespace() {
        beforeConfirm();
        when(amount.getValue()).thenReturn(StringUtils.SPACE);
        testBlankAmount(ORDER_AMOUNT_ERROR_EMPTY, ORDER_AMOUNT_ERROR_EMPTY_LABEL);
    }

    @Test
    public void shouldConfirmWhenAmountIsNull() {
        beforeConfirm();
        when(amount.getValue()).thenReturn(null);
        testBlankAmount(ORDER_AMOUNT_ERROR_EMPTY, ORDER_AMOUNT_ERROR_EMPTY_LABEL);
    }

    @Test
    public void shouldConfirmWhenAmountIsZero() {
        beforeConfirm();
        String TEST_STRING_ZERO_AMOUNT = "0";
        when(amount.getValue()).thenReturn(TEST_STRING_ZERO_AMOUNT);
        testBlankAmount(ORDER_AMOUNT_ERROR_ZERO, ORDER_AMOUNT_ERROR_ZERO_LABEL);
    }

    @Test
    public void shouldConfirmWhenAdjustedAmountTheSame() {
        beforeConfirm();
        when(amount.getValue()).thenReturn(TEST_ADJUSTED_AMOUNT);
        when(braintreeBackofficeAdjustAuthorizationFacade.getAmountByTransactionId(any(), any()))
                .thenReturn(new BigDecimal(TEST_ADJUSTED_AMOUNT));
        testBlankAmount(ORDER_ADJUST_AUTHORIZATION_AMOUNT_ERROR_EQUAL, ORDER_ADJUST_AUTHORIZATION_AMOUNT_ERROR_EQUAL_LABEL);
    }

    @Test
    public void shouldConfirmWhenAmountHasWrongFormat() {
        beforeConfirm();
        when(amount.getValue()).thenReturn("WRONG_AMOUNT");
        testBlankAmount(ORDER_AMOUNT_ERROR_NUMBER_FORMAT, ORDER_AMOUNT_ERROR_NUMBER_FORMAT_LABEL);
    }

    @Test
    public void shouldConfirmWhenOrderIsNotNullAndTransactionIsValidAndAuthorizationResultIsSuccess() throws Exception {
        beforeConfirm();
        when(brainTreePaymentTransactionService.isValidTransactionId(any(), any())).thenReturn(true);
        when(amount.getValue()).thenReturn(TEST_ADJUSTED_AMOUNT);
        when(braintreeBackofficeAdjustAuthorizationFacade.getAmountByTransactionId(any(), any()))
                .thenReturn(new BigDecimal(TEST_ORIGINAL_TRANSACTION_AMOUNT));
        when(braintreeBackofficeAdjustAuthorizationFacade.adjustAuthorization(any(), any(), any())).thenReturn(brainTreeAdjustedAuthorizationResult);
        when(brainTreeAdjustedAuthorizationResult.isSuccess()).thenReturn(true);
        when(widgetInstanceManager.getLabel(ADJUST_AUTHORIZATION_SUCCESS)).thenReturn(ADJUST_AUTHORIZATION_SUCCESS_LABEL);
        when(widgetInstanceManager.getLabel(ADJUST_AUTHORIZATION_TITLE)).thenReturn(ADJUST_AUTHORIZATION_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(),any(),anyInt(),any());
            unit.confirm();
            verify(braintreeBackofficeAdjustAuthorizationFacade).adjustAuthorization(order, TEST_COMBOITEM_LABEL, new BigDecimal(TEST_ADJUSTED_FORMATED_AMOUNT));
            verify(unit).showMsg(ADJUST_AUTHORIZATION_SUCCESS_LABEL, ADJUST_AUTHORIZATION_TITLE_LABEL, Messagebox.OK, Messagebox.INFORMATION);
            verify(widgetInstanceManager).sendOutput(OUT_MODIFIED_ITEM, order);
    }

    @Test
    public void shouldConfirmWhenOrderIsNotNullAndTransactionIsValidAndAuthorizationResultIsNotSuccess() throws Exception {
        beforeConfirm();
        when(brainTreePaymentTransactionService.isValidTransactionId(any(), any())).thenReturn(true);
        when(amount.getValue()).thenReturn(TEST_ADJUSTED_AMOUNT);
        when(braintreeBackofficeAdjustAuthorizationFacade.getAmountByTransactionId(any(), any()))
                .thenReturn(new BigDecimal(TEST_ORIGINAL_TRANSACTION_AMOUNT));
        when(braintreeBackofficeAdjustAuthorizationFacade.adjustAuthorization(any(), any(), any())).thenReturn(brainTreeAdjustedAuthorizationResult);
        when(brainTreeAdjustedAuthorizationResult.isSuccess()).thenReturn(false);
        when(brainTreeAdjustedAuthorizationResult.getErrorCode()).thenReturn(TEST_ERROR_CODE);
        when(brainTreeAdjustedAuthorizationResult.getErrorMessage()).thenReturn(TEST_ADJUSTED_ERR_MSG);
        when(widgetInstanceManager.getLabel(ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE)).thenReturn(ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(),any(),anyInt(),any());
            unit.confirm();
            verify(braintreeBackofficeAdjustAuthorizationFacade).adjustAuthorization(order, TEST_COMBOITEM_LABEL, new BigDecimal(TEST_ADJUSTED_FORMATED_AMOUNT));
            verify(unit).showMsg( ERROR_CODE + TEST_ERROR_CODE + ": " + TEST_ADJUSTED_ERR_MSG, ORDER_ADJUST_AUTHORIZATION_ERROR_TITLE_LABEL, Messagebox.OK, Messagebox.ERROR);
            verify(widgetInstanceManager).sendOutput(OUT_MODIFIED_ITEM, order);
    }

    private void beforeConfirm() {
        final Object[] executionBodyResult = new Object[1];
        when(order.getSite()).thenReturn(baseSiteModel);
        when(transactions.getSelectedIndex()).thenReturn(ZERO_INDEX);
        when(transactions.getSelectedItem()).thenReturn(comboitem);
        when(comboitem.getLabel()).thenReturn(TEST_COMBOITEM_LABEL);
        when(sessionService.executeInLocalViewWithParams(Mockito.anyMap(), Mockito.any())).thenAnswer(invocationOnMock -> {
            SessionExecutionBody sessionExecutionBody = invocationOnMock.getArgument(1);
            executionBodyResult[0] = sessionExecutionBody.execute();
            return executionBodyResult[0];
        });
    }

    private void testInitCreateAdjustAuthorizationFormWhenOrderHaveAuthorizedTransactions() {
        final Object[] executionBodyResult = new Object[1];
        when(widgetInstanceManager.getLabel(ADJUSTAUTHORIZATION_CONFIRM_TITLE)).thenReturn(ADJUSTAUTHORIZATION_CONFIRM_TITLE_LABEL);
        when(mockedOrder.getCode()).thenReturn(TEST_ORDER_CODE);
        when(mockedOrder.getUser()).thenReturn(mockedUser);
        when(mockedOrder.getSite()).thenReturn(baseSiteModel);
        when(mockedUser.getDisplayName()).thenReturn(TEST_DISPLAY_NAME);
        when(braintreeBackofficeMultiCaptureFacade.getTransactionAmount(any(), any())).thenReturn(new BigDecimal(TEST_ORIGINAL_TRANSACTION_AMOUNT));
        when(transactions.getSelectedItem()).thenReturn(selectedItem);
        String TEST_ORDER_TRANSACTION_AMOUNT = "6";
        when(paymentTransactionEntryModel.getAmount()).thenReturn(new BigDecimal(TEST_ORDER_TRANSACTION_AMOUNT));
        when(braintreeBackofficeAdjustAuthorizationFacade.getAuthorizedTransactions(any())).thenReturn(List.of(paymentTransactionEntryModel));
        when(sessionService.executeInLocalViewWithParams(Mockito.anyMap(), Mockito.any())).thenAnswer(invocationOnMock -> {
            SessionExecutionBody sessionExecutionBody = invocationOnMock.getArgument(1);
            executionBodyResult[0] = sessionExecutionBody.execute();
            return executionBodyResult[0];
        });
        doReturn(comboitem).when(unit).createComboItem(any());
        unit.initCreateAdjustAuthorizationForm(mockedOrder);
        assertEquals(mockedOrder, unit.getOrder());
        verify(unit).createComboItem(paymentTransactionEntryModel);
        verify(widgetInstanceManager).setTitle(ADJUSTAUTHORIZATION_CONFIRM_TITLE_LABEL + StringUtils.SPACE + TEST_ORDER_CODE);
        assertEquals(ZERO_INDEX, transactions.getSelectedIndex());
        assertNull(executionBodyResult[0]);
    }

    private void testBlankAmount(String labelName, String label) {
        when(widgetInstanceManager.getLabel(labelName)).thenReturn(label);
        WrongValueException throwable = Assert.assertThrows(WrongValueException.class, () -> unit.confirm());
        Assert.assertEquals(label, throwable.getMessage());
    }

}
