package com.izouma.nineth.service;

import com.alibaba.fastjson15.JSON;
import com.alibaba.fastjson15.JSONObject;
import com.alibaba.fastjson15.parser.Feature;
import com.izouma.nineth.config.*;
import com.izouma.nineth.dto.BindCardRequest;
import com.izouma.nineth.dto.PayQuery;
import com.izouma.nineth.enums.PayStatus;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import com.upay.sdk.CipherWrapper;
import com.upay.sdk.ConfigurationUtils;
import com.upay.sdk.HttpClientUtils;
import com.upay.sdk.cashier.bindcard.builder.BindCardBuilder;
import com.upay.sdk.cashier.bindcard.builder.BindCardConfirmBuilder;
import com.upay.sdk.cashier.bindcard.builder.BindCardKaptchaBuilder;
import com.upay.sdk.cashier.bindcard.builder.UnBindCardBuilder;
import com.upay.sdk.cashier.order.builder.ReceiptPaymentBuilder;
import com.upay.sdk.entity.Payer;
import com.upay.sdk.entity.ProductDetail;
import com.upay.sdk.entity.TransferDomesticPayee;
import com.upay.sdk.onlinepay.builder.OrderBuilder;
import com.upay.sdk.onlinepay.builder.QueryBuilder;
import com.upay.sdk.onlinepay.builder.RefundBuilder;
import com.upay.sdk.transferDomestic.builder.AccountBuilder;
import com.upay.sdk.transferDomestic.builder.SingleOrderBuilder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
@AllArgsConstructor
@EnableConfigurationProperties({PayEaseProperties.class})
public class PayEaseService {

    private PayEaseProperties             payEaseProperties;
    private RedisTemplate<String, Object> redisTemplate;
    private SnowflakeIdWorker             snowflakeIdWorker;

    public JSONObject request(String url, JSONObject requestData) {
        log.info("requestData: {}", JSON.toJSONString(requestData, true));
        String responseStr = HttpClientUtils.post3(url, requestData);
        log.info("responseStr: \n{}", responseStr);
        JSONObject responseData = JSONObject.parseObject(responseStr, Feature.SortFeidFastMatch);
        responseData = CipherWrapper.bothDecryptWrap(responseData);
        log.info("responseStr: {}", JSON.toJSONString(responseData, true));
        return responseData;
    }

    public String bindCard(BindCardRequest bindCardRequest) {
        BindCardBuilder builder = new BindCardBuilder(payEaseProperties.getMerchantId());
        builder.setMerchantUserId(bindCardRequest.getUserId())
                .setBankCardNumber(bindCardRequest.getBankNo())
                .setPhoneNumber(bindCardRequest.getPhone())
                .setName(bindCardRequest.getName())
                .setIdCardNum(bindCardRequest.getIdNo());
        JSONObject requestData = builder.bothEncryptBuild();
        JSONObject responseData = request(ConfigurationUtils.getCashierBindCardUrl(), requestData);

        if (!"SUCCESS".equals(responseData.getString("status"))) {
            String error = responseData.getString("error");
            String cause = responseData.getString("cause");
            Pattern p = Pattern.compile("银行卡已绑定！绑卡ID:(\\d+)");
            Matcher m = p.matcher(cause);
            if (m.find()) {
                String bindCardId = m.group(1);
                if (StringUtils.isNotEmpty(bindCardId)) {
                    unbind(bindCardRequest.getUserId(), bindCardId);
                    return bindCard(bindCardRequest);
                }
            }
            throw new BusinessException(error);
        } else {
            String bindStatus = responseData.getString("bindStatus");
            if ("FAIL".equals(bindStatus)) {
                String errorMsg = responseData.getString("errorMsg");
                throw new BusinessException(errorMsg);
            }
            String bindCardId = responseData.getString("bindCardId");
            String bankCardNumber = responseData.getString("bankCardNumber");
            bindCardRequest.setBindCardId(bindCardId);
            redisTemplate.opsForValue().set(RedisKeys.BIND_CARD + bindCardId,
                    bindCardRequest, Duration.ofMinutes(30));
            return bindCardId;
        }
    }

    public BindCardRequest bindCardConfirm(String bindCardId, String code) {
        Object obj = redisTemplate.opsForValue().get(RedisKeys.BIND_CARD + bindCardId);
        Objects.requireNonNull(obj, "绑卡信息不存在");
        BindCardRequest bindCardRequest = (BindCardRequest) obj;


        BindCardConfirmBuilder builder = new BindCardConfirmBuilder(payEaseProperties.getMerchantId());
        builder.setBindCardId(bindCardId)
                .setMerchantUserId(bindCardRequest.getUserId())
                .setKaptchaCode(code);

        JSONObject requestData = builder.bothEncryptBuild();
        JSONObject responseData = request(ConfigurationUtils.getCashierBindCardConfirmUrl(), requestData);

        String status = responseData.getString("status");
        if (!"SUCCESS".equals(status)) {
            String error = responseData.getString("error");
            String cause = responseData.getString("cause");
            throw new BusinessException(error + "[" + cause + "]");
        }
        String bindStatus = responseData.getString("bindStatus");
        if (!"SUCCESS".equals(bindStatus)) {
            String errorMsg = responseData.getString("errorMsg");
            throw new BusinessException(errorMsg);
        }
        return bindCardRequest;
    }

    public void bindCardCaptcha(String bindCardId) {
        BindCardKaptchaBuilder builder = new BindCardKaptchaBuilder(payEaseProperties.getMerchantId());
        builder.setBindCardId(bindCardId);
        JSONObject response = request(ConfigurationUtils.getCashierBindCardKaptchaUrl(), builder.bothEncryptBuild());
        if (!"SUCCESS".equals(response.getString("status"))) {
            String error = response.getString("error");
            String cause = response.getString("cause");
            throw new BusinessException(error);
        }
    }

    public void unbind(String userId, String bindCardId) {

        UnBindCardBuilder builder = new UnBindCardBuilder(payEaseProperties.getMerchantId());
        builder.setBindCardId(bindCardId)
                .setMerchantUserId(userId);
        JSONObject response = request(ConfigurationUtils.getCashierUnBindCardUrl(), builder.bothEncryptBuild());
        if (!"SUCCESS".equals(response.getString("status"))) {
            String error = response.getString("error");
            throw new BusinessException(error);
        }
        String bindStatus = response.getString("bindStatus");
        if (!"UNBIND".equals(bindStatus)) {
            String errorMsg = response.getString("errorMsg");
            throw new BusinessException(errorMsg);
        }
    }


    public Map<String, Object> pay(String subject, String orderId, BigDecimal amount, String userId, String bindCardId,
                                   String type) {
        String amountStr = amount.multiply(new BigDecimal("100")).setScale(0, RoundingMode.FLOOR)
                .stripTrailingZeros().toPlainString();
        //通知地址
        String notifyUrl = payEaseProperties.getNotifyUrl() + "/" + type + "/" + orderId;
        //回调地址
        String callbackUrl = "https://www.raex.vip/9th/home";
        //备注
        String remark = "111";
        //超时时间(分钟)
        String timeout = "3";

        ProductDetail productDetail = new ProductDetail();
        productDetail.setName(subject);
        productDetail.setQuantity(1L);
        productDetail.setAmount(Long.parseLong(amountStr));

        OrderBuilder builder = new OrderBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(orderId)
                .setOrderAmount(amountStr)
                .setOrderCurrency("CNY")
                .setNotifyUrl(notifyUrl)
                .setCallbackUrl(callbackUrl)
                .setRemark(remark)
                .setPaymentModeCode("BANK_CARD-AGREEMENT-P2P")
                .setNotifyUrl(notifyUrl)
                .setMerchantUserId(userId)
                .setBindCardId(bindCardId)
                .setPayer(new Payer())
                .addProductDetail(productDetail)
                .setClientIp("192.168.22.55");
        builder.setTimeout(timeout);

        JSONObject response = request(ConfigurationUtils.getOnlinePayOrderUrl(), builder.bothEncryptBuild());
        if (!"SUCCESS".equals(response.getString("status"))) {
            String error = response.getString("error");
            throw new BusinessException(error);
        }
        redisTemplate.opsForValue()
                .set(RedisKeys.PAY_TMP + orderId, Constants.PayChannel.PE, 1, TimeUnit.DAYS);
        boolean needCaptcha = response.getBoolean("needKaptcha");
        String paymentOrderId = response.getString("paymentOrderId");
        String requestId = response.getString("requestId");
        Map<String, Object> map = new HashMap<>();
        map.put("needCaptcha", needCaptcha);
        map.put("paymentOrderId", paymentOrderId);
        map.put("requestId", requestId);
        map.put("orderId", orderId);
        return map;
    }

    public void payConfirm(String orderId, String paymentOrderId, String code) {
        ReceiptPaymentBuilder builder = new ReceiptPaymentBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(orderId)
                .setPaymentOrderId(paymentOrderId)
                .setKaptchaCode(code);
        JSONObject response = request(ConfigurationUtils.getCashierReceiptPaymentUrl(), builder.bothEncryptBuild());
        String status = response.getString("status");
        if (!"SUCCESS".equals(status)) {
            if ("FAILED".equals(status)) {
                String errorMessage = response.getString("errorMessage");
                throw new BusinessException(errorMessage);
            }
            String error = response.getString("error");
            throw new BusinessException(error);
        }
    }

    public com.alibaba.fastjson.JSONObject refund(String orderId, String transactionId, BigDecimal amount) {
        RefundBuilder builder = new RefundBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(orderId)
                .setAmount(amount.multiply(new BigDecimal("100")).setScale(0, RoundingMode.FLOOR)
                        .stripTrailingZeros().toPlainString())
                .setOrderId(transactionId).setNotifyUrl(payEaseProperties.getNotifyUrl());
        JSONObject response = request(ConfigurationUtils.getOnlinePayRefundUrl(), builder.bothEncryptBuild());
        return com.alibaba.fastjson.JSON.parseObject(response.toJSONString());
    }

    public com.alibaba.fastjson.JSONObject query(String requestId) {
        QueryBuilder builder = new QueryBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(requestId);
        JSONObject response = request(ConfigurationUtils.getOnlinePayQueryUrl(), builder.bothEncryptBuild());
        return com.alibaba.fastjson.JSON.parseObject(response.toJSONString());
    }

    public com.alibaba.fastjson.JSONObject refundQuery(String requestId) {
        RefundBuilder builder = new RefundBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(requestId);
        JSONObject response = request(ConfigurationUtils.getOnlinePayRefundQueryUrl(), builder.bothEncryptBuild());
        return com.alibaba.fastjson.JSON.parseObject(response.toJSONString());
    }

    public PayQuery payQuery(String orderId) {
        com.alibaba.fastjson.JSONObject res = query(orderId);
        PayQuery query = new PayQuery(Constants.PayChannel.PE);
        String status = res.getString("status");
        if ("ERROR".equals(status)) {
            String error = res.getString("error");
            query.setExist(false);
            query.setMsg(error);
        } else {
            query.setExist(true);
            if (res.containsKey("completeDateTime")) {
                query.setPayTime(LocalDateTime.parse(res.getString("completeDateTime"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }
            query.setAmount(new BigDecimal(res.getString("orderAmount")).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
            query.setTransactionId(res.getString("serialNumber"));
            switch (status) {
                case "INIT":
                    query.setStatus(PayStatus.PENDING);
                    break;
                case "SUCCESS":
                    query.setStatus(PayStatus.SUCCESS);
                    break;
                case "FAILED":
                    query.setStatus(PayStatus.FAIL);
                    break;
                case "CANCEL":
                    query.setStatus(PayStatus.CANCEL);
                    break;
            }
            com.alibaba.fastjson.JSONObject res1 = refundQuery(orderId);
            if ("SUCCESS".equals(res1.getString("status"))) {
                query.setStatus(PayStatus.REFUNDED);
            }
        }
        return query;
    }

    public BigDecimal balance() {
        AccountBuilder builder = new AccountBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(new SnowflakeIdWorker(1, 1).nextId() + "");
        JSONObject response = request(ConfigurationUtils.getTransferDomesticAccountAvailableBalanceUrl(), builder.bothEncryptBuild());
        return response.getBigDecimal("availableTransferBalance").divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
    }

    public com.alibaba.fastjson.JSONObject transfer(String id, String name, String bank, BigDecimal amount, String phone) {
        SingleOrderBuilder builder = new SingleOrderBuilder(payEaseProperties.getMerchantId());
        builder.setRequestId(id);
        builder.setAmount(amount.multiply(new BigDecimal("100")).setScale(0, RoundingMode.FLOOR)
                .stripTrailingZeros().toPlainString());
        builder.setCurrency("CNY");
        builder.setNotifyUrl("http://xiongzhu.frp.izouma.com/sandpay/notify");
        TransferDomesticPayee payee = new TransferDomesticPayee();
        payee.setName(name);
        payee.setBankCardNum(bank);
        builder.setPayee(payee);
        JSONObject response = request(ConfigurationUtils.getTransferDomesticSingleOrderUrl(), builder.bothEncryptBuild());

        if (!"SUCCESS".equals(response.getString("status"))) {
            throw new BusinessException(response.getString("error"));
        }

        for (int i = 0; i < 20; i++) {
            com.upay.sdk.transferDomestic.builder.OrderBuilder builder1 = new com.upay.sdk.transferDomestic.builder.OrderBuilder(payEaseProperties.getMerchantId());
            builder1.setRequestId(id);
            JSONObject response1 = request(ConfigurationUtils.getTransferDomesticSingleQueryUrl(), builder1.bothEncryptBuild());
            String status = response1.getString("status");
            switch (status) {
                case "ERROR":
                    throw new BusinessException(response1.getString("error"));
                case "FAIL":
                case "FAILED":
                    throw new BusinessException(response1.getString("errorMessage"));
                case "CANCEL":
                    throw new BusinessException(response1.getString("已取消"));
                case "INIT":
                case "REMITING":
                    try {
                        Thread.sleep(3000);
                    } catch (Exception ignored) {
                    }
                    continue;
                case "SUCCESS":
                    return com.alibaba.fastjson.JSON.parseObject(response1.toJSONString());
            }
        }
        throw new BusinessException("超时");
    }
}
