package com.braintree.facade.backoffice.impl;

import com.braintree.enums.BraintreePaymentTransactionStatus;
import com.braintree.exceptions.BraintreeErrorException;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.order.adjust.authorization.BraintreeAdjustAuthorizationService;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.payment.enums.PaymentTransactionType;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.servicelayer.model.ModelService;
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.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String TRANSACTION_ID = "transactionId";
    private static final String AMOUNT = "120";

    @Mock
    private ModelService modelService;
    @Mock
    private BraintreeAdjustAuthorizationService braintreeAdjustAuthorizationService;

    @Spy
    @InjectMocks
    private DefaultBraintreeBackofficeAdjustAuthorizationFacade unit;

    @Mock
    private OrderModel orderModel;

    @Mock
    private PaymentTransactionEntryModel transactionEntryModel;

    @Mock
    private BrainTreePaymentInfoModel paymentInfoModel;

    @Mock
    private PaymentTransactionModel paymentTransactionModel;

    private BigDecimal bigDecimalAmount;

    @Before
    public void setUp() {
        bigDecimalAmount = new BigDecimal(AMOUNT);

        when(braintreeAdjustAuthorizationService.getTransactionEntryById(orderModel, TRANSACTION_ID)).thenReturn(transactionEntryModel);
        when(orderModel.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(orderModel.getPaymentTransactions()).thenReturn(List.of(paymentTransactionModel));
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(transactionEntryModel));
    }

    @Test
    public void shouldAdjustAuthorization() throws BraintreeErrorException {
        //when
        unit.adjustAuthorization(orderModel, TRANSACTION_ID, bigDecimalAmount);

        //then
        verify(braintreeAdjustAuthorizationService).adjustAuthorization(orderModel, TRANSACTION_ID, bigDecimalAmount);
    }

    @Test
    public void shouldGetOriginalAmountByTransactionId() {
        //given
        when(transactionEntryModel.getOriginalAmount()).thenReturn(bigDecimalAmount);

        //when
        BigDecimal originalAmountByTransactionId = unit.getOriginalAmountByTransactionId(orderModel, TRANSACTION_ID);

        //then
        assertNotNull(originalAmountByTransactionId);
        assertEquals(bigDecimalAmount, originalAmountByTransactionId);

        verify(braintreeAdjustAuthorizationService).getTransactionEntryById(orderModel, TRANSACTION_ID);
        verify(transactionEntryModel).getOriginalAmount();
    }

    @Test
    public void shouldGetAmountByTransactionId() {
        //given
        when(transactionEntryModel.getAmount()).thenReturn(bigDecimalAmount);

        //when
        BigDecimal originalAmountByTransactionId = unit.getAmountByTransactionId(orderModel, TRANSACTION_ID);

        //then
        assertNotNull(originalAmountByTransactionId);
        assertEquals(bigDecimalAmount, originalAmountByTransactionId);

        verify(braintreeAdjustAuthorizationService).getTransactionEntryById(orderModel, TRANSACTION_ID);
        verify(transactionEntryModel).getAmount();
    }

    @Test
    public void shouldReturnTrueWhenAdjustAuthorizationPossible() {
        //given
        when(braintreeAdjustAuthorizationService.isAdjustAuthorizationPossible(orderModel, paymentTransactionModel)).thenReturn(true);

        //when
        boolean adjustAuthorizationPossible = unit.isAdjustAuthorizationPossible(orderModel);

        //then
        assertTrue(adjustAuthorizationPossible);

        verify(braintreeAdjustAuthorizationService).isAdjustAuthorizationPossible(orderModel, paymentTransactionModel);
        verify(modelService).refresh(orderModel);
    }

    @Test
    public void shouldReturnFalseWhenAdjustAuthorizationNotPossible() {
        //given
        when(braintreeAdjustAuthorizationService.isAdjustAuthorizationPossible(orderModel, paymentTransactionModel)).thenReturn(false);

        //when
        boolean adjustAuthorizationPossible = unit.isAdjustAuthorizationPossible(orderModel);

        //then
        assertFalse(adjustAuthorizationPossible);

        verify(braintreeAdjustAuthorizationService).isAdjustAuthorizationPossible(orderModel, paymentTransactionModel);
        verify(modelService).refresh(orderModel);
    }

    @Test
    public void shouldReturnFalseWhenOrderIsNull() {
        //when
        boolean adjustAuthorizationPossible = unit.isAdjustAuthorizationPossible(null);

        //then
        assertFalse(adjustAuthorizationPossible);

        verify(braintreeAdjustAuthorizationService, never()).isAdjustAuthorizationPossible(orderModel, paymentTransactionModel);
        verify(modelService, never()).refresh(orderModel);
    }

    @Test
    public void shouldGetAuthorizedTransactions() {
        //given
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.AUTHORIZED);
        when(transactionEntryModel.getType()).thenReturn(PaymentTransactionType.AUTHORIZATION);

        //when
        List<PaymentTransactionEntryModel> result = unit.getAuthorizedTransactions(orderModel);

        //then
        assertNotNull(result);
        assertEquals(1, result.size());
        assertEquals(transactionEntryModel, result.get(0));
    }

    @Test
    public void shouldNotGetAuthorizedTransactionWhenTransactionEntryHasNotAuthorizationType() {
        //given
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.AUTHORIZED);
        when(transactionEntryModel.getType()).thenReturn(PaymentTransactionType.CAPTURE);

        //when
        List<PaymentTransactionEntryModel> result = unit.getAuthorizedTransactions(orderModel);

        //then
        assertNotNull(result);
        assertTrue(result.isEmpty());
    }

    @Test
    public void shouldNotGetAuthorizedTransactionWhenTransactionPaymentStatusNotAuthorized() {
        //given
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.VOIDED);

        //when
        List<PaymentTransactionEntryModel> result = unit.getAuthorizedTransactions(orderModel);

        //then
        assertNotNull(result);
        assertTrue(result.isEmpty());

        verify(paymentTransactionModel, never()).getEntries();
    }

}
