package com.braintree.actions.order;

import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.orderprocessing.model.OrderProcessModel;
import de.hybris.platform.payment.dto.TransactionStatus;
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 de.hybris.platform.task.RetryLaterException;
import de.hybris.platform.ticket.enums.CsTicketCategory;
import de.hybris.platform.ticket.enums.CsTicketPriority;
import de.hybris.platform.ticket.events.model.CsCustomerEventModel;
import de.hybris.platform.ticket.model.CsTicketModel;
import de.hybris.platform.ticket.service.TicketBusinessService;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.List;
import java.util.Set;

import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


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

    private static final Logger LOG = Logger.getLogger(BrainTreeCheckTransactionReviewStatusActionTest.class);

    private static final String OK = "OK"; //NOPMD
    private static final String NOK = "NOK";
    private static final String WAIT = "WAIT";
    private static final String SUBJECT = "subject";
    private static final String DESCRIPTION = "description";
    private static final String RETRY_LATER_EXCEPTION_MSG = "RetryLaterException: ";
    private static final String UNEXPECTED_EXCEPTION_MSG = "Unexpected exception: ";
    private static final String EXCEPTION_LOG_MSG = "Exception : ";

    @Mock
    private ModelService modelService;
    @Mock
    private TicketBusinessService ticketBusinessService;

    @InjectMocks
    private BrainTreeCheckTransactionReviewStatusAction action;

    @Mock
    private OrderModel order;
    @Mock
    private CsTicketCategory csTicketCategory;
    @Mock
    private CsTicketPriority csTicketPriority;
    @Mock
    private CsTicketModel csTicketModel;
    @Mock
    private CustomerModel customerModel;
    @Mock
    private OrderProcessModel process;
    @Mock
    private PaymentTransactionModel paymentTransactionModel;
    @Mock
    private PaymentTransactionEntryModel authorizationAccepted;
    @Mock
    private PaymentTransactionEntryModel authorizationReview;
    @Mock
    private PaymentTransactionEntryModel reviewAccepted;
    @Mock
    private PaymentTransactionEntryModel reviewRejected;
    @Mock
    private PaymentTransactionEntryModel authorizationReviewError;

    protected void mockPaymentTransactionEntry(PaymentTransactionEntryModel entryModel, final PaymentTransactionType type,
                                               final TransactionStatus status) {
        when(entryModel.getType()).thenReturn(type);
        when(entryModel.getTransactionStatus()).thenReturn(status.toString());
    }

    @Before
    public void setUp() {
        mockPaymentTransactionEntry(authorizationAccepted, PaymentTransactionType.AUTHORIZATION, TransactionStatus.ACCEPTED);
        mockPaymentTransactionEntry(authorizationReview, PaymentTransactionType.AUTHORIZATION, TransactionStatus.REVIEW);
        mockPaymentTransactionEntry(reviewAccepted, PaymentTransactionType.REVIEW_DECISION, TransactionStatus.ACCEPTED);
        mockPaymentTransactionEntry(reviewRejected, PaymentTransactionType.REVIEW_DECISION, TransactionStatus.REJECTED);
        mockPaymentTransactionEntry(authorizationReviewError, PaymentTransactionType.AUTHORIZATION, TransactionStatus.REJECTED);

        when(process.getOrder()).thenReturn(order);
        when(order.getPaymentTransactions()).thenReturn(List.of(paymentTransactionModel));
    }

    @Test
    public void testAcceptedAuthorization() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationAccepted));
        try {
            final String result = action.execute(process);
            assertEquals(OK, result);
        } catch (final RetryLaterException e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        } catch (final Exception e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        }
    }

    @Test
    public void testReviewAuthorization() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(reviewAccepted));
        try {
            final String result = action.execute(process);

            assertEquals(OK, result);
        } catch (RetryLaterException e) {
            LOG.error(RETRY_LATER_EXCEPTION_MSG, e);
        } catch (Exception e) {
            LOG.error(UNEXPECTED_EXCEPTION_MSG, e);
        }
    }

    @Test
    public void testReviewAuthorizationError() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationReviewError));
        try {
            final String result = action.execute(process);

            assertEquals(OK, result);
        } catch (RetryLaterException e) {
            LOG.error(RETRY_LATER_EXCEPTION_MSG, e);
        } catch (Exception e) {
            LOG.error(UNEXPECTED_EXCEPTION_MSG, e);
        }
    }

    @Test
    public void testAcceptedReviewAuthorization() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationReview, reviewAccepted));
        try {
            final String result = action.execute(process);
            assertEquals(OK, result);
        } catch (final RetryLaterException e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        } catch (final Exception e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        }
    }

    @Test
    public void testRejectedReviewAuthorization() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationReview, reviewRejected));
        try {
            final String result = action.execute(process);
            assertEquals(NOK, result);
        } catch (final RetryLaterException e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        } catch (final Exception e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        }
    }

    @Test
    public void testMultipleReviewAuthorization() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationReview, reviewAccepted));
        try {
            final String result = action.execute(process);
            assertEquals(OK, result);
        } catch (final RetryLaterException e) {
            LOG.error(RETRY_LATER_EXCEPTION_MSG, e);
            fail();
        } catch (final Exception e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        }
    }

    @Test
    public void testMultipleReview() {
        when(paymentTransactionModel.getEntries()).thenReturn(List.of(authorizationReview, reviewRejected,
                authorizationReview, reviewAccepted));
        try {
            final String result = action.execute(process);
            assertEquals(OK, result);
        } catch (final RetryLaterException e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        } catch (final Exception e) {
            LOG.error(EXCEPTION_LOG_MSG, e);
            fail();
        }
    }

    @Test
    public void shouldCreateTicket() {
        //given
        when(modelService.create(CsTicketModel.class)).thenReturn(csTicketModel);
        when(order.getUser()).thenReturn(customerModel);

        //when
        action.createTicket(SUBJECT, DESCRIPTION, order, csTicketCategory, csTicketPriority);


        //then
        verify(modelService).create(CsTicketModel.class);
        verify(csTicketModel).setCategory(csTicketCategory);
        verify(csTicketModel).setHeadline(SUBJECT);
        verify(csTicketModel).setPriority(csTicketPriority);
        verify(csTicketModel).setOrder(order);
        verify(csTicketModel).setCustomer(customerModel);
        verify(ticketBusinessService).createTicket(eq(csTicketModel), any(CsCustomerEventModel.class));

    }

    @Test
    public void shouldGetTransition() {
        //when
        Set<String> transitions = action.getTransitions();

        //then
        assertTrue(transitions.containsAll(List.of(OK, NOK, WAIT)));
    }

}
