/**
 *
 */
package com.braintreeb2bocc.controllers;

import com.braintree.facade.BrainTreeB2BCheckoutFacade;
import com.braintree.facade.impl.DefaultBrainTreeCheckoutFacade;
import com.braintree.facade.impl.DefaultBrainTreePaymentFacade;
import com.braintree.hybris.data.ws.LocalPaymentRequestWsDTO;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintreeocc.controllers.BraintreeBaseCommerceController;
import de.hybris.platform.commercefacades.order.data.OrderData;
import de.hybris.platform.commercefacades.storesession.StoreSessionFacade;
import de.hybris.platform.commerceservices.order.CommerceCartService;
import de.hybris.platform.commerceservices.request.mapping.annotation.RequestMappingOverride;
import de.hybris.platform.commerceservices.service.data.CommerceCartParameter;
import de.hybris.platform.commercewebservicescommons.dto.order.OrderWsDTO;
import de.hybris.platform.commercewebservicescommons.errors.exceptions.PaymentAuthorizationException;
import de.hybris.platform.commercewebservicescommons.errors.exceptions.RequestParameterException;
import de.hybris.platform.core.model.order.CartModel;
import de.hybris.platform.core.model.order.payment.PaymentInfoModel;
import de.hybris.platform.order.InvalidCartException;
import de.hybris.platform.order.exceptions.CalculationException;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.webservicescommons.cache.CacheControl;
import de.hybris.platform.webservicescommons.cache.CacheControlDirective;
import de.hybris.platform.webservicescommons.swagger.ApiBaseSiteIdParam;
import de.hybris.platform.webservicescommons.swagger.ApiBaseSiteIdUserIdAndCartIdParam;
import de.hybris.platform.webservicescommons.swagger.ApiFieldsParam;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.apache.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@RequestMapping(value = "/{baseSiteId}")
@CacheControl(directive = CacheControlDirective.NO_CACHE)
@Tag(name = "Braintree Local PaymentMethods")
public class BraintreeB2BLocalPaymentMethodsController extends BraintreeBaseCommerceController {

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

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

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

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

    @Resource(name = "storeSessionFacade")
    private StoreSessionFacade storeSessionFacade;

    @Resource(name = "commonI18NService")
    private CommonI18NService commonI18NService;

    @Resource(name = "commerceCartService")
    private CommerceCartService commerceCartService;

    @Secured({ "ROLE_CUSTOMERGROUP", "ROLE_GUEST", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT", "ROLE_ANONYMOUS" })
    @RequestMapping(value = "/braintree/localPaymentMethods/processFallback", method = RequestMethod.POST)
    @ResponseBody
    @ApiBaseSiteIdParam
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMappingOverride
    public OrderWsDTO processFallback(
            @RequestParam(required = false) String deviceData,
                                      @Parameter(description = "Request body parameter that contains details such as payment " +
                                        "method nonce (nonce), correlationId, paymentId and local payment details\n\nThe" +
                                        " DTO is in XML or .json format.", required = true) @RequestBody final
                                LocalPaymentRequestWsDTO localPaymentRequest, @RequestParam final String currency,
                                @ApiFieldsParam @RequestParam(defaultValue = DEFAULT_FIELD_SET) final String fields)
            throws PaymentAuthorizationException, InvalidCartException {

        final CartModel cartByPaymentId = brainTreePaymentFacade.getCartByPaymentId(localPaymentRequest.getCorrelationId());
        if (cartByPaymentId != null){

            if (currency != null && !currency.equals(storeSessionFacade.getCurrentCurrency().getIsocode())) {
                setCurrencyAndRecalculateCart(currency, cartByPaymentId);
            }

            final PaymentInfoModel paymentInfoModel = brainTreePaymentFacade
                    .updateLocalPaymentMethodSubscription(localPaymentRequest.getNonce(), cartByPaymentId);

            if (paymentInfoModel instanceof BrainTreePaymentInfoModel){

                brainTreePaymentFacade.setDeviceData(deviceData, (BrainTreePaymentInfoModel)paymentInfoModel);
            }

            final boolean isAuthorized = brainTreeCheckoutFacade.authorizePayment(cartByPaymentId);

            if (!isAuthorized){
                throw new PaymentAuthorizationException();
            }
            final OrderData orderData= brainTreeB2BCheckoutFacade.placeOrderByCart(cartByPaymentId);

            LOG.info("Order has been placed, number/code: " + orderData.getCode());

            return getDataMapper().map(orderData, OrderWsDTO.class, fields);
        }

        throw new RequestParameterException("Cannot find cart with provided paymentId");

    }

    private void setCurrencyAndRecalculateCart(final String currencyCode, final CartModel cart){

            storeSessionFacade.setCurrentCurrency(currencyCode);

            try {
                cart.setCurrency(commonI18NService.getCurrency(currencyCode));
                final CommerceCartParameter parameter = new CommerceCartParameter();
                parameter.setEnableHooks(true);
                parameter.setCart(cart);
                commerceCartService.recalculateCart(parameter);
            } catch (final CalculationException e) {
                LOG.warn("Could not recalculate the session cart.", e);
            }
    }

}
