package com.braintree.populators;

import com.braintree.command.result.BrainTreeFindTransactionResult;
import com.braintree.enums.BraintreePaymentTransactionStatus;
import com.braintree.hybris.data.BraintreeTransactionData;
import com.braintree.hybris.data.BraintreeTransactionEntryData;
import com.braintree.hybris.data.BraintreeTransactionEntryDetailData;
import com.braintreegateway.Customer;
import com.braintreegateway.PayPalDetails;
import com.braintreegateway.ResourceCollection;
import com.braintreegateway.RiskData;
import com.braintreegateway.StatusEvent;
import com.braintreegateway.Transaction;
import de.hybris.bootstrap.annotations.UnitTest;
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.Calendar;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

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

    private static final String TRANSACTION_ID = "TransactionId";
    private static final String DATE = "Date";
    private static final String GRAPH_QL_ID = "GraphQLId";
    private static final String PAYMENT_INSTRUMENT_TYPE = "PaymentInstrumentType";
    private static final String RISK_DECISION = "RiskDecision";
    private static final String FIRST_NAME = "Tester";
    private static final String LAST_NAME = "Test";
    private static final String CAPTURE_ID = "CaptureId";
    private static final String REFUND_ID = "RefundId";
    private static final String CURRENCY = "USD";
    private static final String AMOUNT = "100";
    private static final String REFUNDED_TRANSACTION_ID = "RefundedTransactionId";
    private static final String FORMED_AMOUNT = String.format("%s %s", AMOUNT, CURRENCY);
    private static final String FORMED_NAME = String.format("%s %s", FIRST_NAME, LAST_NAME);

    @InjectMocks
    @Spy
    private BraintreeTransactionPopulator unit;

    @Mock
    private ResourceCollection<Transaction> transactions;
    @Mock
    private Iterator<Transaction> transactionIterator;
    @Mock
    private Iterator<String> stringIterator;
    @Mock
    private Iterator<StatusEvent> statusEventIterator;
    @Mock
    private Transaction transaction;
    @Mock
    private BrainTreeFindTransactionResult brainTreeFindTransactionResult;
    @Mock
    private PayPalDetails payPalDetails;
    @Mock
    private Customer customer;
    @Mock
    private RiskData riskData;
    @Mock
    private BraintreeTransactionEntryDetailData braintreeTransactionEntryDetailData;
    @Mock
    private List<String> refundIds;
    @Mock
    private List<StatusEvent> statusEventList;
    @Mock
    private StatusEvent statusHistory;
    @Mock
    private Calendar statusHistoryTime;

    @Before
    public void setUp(){
        when(brainTreeFindTransactionResult.getTransactions()).thenReturn(transactions);
        when(transactions.iterator()).thenReturn(transactionIterator);
        when(transactionIterator.hasNext()).thenReturn(true, false);
        when(transactionIterator.next()).thenReturn(transaction);
        when(transaction.getId()).thenReturn(TRANSACTION_ID);
        when(transaction.getAmount()).thenReturn(new BigDecimal(AMOUNT));
        when(transaction.getCurrencyIsoCode()).thenReturn(CURRENCY);
        when(transaction.getPayPalDetails()).thenReturn(payPalDetails);
        when(payPalDetails.getRefundId()).thenReturn(REFUND_ID);
        when(payPalDetails.getCaptureId()).thenReturn(CAPTURE_ID);
        when(transaction.getCustomer()).thenReturn(customer);
        when(customer.getFirstName()).thenReturn(FIRST_NAME);
        when(customer.getLastName()).thenReturn(LAST_NAME);
        when(transaction.getStatus()).thenReturn(Transaction.Status.AUTHORIZED);
        when(transaction.getType()).thenReturn(Transaction.Type.SALE);
        when(transaction.getRiskData()).thenReturn(riskData);
        when(riskData.getDecision()).thenReturn(RISK_DECISION);
        when(transaction.getPaymentInstrumentType()).thenReturn(PAYMENT_INSTRUMENT_TYPE);
        when(transaction.getGraphQLId()).thenReturn(GRAPH_QL_ID);
        when(transaction.getRefundIds()).thenReturn(refundIds);
        when(refundIds.isEmpty()).thenReturn(false);
        when(refundIds.iterator()).thenReturn(stringIterator);
        when(stringIterator.hasNext()).thenReturn(true, false);
        when(stringIterator.next()).thenReturn(REFUND_ID);
        when(transaction.getStatusHistory()).thenReturn(statusEventList);
        when(statusEventList.iterator()).thenReturn(statusEventIterator);
        when(statusEventIterator.hasNext()).thenReturn(true, false);
        when(statusEventIterator.next()).thenReturn(statusHistory);
        when(statusHistory.getStatus()).thenReturn(Transaction.Status.AUTHORIZED);
        when(statusHistory.getTimestamp()).thenReturn(statusHistoryTime);
        when(transaction.getRefundedTransactionId()).thenReturn(REFUNDED_TRANSACTION_ID);
    }

    @Test
    public void shouldPopulateBrainTreeFindTransactionResultToBraintreeTransactionData() {
        BraintreeTransactionData braintreeTransactionData = new BraintreeTransactionData();

        doReturn(DATE).when(unit).getFormedDate(any());
        doReturn(braintreeTransactionEntryDetailData).when(unit).convertDetails(any());

        unit.populate(brainTreeFindTransactionResult, braintreeTransactionData);

        BraintreeTransactionEntryData braintreeTransactionEntryData = braintreeTransactionData.getTransactionEntries().get(0);
        assertEquals(braintreeTransactionEntryData.getId(), TRANSACTION_ID);
        assertEquals(braintreeTransactionEntryData.getDate(), DATE);
        assertEquals(braintreeTransactionEntryData.getAmount(), FORMED_AMOUNT);
        assertEquals(braintreeTransactionEntryData.getCurrencyIsoCode(), CURRENCY);
        assertEquals(braintreeTransactionEntryData.getTotal(), new BigDecimal(AMOUNT));
        assertEquals(braintreeTransactionEntryData.getRefundId(), REFUND_ID);
        assertEquals(braintreeTransactionEntryData.getCaptureId(), CAPTURE_ID);
        assertEquals(braintreeTransactionEntryData.getCustomer(), FORMED_NAME);
        assertEquals(braintreeTransactionEntryData.getStatus(), Transaction.Status.AUTHORIZED.name());
        assertEquals(braintreeTransactionEntryData.getType(), Transaction.Type.SALE.name());
        assertEquals(braintreeTransactionEntryData.getRiskDecision(), RISK_DECISION);
        assertEquals(braintreeTransactionEntryData.getPaymentInstrumentType(), PAYMENT_INSTRUMENT_TYPE);
        assertEquals(braintreeTransactionEntryData.getIdGraphQL(), GRAPH_QL_ID);
        assertEquals(braintreeTransactionEntryData.getRefundIds().get(0).getRequestId(), REFUND_ID);
        assertEquals(braintreeTransactionEntryData.getStatusHistory().get(0).getStatus(), BraintreePaymentTransactionStatus.AUTHORIZED);
        assertEquals(braintreeTransactionEntryData.getRefundedTransaction().getId(), REFUNDED_TRANSACTION_ID);
    }

    @Test
    public void shouldPopulateBrainTreeFindTransactionResultToBraintreeTransactionDataWithSomeNullValues() {
        BraintreeTransactionData braintreeTransactionData = new BraintreeTransactionData();

        doReturn(DATE).when(unit).getFormedDate(any());
        doReturn(braintreeTransactionEntryDetailData).when(unit).convertDetails(any());
        when(transaction.getPayPalDetails()).thenReturn(null);
        when(transaction.getCustomer()).thenReturn(null);
        when(transaction.getStatus()).thenReturn(null);
        when(transaction.getType()).thenReturn(null);
        when(transaction.getRiskData()).thenReturn(null);
        when(transaction.getRefundIds()).thenReturn(null);
        when(transaction.getStatusHistory()).thenReturn(null);
        when(transaction.getRefundedTransactionId()).thenReturn(null);

        unit.populate(brainTreeFindTransactionResult, braintreeTransactionData);

        BraintreeTransactionEntryData braintreeTransactionEntryData = braintreeTransactionData.getTransactionEntries().get(0);
        assertNull(braintreeTransactionEntryData.getRefundId());
        assertNull(braintreeTransactionEntryData.getCaptureId());
        assertNull(braintreeTransactionEntryData.getCustomer());
        assertNull(braintreeTransactionEntryData.getCustomer());
        assertNull(braintreeTransactionEntryData.getStatus());
        assertNull(braintreeTransactionEntryData.getType());
        assertNull(braintreeTransactionEntryData.getRiskDecision());
        assertNull(braintreeTransactionEntryData.getRefundIds());
        assertEquals(0, braintreeTransactionEntryData.getStatusHistory().size());
        assertNull(braintreeTransactionEntryData.getRefundedTransaction());
    }
}
