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

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 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_CLONE_TITLE;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_CLONE_ERROR;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_CLONE_SUCCESS;
import static com.braintree.customersupportbackoffice.constants.BraintreecustomersupportbackofficeConstants.TransactionManagementActions.WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX;
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.anyBoolean;
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 BrainTreeCloneTransactionControllerTest {
    private static final String TEST_TRANSACTION_ID = "TEST_TRANSACTION_ID";
    private static final String TEST_LABEL = "TEST_TITLE";
    private static final String CLONE_TRANSACTION_TITLE = "braintreecustomersupportbackoffice.clonetransaction.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 SUCCESS_MSG_LABEL = "SUCCESS_MSG_LABEL";
    private static final String FAILED_MSG_LABEL = "FAILED_MSG_LABEL";
    private static final String WIDGET_MESSAGE_CLONE_SUCCESS_LABEL = "WIDGET_MESSAGE_CLONE_SUCCESS_LABEL";
    private static final String WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL = "WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL";
    private static final String WIDGET_MESSAGE_CLONE_ERROR_LABEL = "WIDGET_MESSAGE_CLONE_ERROR_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 CLONE_TRANSACTION_ERROR_MSG = "CLONE_TRANSACTION_ERROR_MSG";
    private static final Object CLONED = "cloned";
    private static final String OUT_CONFIRM = "confirm";
    private static final boolean SUBMIT_FOR_SETTLEMENT_FALSE = false;
    @Mock
    private BrainTreeTransactionDetailModel brainTreeTransactionDetailModel;
    @Mock
    private BrainTreeCustomerSupportFacade brainTreeCustomerSupportFacade;
    @Mock
    private BrainTreeResponseResultData brainTreeResponseResultData;
    @Mock
    private WidgetInstanceManager widgetInstanceManager;
    @Mock
    protected Textbox transactionId;
    @Mock
    protected Textbox amount;
    @Spy
    @InjectMocks
    private BrainTreeCloneTransactionController unit;

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

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

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

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

    @Test
    public void shouldConfirmCloneWhenAmountIsZero() {
        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.confirmClone());
        Assert.assertEquals(TEST_AMOUNT_ERROR_ZERO, throwable.getMessage());
    }

    @Test
    public void shouldConfirmCloneWhenThrownNumberFormatException() {
        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.confirmClone());
        Assert.assertEquals(TEST_AMOUNT_ERROR_WRONG_FORMAT, throwable.getMessage());
    }

    @Test
    public void shouldConfirmCloneWhenResultIsSuccessAndTransactionIdIsEmpty() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(EMPTY);
        testConfirmCloneWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsSuccessAndTransactionIdIsNull() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(null);
        testConfirmCloneWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsSuccessAndTransactionIdIsWhitespace() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(SPACE);
        testConfirmCloneWhenResultIsSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsSuccessAndTransactionIdIsNotBlank() {
        when(brainTreeResponseResultData.getTransactionId()).thenReturn(TEST_TRANSACTION_ID);
        when(brainTreeCustomerSupportFacade.cloneTransaction(any(), any(), anyBoolean())).thenReturn(brainTreeResponseResultData);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(true);
        when(widgetInstanceManager.getLabel(WIDGET_CLONE_TITLE)).thenReturn(SUCCESS_MSG_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_CLONE_SUCCESS)).thenReturn(WIDGET_MESSAGE_CLONE_SUCCESS_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX)).thenReturn(WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        final String expectedMsg = String.format("%s %s: %s", WIDGET_MESSAGE_CLONE_SUCCESS_LABEL, WIDGET_MESSAGE_TRANSACTION_CREATE_SUCCESS_POSTFIX_LABEL, TEST_TRANSACTION_ID);
        unit.setTransaction(brainTreeTransactionDetailModel);
        unit.confirmClone();
        verify(brainTreeCustomerSupportFacade).cloneTransaction(unit.getTransaction(), TEST_TRANSACTION_EXPECTED_AMOUNT, SUBMIT_FOR_SETTLEMENT_FALSE);
        verify(unit).showMsg(expectedMsg, SUCCESS_MSG_LABEL, Messagebox.OK, Messagebox.INFORMATION);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, CLONED);
    }

    @Test
    public void shouldConfirmCloneWhenResultIsNotSuccessAndErrorMsgIsEmpty() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(EMPTY);
        testConfirmCloneWhenResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsNotSuccessAndErrorMsgIsNull() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(null);
        testConfirmCloneWhenResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsNotSuccessAndErrorMsgIsWhitespace() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(SPACE);
        testConfirmCloneWhenResultIsNotSuccess();
    }

    @Test
    public void shouldConfirmCloneWhenResultIsNotSuccessAndErrorMsgIsNotBlank() {
        when(brainTreeResponseResultData.getErrorMessage()).thenReturn(CLONE_TRANSACTION_ERROR_MSG);
        when(brainTreeCustomerSupportFacade.cloneTransaction(any(), any(), anyBoolean())).thenReturn(brainTreeResponseResultData);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(false);
        when(widgetInstanceManager.getLabel(WIDGET_CLONE_TITLE)).thenReturn(FAILED_MSG_LABEL);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.setTransaction(brainTreeTransactionDetailModel);
        unit.confirmClone();
        verify(brainTreeCustomerSupportFacade).cloneTransaction(unit.getTransaction(), TEST_TRANSACTION_EXPECTED_AMOUNT, SUBMIT_FOR_SETTLEMENT_FALSE);
        verify(unit).showMsg(CLONE_TRANSACTION_ERROR_MSG, FAILED_MSG_LABEL, Messagebox.OK, Messagebox.ERROR);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, CLONED);

    }

    private void testConfirmCloneWhenResultIsSuccess() {
        when(brainTreeCustomerSupportFacade.cloneTransaction(any(), any(), anyBoolean())).thenReturn(brainTreeResponseResultData);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(true);
        when(widgetInstanceManager.getLabel(WIDGET_CLONE_TITLE)).thenReturn(SUCCESS_MSG_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_CLONE_SUCCESS)).thenReturn(WIDGET_MESSAGE_CLONE_SUCCESS_LABEL);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.setTransaction(brainTreeTransactionDetailModel);
        unit.confirmClone();
        verify(brainTreeCustomerSupportFacade).cloneTransaction(unit.getTransaction(), TEST_TRANSACTION_EXPECTED_AMOUNT, SUBMIT_FOR_SETTLEMENT_FALSE);
        verify(unit).showMsg(WIDGET_MESSAGE_CLONE_SUCCESS_LABEL, SUCCESS_MSG_LABEL, Messagebox.OK, Messagebox.INFORMATION);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, CLONED);
    }

    private void testConfirmCloneWhenResultIsNotSuccess() {
        when(brainTreeCustomerSupportFacade.cloneTransaction(any(), any(), anyBoolean())).thenReturn(brainTreeResponseResultData);
        when(brainTreeResponseResultData.isSuccess()).thenReturn(false);
        when(widgetInstanceManager.getLabel(WIDGET_CLONE_TITLE)).thenReturn(FAILED_MSG_LABEL);
        when(widgetInstanceManager.getLabel(WIDGET_MESSAGE_CLONE_ERROR)).thenReturn(WIDGET_MESSAGE_CLONE_ERROR_LABEL);
        when(amount.getValue()).thenReturn(TEST_TRANSACTION_EXPECTED_AMOUNT);
        doNothing().when(unit).showMsg(any(), any(), anyInt(), any());
        unit.setTransaction(brainTreeTransactionDetailModel);
        unit.confirmClone();
        verify(brainTreeCustomerSupportFacade).cloneTransaction(unit.getTransaction(), TEST_TRANSACTION_EXPECTED_AMOUNT, SUBMIT_FOR_SETTLEMENT_FALSE);
        verify(unit).showMsg(WIDGET_MESSAGE_CLONE_ERROR_LABEL, FAILED_MSG_LABEL, Messagebox.OK, Messagebox.ERROR);
        verify(widgetInstanceManager).sendOutput(OUT_CONFIRM, CLONED);
    }

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

}
