package com.braintree.populators;

import com.braintree.enums.BraintreePaymentTransactionStatus;
import com.braintree.hybris.data.BrainTreeGraphQLTransactionData;
import com.braintree.hybris.data.BraintreeTransactionEntryData;
import com.braintree.hybris.data.BraintreeTransactionEntryDetailData;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeCustomer;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeMonetaryAmount;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePageInfo;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePayPalAccountDetails;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodSnapshot;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentStatus;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentStatusEvent;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeRefund;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeRiskData;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransaction;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransactionConnection;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransactionConnectionEdge;
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.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

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

    public static final String TIME_STAMP = "TimeStamp";
    public static final String END_CURSOR = "EndCursor";
    public static final String DATE = "Date";
    public static final String LEGACY_ID = "LegacyId";
    public static final String ID = "Id";
    public static final String PAY_PAL_ACCOUNT_DETAILS = "PayPalAccountDetails";
    public static final String PAYER_EMAIL = "PayerEmail";
    public static final String USD = "USD";
    public static final String AMOUNT = "100";
    public static final String CAPTURE_ID = "CaptureId";
    private static final String FIRST_NAME = "Tester";
    public static final String LAST_NAME = "Testerenko";
    public static final String DECISION = "Decision";
    public static final String REFUND_ID = "RefundId";
    public static final String REFUND_LEGACY_ID = "RefundLegacyId";

    @InjectMocks
    @Spy
    private BraintreeGraphQLTransactionPopulator unit;

    @Mock
    private BrainTreeTransactionConnection brainTreeTransactionConnection;
    @Mock
    private BrainTreePageInfo pageInfo;
    @Mock
    private BrainTreeTransactionConnectionEdge edge;
    @Mock
    private List<BrainTreeTransactionConnectionEdge> edges;
    @Mock
    private Iterator<BrainTreeTransactionConnectionEdge> edgeIterator;
    @Mock
    private List<BrainTreeRefund> refundList;
    @Mock
    private BrainTreeRefund refund;
    @Mock
    private Iterator<BrainTreeRefund> refundIterator;
    @Mock
    private List<BrainTreePaymentStatusEvent> paymentStatusEventList;
    @Mock
    private BrainTreePaymentStatusEvent paymentStatusEvent;
    @Mock
    private Iterator<BrainTreePaymentStatusEvent> paymentStatusEventIterator;
    @Mock
    private BrainTreeTransaction node;
    @Mock
    private BrainTreePaymentMethodSnapshot paymentMethodSnapshot;
    @Mock
    private BrainTreePayPalAccountDetails brainTreePayPalAccountDetails;
    @Mock
    private BrainTreeMonetaryAmount brainTreeMonetaryAmount;
    @Mock
    private BrainTreeCustomer customer;
    @Mock
    private BrainTreeRiskData riskData;
    @Mock
    private BraintreeTransactionEntryDetailData transactionEntryDetailData;
    @Mock
    private Calendar calendar;

    @Before
    public void setUp(){
        doReturn(DATE).when(unit).getFormedDate(node);
        doReturn(transactionEntryDetailData).when(unit).getCoverDetails(node);
        doReturn(calendar).when(unit).getParseDateTime(TIME_STAMP);

        when(brainTreeTransactionConnection.getPageInfo()).thenReturn(pageInfo);
        when(pageInfo.isHasNextPage()).thenReturn(true);
        when(pageInfo.getEndCursor()).thenReturn(END_CURSOR);
        when(brainTreeTransactionConnection.getEdges()).thenReturn(edges);
        when(edges.iterator()).thenReturn(edgeIterator);
        when(edgeIterator.hasNext()).thenReturn(true, false);
        when(edgeIterator.next()).thenReturn(edge);
        when(edge.getNode()).thenReturn(node);
        when(node.getPaymentMethodSnapshot()).thenReturn(paymentMethodSnapshot);
        when(node.getLegacyId()).thenReturn(LEGACY_ID);
        when(node.getId()).thenReturn(ID);
        when(paymentMethodSnapshot.get__typename()).thenReturn(PAY_PAL_ACCOUNT_DETAILS);
        when(paymentMethodSnapshot.getPayer()).thenReturn(brainTreePayPalAccountDetails);
        when(brainTreePayPalAccountDetails.getEmail()).thenReturn(PAYER_EMAIL);
        when(node.getAmount()).thenReturn(brainTreeMonetaryAmount);
        when(brainTreeMonetaryAmount.getCurrencyCode()).thenReturn(USD);
        when(brainTreeMonetaryAmount.getValue()).thenReturn(AMOUNT);
        when(paymentMethodSnapshot.getCaptureId()).thenReturn(CAPTURE_ID);
        when(node.getCustomer()).thenReturn(customer);
        when(customer.getFirstName()).thenReturn(FIRST_NAME);
        when(customer.getLastName()).thenReturn(LAST_NAME);
        when(node.getStatus()).thenReturn(BrainTreePaymentStatus.AUTHORIZED);
        when(node.getRiskData()).thenReturn(riskData);
        when(riskData.getDecision()).thenReturn(DECISION);
        when(node.getRefunds()).thenReturn(refundList);
        when(refundList.iterator()).thenReturn(refundIterator);
        when(refundIterator.hasNext()).thenReturn(true, false);
        when(refundIterator.next()).thenReturn(refund);
        when(refund.getId()).thenReturn(REFUND_ID);
        when(refund.getLegacyId()).thenReturn(REFUND_LEGACY_ID);
        when(node.getStatusHistory()).thenReturn(paymentStatusEventList);
        when(paymentStatusEventList.iterator()).thenReturn(paymentStatusEventIterator);
        when(paymentStatusEventIterator.hasNext()).thenReturn(true, false);
        when(paymentStatusEventIterator.next()).thenReturn(paymentStatusEvent);
        when(paymentStatusEvent.getStatus()).thenReturn(BrainTreePaymentStatus.AUTHORIZED);
        when(paymentStatusEvent.getTimestamp()).thenReturn(TIME_STAMP);
    }

    @Test
    public void shouldPopulateBraintreeGraphQLTransactionTest() {
        BrainTreeGraphQLTransactionData target = new BrainTreeGraphQLTransactionData();

        unit.populate(brainTreeTransactionConnection, target);

        BraintreeTransactionEntryData transactionEntryData = target.getTransactionData().getTransactionEntries().get(0);
        assertTrue(target.getHasNextPage());
        assertEquals(END_CURSOR, target.getEndCursor());
        assertEquals(LEGACY_ID, transactionEntryData.getId());
        assertEquals(ID, transactionEntryData.getIdGraphQL());
        assertEquals(DATE, transactionEntryData.getDate());
        assertEquals(PAYER_EMAIL, transactionEntryData.getPaymentInfo());
        assertEquals(String.format("%s %s", AMOUNT, USD), transactionEntryData.getAmount());
        assertEquals(new BigDecimal(AMOUNT), transactionEntryData.getTotal());
        assertEquals(CAPTURE_ID, transactionEntryData.getCaptureId());
        assertEquals(String.format("%s %s", FIRST_NAME, LAST_NAME), transactionEntryData.getCustomer());
        assertEquals(DECISION, transactionEntryData.getRiskDecision());
        assertEquals(REFUND_ID, transactionEntryData.getRefundIds().get(0).getRequestIdGraphQL());
        assertEquals(REFUND_LEGACY_ID, transactionEntryData.getRefundIds().get(0).getRequestId());
        assertEquals(BraintreePaymentTransactionStatus.AUTHORIZED, transactionEntryData.getStatusHistory().get(0).getStatus());
        assertEquals(calendar.getTime(), transactionEntryData.getStatusHistory().get(0).getTimestamp());
    }

    @Test
    public void shouldPopulateBraintreeGraphQLTransactionWithSomeNullValuesTest() {
        BrainTreeGraphQLTransactionData target = new BrainTreeGraphQLTransactionData();
        when(node.getCustomer()).thenReturn(null);
        when(node.getRiskData()).thenReturn(null);
        when(node.getRefunds()).thenReturn(null);
        when(node.getStatusHistory()).thenReturn(null);

        unit.populate(brainTreeTransactionConnection, target);

        BraintreeTransactionEntryData transactionEntryData = target.getTransactionData().getTransactionEntries().get(0);
        assertTrue(target.getHasNextPage());
        assertEquals(END_CURSOR, target.getEndCursor());
        assertEquals(LEGACY_ID, transactionEntryData.getId());
        assertEquals(ID, transactionEntryData.getIdGraphQL());
        assertEquals(DATE, transactionEntryData.getDate());
        assertEquals(PAYER_EMAIL, transactionEntryData.getPaymentInfo());
        assertEquals(String.format("%s %s", AMOUNT, USD), transactionEntryData.getAmount());
        assertEquals(new BigDecimal(AMOUNT), transactionEntryData.getTotal());
        assertEquals(CAPTURE_ID, transactionEntryData.getCaptureId());

        assertNull(transactionEntryData.getRiskDecision());
        assertNull(transactionEntryData.getCustomer());
        assertEquals(0, transactionEntryData.getStatusHistory().size());
        assertEquals(0, transactionEntryData.getStatusHistory().size());
    }


}