package com.braintree.customersupportbackoffice.widgets.transaction.refund;

import com.braintree.customersupportbackoffice.facade.BrainTreeCustomerSupportFacade;
import com.braintree.hybris.data.BrainTreeResponseResultData;
import com.braintree.model.BrainTreeTransactionDetailModel;
import com.hybris.cockpitng.engine.WidgetInstanceManager;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.OrderModel;
import org.apache.commons.lang.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.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Textbox;

import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_REFUND_ERROR;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_REFUND_SUCCESS;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_REFUND_TITLE;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.SPACE;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class BrainTreeRefundTransactionControllerTest {
    private static final String TEST_TRANSACTION_ID = "TEST_TRANSACTION_ID";
    private static final String TEST_LABEL = "TEST_TITLE";
    private static final String REFUND_TRANSACTION_TITLE = "braintreecustomersupportbackoffice.refundtransaction.confirm.title";
    private static final String TEST_TRANSACTION_AMOUNT = "1 2";
    private static final String TEST_TRANSACTION_EXPECTED_AMOUNT = "1";
    private static final String TEST_AMOUNT_LABEL_ERROR = "bt.customersupport.transaction.amount.error.empty";
    private static final String TEST_AMOUNT_LABEL_ERROR_ZERO = "bt.customersupport.transaction.amount.error.zero";
    private static final String TEST_AMOUNT_LABEL_ERROR_WRONG_FORMAT = "bt.customersupport.transaction.amount.error.number.format";
    private static final String WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER_LABEL = "WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER_LABEL";
    private static final String WIDGET_MESSAGE_REFUND_ERROR_LABEL = "WIDGET_MESSAGE_REFUND_ERROR_LABEL";
    private static final String REFUND_TRANSACTION_ERROR_MSG = "REFUND_TRANSACTION_ERROR_MSG";
    private static final String WIDGET_REFUND_TITLE_LABEL = "WIDGET_REFUND_TITLE_LABEL";
    private static final String WIDGET_MESSAGE_REFUND_SUCCESS_LABEL = "WIDGET_MESSAGE_REFUND_SUCCESS_LABEL";
    private static final String WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL = "WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL";
    private static final String TEST_AMOUNT_ERROR = "TEST_AMOUNT_ERROR";
    private static final String TEST_AMOUNT_ERROR_ZERO = "TEST_AMOUNT_ERROR_ZERO";
    private static final String TEST_AMOUNT_ERROR_WRONG_FORMAT = "TEST_AMOUNT_ERROR_WRONG_FORMAT";
    private static final String ZERO_AMOUNT = "0";
    private static final String WRONG_FORMAT_STRING = "WRONG_FORMAT_STRING";
    private static final String REFUNDED = "refunded";
    private static final String OUT_CONFIRM = "confirm";
    @Mock
    protected Textbox transactionId;
    @Mock
    protected Textbox amount;
    @Mock
    private BrainTreeTransactionDetailModel brainTreeTransactionDetailModel;
    @Mock
    private BrainTreeCustomerSupportFacade brainTreeCustomerSupportFacade;
    @Mock
    private BrainTreeResponseResultData brainTreeResponseResultData;
    @Mock
    private OrderModel orderModel;
    @Mock
    private WidgetInstanceManager widgetInstanceManager;
    @Spy
    @InjectMocks
    private BrainTreeRefundTransactionController unit;

    @Test
    public void shouldInitRefundForm() {
        when(brainTreeTransactionDetailModel.getId()).thenReturn(TEST_TRANSACTION_ID);
        when(brainTreeTransactionDetailModel.getAmount()).thenReturn(TEST_TRANSACTION_AMOUNT);
        when(widgetInstanceManager.getLabel(REFUND_TRANSACTION_TITLE)).thenReturn(TEST_LABEL);
        unit.initRefundForm(brainTreeTransactionDetailModel);
        verify(widgetInstanceManager).setTitle(TEST_LABEL + SPACE + TEST_TRANSACTION_ID);
    }

    @Test
    public void shouldConfirmRefundWhenAmountIsEmpty() {
        when(amount.getValue()).thenReturn(StringUtils.EMPTY);
        testBlankAmount();
    }

    @Test
    public void shouldConfirmRefundWhenAmountIsNull() {
        when(amount.getValue()).thenReturn(null);
        testBlankAmount();
    }

    @Test
    public void shouldConfirmRefundWhenAmountIsWhiteSpace() {
        when(amount.getValue()).thenReturn(SPACE);
        testBlankAmount();
    }

    @Test
    public void shouldConfirmRefundWhenAmountIsZero() {
        when(amount.getValue()).thenReturn(ZERO_AMOUNT);
        when(widgetInstanceManager.getLabel(TEST_AMOUNT_LABEL_ERROR_ZERO)).thenReturn(TEST_AMOUNT_ERROR_ZERO);
        WrongValueException throwable = Assert.assertThrows(WrongValueException.class, () -> unit.confirmRefund());
        Assert.assertEquals(TEST_AMOUNT_ERROR_ZERO, throwable.getMessage());
    }

    @Test
    public void shouldConfirmRefundWhenThrownNumberFormatException() {
        when(amount.getValue()).thenReturn(WRONG_FORMAT_STRING);
        when(widgetInstanceManager.getLabel(TEST_AMOUNT_LABEL_ERROR_WRONG_FORMAT)).thenReturn(TEST_AMOUNT_ERROR_WRONG_FORMAT);
        WrongValueException throwable = Assert.assertThrows(WrongValueException.class, () -> unit.confirmRefund());
        Assert.assertEquals(TEST_AMOUNT_ERROR_WRONG_FORMAT, throwable.getMessage());
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNotNull() {
        when(brainTreeTransactionDetailModel.getLinkedOrder()).thenReturn(orderModel);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER)).thenReturn(WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_REFUND_TITLE)).thenReturn(WIDGET_REFUND_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.confirmRefund();
        verify(unit).showMsg(WIDGET_MESSAGE_REFUND_ERROR_GO_TO_ORDER_LABEL, WIDGET_REFUND_TITLE_LABEL, Messagebox.OK, Messagebox.ERROR);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, REFUNDED);
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsSuccessAndTransactionIdIsNull() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(null);
        testConfirmRefundWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsSuccessAndTransactionIdIsEmpty() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(EMPTY);
        testConfirmRefundWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsSuccessAndTransactionIdIsWhitespace() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(SPACE);
        testConfirmRefundWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsSuccessAndTransactionIdIsNotBlank() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(TEST_TRANSACTION_ID);
        when(brainTreeTransactionDetailModel.getLinkedOrder()).thenReturn(null);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(true);
        when(brainTreeCustomerSupportFacade.refundTransaction(any(), any())).thenReturn(brainTreeResponseResultData);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX)).thenReturn(WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_REFUND_SUCCESS)).thenReturn(WIDGET_MESSAGE_REFUND_SUCCESS_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_REFUND_TITLE)).thenReturn(WIDGET_REFUND_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        final String expectedMsg = String.format("%s %s: %s", WIDGET_MESSAGE_REFUND_SUCCESS_LABEL, WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL, TEST_TRANSACTION_ID);
        unit.confirmRefund();
        verify(brainTreeCustomerSupportFacade).refundTransaction(brainTreeTransactionDetailModel, TEST_TRANSACTION_EXPECTED_AMOUNT);
        verify(unit).showMsg(expectedMsg, WIDGET_REFUND_TITLE_LABEL, Messagebox.OK, Messagebox.INFORMATION);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, REFUNDED);
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccessAndErrorMsgIsNull() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(null);
        shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccessAndErrorMsgIsEmpty() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(EMPTY);
        shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccessAndErrorMsgIsWhitespace() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(SPACE);
        shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccessAndErrorMsgIsNotBlank() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(REFUND_TRANSACTION_ERROR_MSG);
        when(brainTreeTransactionDetailModel.getLinkedOrder()).thenReturn(null);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(false);
        when(brainTreeCustomerSupportFacade.refundTransaction(any(), any())).thenReturn(brainTreeResponseResultData);
        when(widgetInstanceManager.getLabel(WIDGET_REFUND_TITLE)).thenReturn(WIDGET_REFUND_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.confirmRefund();
        verify(brainTreeCustomerSupportFacade).refundTransaction(brainTreeTransactionDetailModel, TEST_TRANSACTION_EXPECTED_AMOUNT);
        verify(unit).showMsg(REFUND_TRANSACTION_ERROR_MSG, WIDGET_REFUND_TITLE_LABEL, Messagebox.OK, Messagebox.ERROR);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, REFUNDED);
    }

    private void shouldConfirmRefundWhenLinkedOrderNullAndResultIsNotSuccess() {
        when(brainTreeTransactionDetailModel.getLinkedOrder()).thenReturn(null);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(false);
        when(brainTreeCustomerSupportFacade.refundTransaction(any(), any())).thenReturn(brainTreeResponseResultData);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_REFUND_ERROR)).thenReturn(WIDGET_MESSAGE_REFUND_ERROR_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_REFUND_TITLE)).thenReturn(WIDGET_REFUND_TITLE_LABEL);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.confirmRefund();
        verify(brainTreeCustomerSupportFacade).refundTransaction(brainTreeTransactionDetailModel, TEST_TRANSACTION_EXPECTED_AMOUNT);
        verify(unit).showMsg(WIDGET_MESSAGE_REFUND_ERROR_LABEL, WIDGET_REFUND_TITLE_LABEL, Messagebox.OK, Messagebox.ERROR);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, REFUNDED);
    }

    private void testConfirmRefundWhenResultIsSuccess() {
        when(brainTreeTransactionDetailModel.getLinkedOrder()).thenReturn(null);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(true);
        when(brainTreeCustomerSupportFacade.refundTransaction(any(), any())).thenReturn(brainTreeResponseResultData);
        when(widgetInstanceManager.getLabel(WIDGET_REFUND_TITLE)).thenReturn(WIDGET_REFUND_TITLE_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_REFUND_SUCCESS)).thenReturn(WIDGET_MESSAGE_REFUND_SUCCESS_LABEL);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.confirmRefund();
        verify(brainTreeCustomerSupportFacade).refundTransaction(brainTreeTransactionDetailModel, TEST_TRANSACTION_EXPECTED_AMOUNT);
        verify(unit).showMsg(WIDGET_MESSAGE_REFUND_SUCCESS_LABEL, WIDGET_REFUND_TITLE_LABEL, Messagebox.OK, Messagebox.INFORMATION);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, REFUNDED);
    }

    private void testBlankAmount() {
        when(widgetInstanceManager.getLabel(TEST_AMOUNT_LABEL_ERROR)).thenReturn(TEST_AMOUNT_ERROR);
        WrongValueException throwable = Assert.assertThrows(WrongValueException.class, () -> unit.confirmRefund());
        Assert.assertEquals(TEST_AMOUNT_ERROR, throwable.getMessage());
    }

}
