package ru.yandex.direct.web.entity.payment.controller;

import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import ru.yandex.direct.core.entity.client.service.ClientService;
import ru.yandex.direct.core.entity.feature.service.FeatureService;
import ru.yandex.direct.core.entity.payment.model.AutopayParams;
import ru.yandex.direct.core.entity.payment.model.AutopaySettingsPaymethodType;
import ru.yandex.direct.core.entity.payment.model.PaymentParams;
import ru.yandex.direct.core.entity.payment.service.PaymentService;
import ru.yandex.direct.core.entity.payment.service.PaymentValidationService;
import ru.yandex.direct.core.entity.promocodes.service.PromocodeHelper;
import ru.yandex.direct.core.entity.promocodes.service.PromocodeValidationContainer;
import ru.yandex.direct.core.entity.user.model.User;
import ru.yandex.direct.core.security.DirectAuthentication;
import ru.yandex.direct.core.security.authorization.PreAuthorizeRead;
import ru.yandex.direct.currency.CurrencyCode;
import ru.yandex.direct.feature.FeatureName;
import ru.yandex.direct.rbac.RbacRole;
import ru.yandex.direct.utils.StringUtils;
import ru.yandex.direct.web.annotations.AllowedOperatorRoles;
import ru.yandex.direct.web.annotations.SubjectHasFeatures;
import ru.yandex.direct.web.core.security.DirectWebAuthenticationSource;
import ru.yandex.direct.web.core.security.captcha.DisableAutoCaptcha;
import ru.yandex.direct.web.entity.payment.model.CardBindingFormRequest;
import ru.yandex.direct.web.entity.payment.model.CardBindingFormResponse;
import ru.yandex.direct.web.entity.payment.model.GetBillingDataResponse;
import ru.yandex.direct.web.entity.payment.model.PaymentFormRequest;
import ru.yandex.direct.web.entity.payment.model.PaymentFormResponse;
import ru.yandex.direct.web.validation.kernel.ValidationResultConversionService;

import static ru.yandex.direct.web.core.security.authentication.DirectCookieAuthProvider.PARAMETER_ULOGIN;
import static ru.yandex.direct.web.entity.payment.util.PaymentUtils.PAYMENT_PATH;
import static ru.yandex.direct.web.entity.payment.util.PaymentUtils.getSuccessRedirectUri;

@Controller
@AllowedOperatorRoles({RbacRole.SUPER, RbacRole.CLIENT})
@RequestMapping(value = PAYMENT_PATH, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class PaymentController {
    private final static Logger LOGGER = LoggerFactory.getLogger(PaymentController.class);

    private final DirectWebAuthenticationSource authenticationSource;
    private final ClientService clientService;
    private final PaymentValidationService paymentValidationService;
    private final PaymentService paymentService;
    private final PromocodeHelper promocodeHelper;
    private final ValidationResultConversionService validationResultConversionService;
    private final FeatureService featureService;

    @Autowired
    public PaymentController(DirectWebAuthenticationSource authenticationSource,
                             ClientService clientService,
                             PaymentValidationService paymentValidationService,
                             PaymentService paymentService,
                             PromocodeHelper promocodeHelper,
                             ValidationResultConversionService validationResultConversionService,
                             FeatureService featureService) {
        this.authenticationSource = authenticationSource;
        this.clientService = clientService;
        this.paymentValidationService = paymentValidationService;
        this.paymentService = paymentService;
        this.promocodeHelper = promocodeHelper;
        this.validationResultConversionService = validationResultConversionService;
        this.featureService = featureService;
    }

    @ApiOperation(
            value = "form",
            httpMethod = "POST"
    )
    @PreAuthorizeRead
    @DisableAutoCaptcha
    @PostMapping(path = "/form")
    @ResponseBody
    public PaymentFormResponse form(@RequestBody PaymentFormRequest paymentFormRequest,
                                    @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {

        DirectAuthentication auth = authenticationSource.getAuthentication();
        User subjectUser = auth.getSubjectUser();
        Long operatorUid = auth.getOperator().getUid();

        paymentFormRequest.setPromocode(StringUtils.nullIfBlank(paymentFormRequest.getPromocode()));
        CurrencyCode clientCurrency = clientService.getWorkCurrency(subjectUser.getClientId()).getCode();

        PromocodeValidationContainer promocodeValidationContainer = promocodeHelper
                .preparePromocodeValidationContainer(subjectUser.getClientId());
        boolean useExtendedCodes = featureService.isEnabledForClientId(subjectUser.getClientId(),
                FeatureName.USE_EXTENDED_ERROR_CODES_FOR_PROMOCODES_ERRORS);

        var paymentParams = new PaymentParams()
                .withSum(paymentFormRequest.getSum())
                .withPaymentSum(paymentFormRequest.getPaymentSum())
                .withRemainingSum(paymentFormRequest.getRemainingSum())
                .withPromocode(paymentFormRequest.getPromocode())
                .withIsMobile(paymentFormRequest.getIsMobile());

        var validationResult = paymentValidationService.validate(
                paymentParams,
                clientCurrency,
                promocodeValidationContainer,
                useExtendedCodes);

        if (validationResult.hasAnyErrors()) {
            LOGGER.error("errors in web payment validation: " + validationResult.flattenErrors().get(0).toString());
            return new PaymentFormResponse(null, null,
                    validationResultConversionService.buildWebValidationResult(validationResult), null);
        }

        AutopayParams autopayParams = new AutopayParams()
                .withPaymentSum(paymentFormRequest.getPaymentSum())
                .withRemainingSum(paymentFormRequest.getRemainingSum())
                .withCardId(paymentFormRequest.getCardId())
                .withPaymentType(AutopaySettingsPaymethodType.CARD);

        String successRedirectUrl = getSuccessRedirectUri();

        var result = paymentService.enableAutopayIfPossibleAndGetUrls(
                subjectUser, operatorUid, clientCurrency,
                paymentFormRequest.getSum(), paymentFormRequest.getIsLegalPerson(), autopayParams,
                paymentFormRequest.getPromocode(), promocodeValidationContainer, paymentFormRequest.getIsMobile(),
                successRedirectUrl);

        return new PaymentFormResponse(
                result.paymentUrl,
                result.cardBindingUrl,
                validationResultConversionService.buildWebValidationResult(result.validation),
                result.amount);
    }

    @ApiOperation(
            value = "bindingForm",
            httpMethod = "POST"
    )
    @PreAuthorizeRead
    @DisableAutoCaptcha
    @PostMapping(path = "/bindingForm")
    @ResponseBody
    public CardBindingFormResponse bindingForm(@RequestBody CardBindingFormRequest request,
                                               @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin,
                                               @RequestHeader(value = "Referer", required = false) String referer) {

        DirectAuthentication auth = authenticationSource.getAuthentication();
        User subjectUser = auth.getSubjectUser();
        Long operatorUid = auth.getOperator().getUid();

        CurrencyCode clientCurrency = clientService.getWorkCurrency(subjectUser.getClientId()).getCode();

        var cardBindingUrl = paymentService.getBindingForm(subjectUser, operatorUid, clientCurrency,
                request.getIsLegalPerson(),
                request.getIsMobile(),
                referer);

        return new CardBindingFormResponse(cardBindingUrl);
    }

    @SubjectHasFeatures(FeatureName.NEW_PAYMENT_WORKFLOW_ENABLED)
    @ApiOperation(
            value = "getBillingData",
            httpMethod = "GET"
    )
    @PreAuthorizeRead
    @DisableAutoCaptcha
    @GetMapping(path = "getBillingData")
    @ResponseBody
    public GetBillingDataResponse getBillingData(@RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin,
                                                 @RequestParam(value = "is_mobile", required = false) Boolean isMobile) {
        DirectAuthentication auth = authenticationSource.getAuthentication();
        User subjectUser = auth.getSubjectUser();
        Long operatorUid = auth.getOperator().getUid();
        var billingData = paymentService.getBillingData(subjectUser, operatorUid);
        return new GetBillingDataResponse()
            .withBoundCards(billingData.getBoundCards())
            .withHasSingleLegalPerson(billingData.getHasSingleLegalPerson());
    }
}
