/**
 *
 */
package com.braintree.controllers;

import com.braintree.email.emailServices.BraintreeLocalPaymentReversedEmailEventService;
import com.braintree.enums.BTFraudToolReviewStatus;
import com.braintree.facade.BrainTreeB2BCheckoutFacade;
import com.braintree.facade.BrainTreeOrderFacade;
import com.braintree.facade.impl.DefaultBrainTreeCheckoutFacade;
import com.braintree.facade.impl.DefaultBrainTreePaymentFacade;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import com.braintree.replenishment.service.BrainTreeReplenishmentService;
import com.braintree.service.BrainTreeFraudCheckToolService;
import com.braintree.transaction.service.BrainTreeTransactionService;
import com.braintreegateway.RevokedPaymentMethodMetadata;
import com.braintreegateway.TransactionReview;
import com.braintreegateway.WebhookNotification;
import de.hybris.platform.commercefacades.order.data.OrderData;
import de.hybris.platform.core.model.order.CartModel;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.order.InvalidCartException;
import de.hybris.platform.servicelayer.exceptions.ModelNotFoundException;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.site.BaseSiteService;
import java.util.Optional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

@Controller
@RequestMapping(value = "/paypal/webhook")
public class BraintreeB2BWebhookListener {

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

    private static final String SUCCESS_BODY_RESPONSE = "200";

    @Resource(name = "brainTreeCheckoutFacade")
    private DefaultBrainTreeCheckoutFacade brainTreeCheckoutFacade;

    @Resource(name = "brainTreePaymentFacadeImpl")
    private DefaultBrainTreePaymentFacade brainTreePaymentFacade;

    @Resource(name = "brainTreeB2BCheckoutFacade")
    private BrainTreeB2BCheckoutFacade brainTreeB2BCheckoutFacade;

    @Resource(name = "brainTreeOrderFacade")
    private BrainTreeOrderFacade brainTreeOrderFacade;

    @Resource(name = "baseSiteService")
    private BaseSiteService baseSiteService;

    @Resource(name = "paymentInfoService")
    private BraintreePaymentInfoService paymentInfoService;

    @Resource(name = "brainTreeReplenishmentService")
    private BrainTreeReplenishmentService replenishmentService;

    @Resource(name = "localPaymentReversedEmailEventService")
    private BraintreeLocalPaymentReversedEmailEventService localPaymentReversedEmailEventService;

    @Resource
    private BrainTreeFraudCheckToolService btFraudCheckToolService;

    @Resource
    private BrainTreeTransactionService brainTreeTransactionService;

    @Secured({"ROLE_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT",
        "ROLE_ANONYMOUS"})
    @PostMapping(value = "/response")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<String> doHandleBillingAgreemenHopResponse(final HttpServletRequest request,
        final HttpServletResponse response) throws InvalidCartException {
        final WebhookNotification webhookNotification = brainTreePaymentFacade.getWebhookNotification(request);
        final String paymentId = webhookNotification.getLocalPaymentCompleted().getPaymentId();
        final String paymentMethodNonce = webhookNotification.getLocalPaymentCompleted().getPaymentMethodNonce();

        final CartModel cart = brainTreePaymentFacade.getCartByPaymentId(paymentId);

        if (cart != null) {
            baseSiteService.setCurrentBaseSite(cart.getSite(), true);
            brainTreePaymentFacade.updateLocalPaymentMethodSubscription(paymentMethodNonce, cart);

            final boolean authorized = brainTreeCheckoutFacade.authorizePayment(cart);
            if (authorized) {
                final OrderData orderData = brainTreeB2BCheckoutFacade.placeOrderByCart(cart);
                LOG.info(orderData.getCode() + " was placed due to the webhook  : " + webhookNotification.getKind());
            }
        }

        return ResponseEntity.ok(SUCCESS_BODY_RESPONSE);
    }

    @Secured({"ROLE_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT",
            "ROLE_ANONYMOUS"})
    @PostMapping(value = "/localPayment/reversed")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<String> handleLocalPaymentRefund(final HttpServletRequest request,
                                                           final HttpServletResponse response) throws InvalidCartException {
        final WebhookNotification webhookNotification = brainTreePaymentFacade.getWebhookNotification(request);

        if (WebhookNotification.Kind.LOCAL_PAYMENT_REVERSED.equals(webhookNotification.getKind())) {
            LOG.info(webhookNotification);
            final String paymentId = webhookNotification.getLocalPaymentReversed().getPaymentId();

            final CartModel cartByPaymentId = brainTreePaymentFacade.getCartByPaymentId(paymentId);

            if(cartByPaymentId != null){
                baseSiteService.setCurrentBaseSite(cartByPaymentId.getSite(), true);
                brainTreeCheckoutFacade.processLocalPaymentReversedWebhook(cartByPaymentId);
                final OrderModel orderModel = brainTreeB2BCheckoutFacade.placeOrderForLocalPaymentReversed(cartByPaymentId);

                localPaymentReversedEmailEventService
                        .sendBraintreeLocalPaymentReversedEmailEvent((CustomerModel) cartByPaymentId.getUser(), orderModel);

            }
        }
        return ResponseEntity.ok(SUCCESS_BODY_RESPONSE);
    }

    @Secured({"ROLE_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT",
        "ROLE_ANONYMOUS"})
    @PostMapping(value = "/payment-method")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<String> doHandlePaymentMethodWebhook(final HttpServletRequest request,
        final HttpServletResponse response) {
        final WebhookNotification webhookNotification = brainTreePaymentFacade.getWebhookNotification(request);
        final RevokedPaymentMethodMetadata revokedPaymentMethodMetadata = webhookNotification
            .getRevokedPaymentMethodMetadata();
        if (revokedPaymentMethodMetadata != null) {
            final String paymentMethodToken = Optional.of(revokedPaymentMethodMetadata.getToken()).get();
            final String customerId = Optional.of(revokedPaymentMethodMetadata.getCustomerId()).get();
            replenishmentService.disableReplenishmentCronJobByPaymentMethodToken(paymentMethodToken);
            paymentInfoService.disable(customerId, paymentMethodToken);
            LOG.info("Payment method with payment method token: " + paymentMethodToken +
                " and braintree customer id: " + customerId + " has been disabled by payment method webhook");
        }
        return ResponseEntity.ok(SUCCESS_BODY_RESPONSE);
    }

    @Secured({"ROLE_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT",
            "ROLE_ANONYMOUS"})
    @PostMapping(value = "/fraud-check")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<String> doHandleBtFraudCheckWebhook(final HttpServletRequest request) {
        LOG.info("Transaction reviewed notification was received");
        WebhookNotification webhookNotification = brainTreePaymentFacade.getWebhookNotification(request);
        LOG.info(webhookNotification);
        if (webhookNotification.getKind().equals(WebhookNotification.Kind.TRANSACTION_REVIEWED)) {
            TransactionReview transactionReview = webhookNotification.getTransactionReview();
            String transactionId = transactionReview.getTransactionId();
            PaymentTransactionModel transaction = brainTreeTransactionService.findTransactionByTransactionId(transactionId);
            BTFraudToolReviewStatus decision =
                    btFraudCheckToolService.translateStatusFromBtToHybris(transactionReview.getDecision());

            btFraudCheckToolService.updateTransactionStatus(transaction, decision);
            btFraudCheckToolService.saveBtFraudToolReviewStatus(transaction, decision);
        }

        return ResponseEntity.ok(SUCCESS_BODY_RESPONSE);
    }

    @Secured({"ROLE_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT",
            "ROLE_ANONYMOUS"})
    @PostMapping(value = "/usBankAccount/paymentTransactionStatus")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<String> handleUsBankAccountPaymentTransactionStatus(final HttpServletRequest request) {

        WebhookNotification webhookNotification = brainTreePaymentFacade.getWebhookNotification(request);
        if (WebhookNotification.Kind.TRANSACTION_SETTLED.equals(webhookNotification.getKind())
                || WebhookNotification.Kind.TRANSACTION_SETTLEMENT_DECLINED.equals(webhookNotification.getKind())) {
            try {
                brainTreeOrderFacade.updateOrderStatusWithUsBankAccountPaymentMethod(webhookNotification);
            } catch (ModelNotFoundException exception) {
                LOG.error("[Webhook UsBankAccount] Payment transaction with id: "
                        + webhookNotification.getTransaction().getId() + " does not exist locally");
            }
        } else {
            LOG.info("[Webhook UsBankAccount] This type of webhook is not supported");
        }
        return ResponseEntity.ok(SUCCESS_BODY_RESPONSE);
    }
}
