package com.braintree.commands.impl;


import com.braintree.command.request.BrainTreeCloneTransactionRequest;
import com.braintree.command.result.BrainTreeCloneTransactionResult;
import com.braintreegateway.BraintreeGateway;
import com.braintreegateway.TransactionGateway;
import com.braintreegateway.Transaction;
import com.braintreegateway.ValidationError;
import com.braintreegateway.ValidationErrorCode;
import com.braintreegateway.ValidationErrors;
import com.braintreegateway.Result;
import com.braintreegateway.TransactionCloneRequest;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.payment.AdapterException;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;
import org.junit.Before;
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.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;


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

    private static final String ORIGINAL_TRANSACTION_ID = "originalTransactionId";
    private static final String CODE = "code";
    @Mock
    private BraintreeGateway mockGateway;

    @Mock
    private Result<Transaction> mockResult;

    @Mock
    private TransactionGateway mockTransaction;

    @Mock Transaction transaction;

    @Mock
    private Logger logger;

    @Mock
    private DefaultBrainTreeLoggingHandler loggingHandler;

    @Spy
    @InjectMocks
    private DefaultBrainTreeCloneTransactionCommand cloneTransactionCommand;

    @Before
    public void setup() {
        doReturn(mockGateway).when(cloneTransactionCommand).getBraintreeGateway();
        doReturn(loggingHandler).when(cloneTransactionCommand).getLoggingHandler();
        when(loggingHandler.getLogger()).thenReturn(logger);
    }

    @Test
    public void shouldPerformSuccessfully() {
        BrainTreeCloneTransactionRequest request = new BrainTreeCloneTransactionRequest(CODE);
        request.setTransactionId(ORIGINAL_TRANSACTION_ID);
        request.setAmount(BigDecimal.TEN);
        request.setSubmitForSettlement(true);

        TransactionCloneRequest expectedTransactionCloneRequest = new TransactionCloneRequest();
        expectedTransactionCloneRequest.amount(BigDecimal.TEN);
        expectedTransactionCloneRequest.options().submitForSettlement(true).done();

        when(mockResult.isSuccess()).thenReturn(true);
        when(mockGateway.transaction()).thenReturn(mockTransaction);
        when(mockTransaction.cloneTransaction(anyString(), any(TransactionCloneRequest.class))).thenReturn(mockResult);

        BrainTreeCloneTransactionResult result = cloneTransactionCommand.perform(request);

        assertNotNull(result);
        assertEquals(TransactionStatus.ACCEPTED, result.getTransactionStatus());
        assertEquals(TransactionStatusDetails.SUCCESFULL, result.getTransactionStatusDetails());
        verify(mockTransaction).cloneTransaction(eq(ORIGINAL_TRANSACTION_ID), any(TransactionCloneRequest.class));
    }

    @Test
    public void shouldPerformSuccessfullyWithGivenTransaction() {
        BrainTreeCloneTransactionRequest request = new BrainTreeCloneTransactionRequest(CODE);
        request.setTransactionId(ORIGINAL_TRANSACTION_ID);
        request.setAmount(BigDecimal.TEN);

        TransactionCloneRequest expectedTransactionCloneRequest = new TransactionCloneRequest();
        expectedTransactionCloneRequest.amount(BigDecimal.TEN);
        expectedTransactionCloneRequest.options().submitForSettlement(true).done();

        when(mockResult.isSuccess()).thenReturn(true);
        when(mockGateway.transaction()).thenReturn(mockTransaction);
        when(mockTransaction.cloneTransaction(anyString(), any(TransactionCloneRequest.class))).thenReturn(mockResult);
        when(mockResult.getTarget()).thenReturn(transaction);
        when(transaction.getId()).thenReturn(CODE);

        BrainTreeCloneTransactionResult result = cloneTransactionCommand.perform(request);

        assertNotNull(result);
        assertEquals(TransactionStatus.ACCEPTED, result.getTransactionStatus());
        assertEquals(TransactionStatusDetails.SUCCESFULL, result.getTransactionStatusDetails());
        verify(mockTransaction).cloneTransaction(eq(ORIGINAL_TRANSACTION_ID), any(TransactionCloneRequest.class));
    }

    @Test
    public void shouldFailPerform() {
        BrainTreeCloneTransactionRequest request = new BrainTreeCloneTransactionRequest(CODE);
        request.setTransactionId(ORIGINAL_TRANSACTION_ID);
        request.setAmount(BigDecimal.TEN);
        request.setSubmitForSettlement(true);

        TransactionCloneRequest expectedTransactionCloneRequest = new TransactionCloneRequest();
        expectedTransactionCloneRequest.amount(BigDecimal.TEN);
        expectedTransactionCloneRequest.options().submitForSettlement(true).done();
        ValidationErrors mockErrorCollection = mock(ValidationErrors.class);
        ValidationError error = mock(ValidationError.class);
        List<ValidationError> errorList = Arrays.asList(error);

        when(mockResult.isSuccess()).thenReturn(false);
        when(mockResult.getErrors()).thenReturn(mockErrorCollection);
        when(mockGateway.transaction()).thenReturn(mockTransaction);
        when(mockTransaction.cloneTransaction(anyString(), any(TransactionCloneRequest.class))).thenReturn(mockResult);
        when(error.getCode()).thenReturn(ValidationErrorCode.UNKNOWN_VALIDATION_ERROR);
        when(loggingHandler.getLogger()).thenReturn(logger);
        when(mockErrorCollection.getAllDeepValidationErrors()).thenReturn(errorList);

        BrainTreeCloneTransactionResult result = cloneTransactionCommand.perform(request);

        assertNotNull(result);
        assertFalse(result.isSuccess());
        assertNotEquals(TransactionStatus.ACCEPTED, result.getTransactionStatus());
        assertNotEquals(TransactionStatusDetails.SUCCESFULL, result.getTransactionStatusDetails());
        verify(mockTransaction).cloneTransaction(eq(ORIGINAL_TRANSACTION_ID), any(TransactionCloneRequest.class));
    }

    @Test(expected = AdapterException.class)
    public void shouldThrowError() {
        BrainTreeCloneTransactionRequest request = new BrainTreeCloneTransactionRequest(CODE);
        request.setTransactionId(ORIGINAL_TRANSACTION_ID);
        request.setAmount(BigDecimal.TEN);
        request.setSubmitForSettlement(true);

        TransactionCloneRequest expectedTransactionCloneRequest = new TransactionCloneRequest();
        expectedTransactionCloneRequest.amount(BigDecimal.TEN);
        expectedTransactionCloneRequest.options().submitForSettlement(true).done();

        when(mockGateway.transaction()).thenReturn(mockTransaction);
        when(mockTransaction.cloneTransaction(anyString(), any(TransactionCloneRequest.class))).thenThrow(new AdapterException());

        BrainTreeCloneTransactionResult result = cloneTransactionCommand.perform(request);

        assertNotNull(result);
        assertFalse(result.isSuccess());
        assertNotEquals(TransactionStatus.ACCEPTED, result.getTransactionStatus());
        assertNotEquals(TransactionStatusDetails.SUCCESFULL, result.getTransactionStatusDetails());
        verify(mockTransaction).cloneTransaction(eq(ORIGINAL_TRANSACTION_ID), any(TransactionCloneRequest.class));
    }
}
