package com.cyksj.service.advertisement.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
import com.cyksj.common.util.Codec;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.config.BaseWeChatConfig;
import com.cyksj.config.WeChatFactory;
import com.cyksj.mapper.advertisement.AdvertisementOrderDonMapper;
import com.cyksj.mapper.advertisement.AdvertisementPopularizeMapper;
import com.cyksj.mapper.advertisement.AdvertisementProjectMapper;
import com.cyksj.model.dto.H5JsPayParams;
import com.cyksj.model.entity.*;
import com.cyksj.model.request.OrderAttach;
import com.cyksj.model.views.PaySuccessViews;
import com.cyksj.service.advertisement.AdvertisementOrderDonService;
import com.cyksj.service.advertisement.AdvertisementOrderPostService;
import com.cyksj.service.advertisement.AdvertisementPopularizeService;
import com.cyksj.service.advertisement.AdvertisementProjectService;
import com.cyksj.service.wechat.WeChatService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author chan
 * @date 2023/5/16 11:07 AM
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AdvertisementOrderDonServiceImpl extends ServiceImpl<AdvertisementOrderDonMapper, AdvertisementOrderDon> implements AdvertisementOrderDonService {
    
    @Value("${advertisement.shopKey}")
    private String shopKey;

    @Value("${advertisement.shopId}")
    private String shopId;

    @Value("${advertisement.notify}")
    private String notify;
    
    private final static GlobalThreadPoolTaskExecutor THREAD_POOL_TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();

    private final AdvertisementPopularizeMapper popularizeMapper;

    private final WeChatService weChatService;
    
    private final AdvertisementOrderPostService orderPostService;

    private final AdvertisementProjectMapper projectMapper;

    @Override
    public H5JsPayParams pay(Long popularizeId, BigDecimal money, String appId, AdvertisementUser user, AdvertisementOrderDon.Source source, String callback, String callbackType) throws Exception {

        AdvertisementPopularize popularize = popularizeMapper.selectById(popularizeId);
        if (popularize == null) {
            log.error("链接不存在:{}", popularizeId);
            throw BusinessRuntimeException.getInstance("链接不存在");
        }
        AdvertisementProject project = projectMapper.selectById(popularize.getProjectId());

        /* 生成订单 */
        String payOpenId = user.getOpenId();

        String donNo = Codec.DoDigest.custom()
                .setAlgorithm(Codec.DoDigest.Algorithm.MD5)
                .setStringData(System.currentTimeMillis() + payOpenId)
                .toHexString();

        AdvertisementOrderDon orderDon = new AdvertisementOrderDon().setPopularizeId(popularizeId).setMoney(money).setOrderNo(donNo).setUserId(user.getId())
                .setOpenId(payOpenId).setSource(source);
        //数据回传
        if (StringUtils.isNotBlank(callback)) {
            orderPostService.savePostData(orderDon.getId(), callback, callbackType);
        }


        OrderAttach attach = new OrderAttach();

        attach.setO(orderDon.getId());

        String toWechatAttach = Codec.DoBase64.custom()
                .setData(Jsons.toJson(attach)).encodeAsText();

        H5JsPayParams params = weChatService.getJsPayParams(
                appId,
                shopId,
                orderDon.getOpenId(),
                orderDon.getMoney().intValue(),
                orderDon.getOrderNo(),
                notify + "applets/advertisement/order/prepay/notify",
                toWechatAttach,
                project.getName(),
                "JSAPI",
                orderDon.getCreatedTime()
        );
        params.setOrderNo(orderDon.getOrderNo());
        log.info("微信支付成功[end]");
        return params;
    }

    @Override
    public PaySuccessViews isPay(Long orderId) {
        
        AdvertisementOrderDon orderDon = getById(orderId);

        Assert.notNull(orderDon, "订单不存在！");

        AdvertisementPopularize popularize = popularizeMapper.selectById(orderDon.getPopularizeId());
        Assert.notNull(popularize, "推广链接不存在");

        AdvertisementProject project = projectMapper.selectById(popularize.getProjectId());
        Assert.notNull(project, "推广项目不存在");
        
        PaySuccessViews paySuccessViews = new PaySuccessViews();
        paySuccessViews.setTransactionId(orderDon.getTransactionId());
        paySuccessViews.setIsPay(AdvertisementOrderDon.Status.hasPayment.equals(orderDon.getStatus()));
        paySuccessViews.setMoney(orderDon.getMoney());
        paySuccessViews.setPayTime(orderDon.getPayTime());
        paySuccessViews.setSource(orderDon.getSource());

        return paySuccessViews;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void prepayNotify(Map<String, String> map) throws Exception {
        /* 此处处理订单等业务逻辑 */
        // 获取订单 和 微信订单号 和 支付的金额

        String attach = Codec.DoBase64.custom().setData(map.get("attach")).decodeAsText();

        OrderAttach orderV2Attach = Jsons.parseObject(attach, OrderAttach.class);

        final Long oid = orderV2Attach.getO();
        AdvertisementOrderDon order = this.getById(oid);

        if (null == order) {
            throw BusinessRuntimeException.getInstance("订单不存在.");
        }
        
        // 校验签名
        boolean f = weChatService.checkSignWithMd5(map, shopKey);
        if (!f) {
            log.error("回调数据：{}", map);
            throw BusinessRuntimeException.getInstance("支付成功回调, 校验签名失败.");
        }

        String transactionId = map.get("transaction_id");

        if (order.getStatus().equals(AdvertisementOrderDon.Status.hasPayment)) {
            log.info("已回调，重复修改状态 id:{}", order.getId());
            return;
        }

        // 更改订单状态
        order.setTransactionId(transactionId);
        order.setStatus(AdvertisementOrderDon.Status.hasPayment);
        order.setPayTime(DateUtil.parse(map.get("time_end"), "yyyyMMddHHmmss"));
        if (StringUtils.isBlank(order.getOpenId())) {
            order.setOpenId(map.get("openid"));
        }

        this.updateById(order);
        
        THREAD_POOL_TASK_EXECUTOR.execute(()->{
            try {
                orderPostService.postData((order.getId()));
            } catch (Exception e) {
                log.error("回传错误 {}",StringUtil.getErrorText(e));
            }
        });
    }
}
