package com.zanxiang.game.module.sdk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.sd4324530.jtuple.Tuple2;
import com.zanxiang.game.module.base.pojo.enums.CpStatusEnum;
import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
import com.zanxiang.game.module.base.util.DateUtils;
import com.zanxiang.game.module.mybatis.entity.*;
import com.zanxiang.game.module.sdk.enums.OrderStateEnum;
import com.zanxiang.game.module.sdk.pojo.dto.GameAppletDTO;
import com.zanxiang.game.module.sdk.pojo.dto.PayMerchantDTO;
import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
import com.zanxiang.game.module.sdk.pojo.result.PushCpResult;
import com.zanxiang.game.module.sdk.service.*;
import com.zanxiang.game.module.sdk.service.api.CpApiService;
import com.zanxiang.game.module.sdk.util.SignUtil;
import com.zanxiang.module.util.JsonUtil;
import com.zanxiang.module.util.URIUtil;
import com.zanxiang.module.util.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 订单履约逻辑
 *
 * @author xufeng
 * @date 2022/6/27 11:17
 */
@Slf4j
@Service
public class PerformOrderServiceImpl implements IPerformOrderService {

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IPayCallCpService payCallCpService;

    @Autowired
    private IGameUserRoleService gameUserRoleService;

    @Autowired
    private IGameUserService gameUserService;

    @Autowired
    private IUserService userService;

    @Autowired
    private IPayMerchantService payMerchantService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private IGameExtService gameExtService;

    @Autowired
    private IMiPaySumService miPaySumService;

    @Autowired
    private IGameAppletService gameAppletService;

    @Autowired
    private IGameService gameService;

    @Autowired
    private CpApiService cpApiService;

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public Boolean pushCp(PlatformOrderDTO orderInfo) {
        log.info("充值回调CP, orderInfo : {} ----------start---------", JsonUtil.toString(orderInfo));
        if (Objects.equals(orderInfo.getCpStatus(), CpStatusEnum.SUCCESS.getStatus())) {
            return true;
        }
        GameExt gameExt = gameExtService.getByGameId(orderInfo.getGameId());
        String cpPaybackUrl = gameExt.getPayCallBackUrl();
        if (Strings.isBlank(cpPaybackUrl)) {
            log.error("充值回调CP失败, 游戏拓展信息中的回调地址为空!");
            return false;
        }
        //游戏信息
        Game game = gameService.getById(orderInfo.getGameId());
        //用户信息
        User user = userService.getById(orderInfo.getUserId());
        //回调参数
        Map<String, String> param = this.payParam(game, user, orderInfo);
        //拼接请求URI
        String url = URIUtil.fillUrlParams(cpPaybackUrl, param, false);
        //调CP充值回调
        String resultStr;
        try {
            resultStr = restTemplate.getForObject(url, String.class);
        } catch (Exception e) {
            log.error("回调CP充值接口异常, orderInfo : {}, e : {}", JsonUtil.toString(orderInfo), e.getMessage());
            return false;
        }
        PushCpResult pushCpResult = JsonUtil.toObj(resultStr, PushCpResult.class);
        boolean pushSuccess = Objects.equals(pushCpResult.getResult(), PushCpResult.SUCCESS_CODE)
                || Objects.equals(pushCpResult.getCode(), PushCpResult.SUCCESS_CODE);

        //CP返回充值失败, 进入重试
        if (!pushSuccess) {
            try {
                Tuple2<Boolean, String> tuple2 = cpApiService.callCpRecharge(url);
                //重试成功, 修改结果, 重试失败, 则打印日志, 将日志给CP方
                if (tuple2.first) {
                    pushSuccess = true;
                    resultStr = tuple2.second;
                }
            } catch (Exception e) {
                log.error("CP充值回调重试失败, url : {}, e : {}", url, e.getMessage());
            }
        }

        log.error("通知CP支付回调结果, url : {}, resultStr : {}, pushSuccess : {}", url, resultStr, pushSuccess);
        //更新订单信息
        orderService.update(new LambdaUpdateWrapper<Order>()
                .set(Order::getCpNotifyCnt, orderInfo.getCpNotifyCnt() + 1)
                .set(Order::getLastCpNotifyTime, LocalDateTime.now())
                .set(Order::getCpStatus, pushSuccess ? CpStatusEnum.SUCCESS.getStatus() : CpStatusEnum.FAIL.getStatus())
                .eq(Order::getOrderId, orderInfo.getOrderId())
        );
        //更新回调记录
        payCallCpService.save(PayCallCpLog.builder()
                .orderId(orderInfo.getOrderId())
                .cpOrderId(orderInfo.getCpOrderId())
                .status(orderInfo.getStatus())
                .cpStatus(pushSuccess ? CpStatusEnum.SUCCESS.getStatus() : CpStatusEnum.FAIL.getStatus())
                .cpPaybackUrl(cpPaybackUrl)
                .params(JsonUtil.toString(param))
                .ext(orderInfo.getExt())
                .notifyCnt(orderInfo.getCpNotifyCnt() + 1)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build());
        log.info("充值回调CP----------end---------");
        return pushSuccess;
    }

    private Map<String, String> payParam(Game game, User user, PlatformOrderDTO orderInfo) {
        Map<String, String> map = new HashMap<>(10);
        //用户存在关联用户id, 且不是微信小程序, 则判定为导量用户, 提交CP原始用户id
        if (user.getRelationUserId() != null
                && !Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
            map.put("userId", String.valueOf(user.getRelationUserId()));
        } else {
            map.put("userId", String.valueOf(orderInfo.getUserId()));
        }
        //判断是否设置充值打折
        if (game.getRechargeRebate() != null) {
            BigDecimal money = orderInfo.getAmount().multiply(game.getRechargeRebate())
                    .setScale(2, RoundingMode.HALF_UP);
            map.put("money", money.stripTrailingZeros().toPlainString());
        } else {
            map.put("money", orderInfo.getAmount().stripTrailingZeros().toPlainString());
        }
        map.put("time", String.valueOf(System.currentTimeMillis()));
        map.put("serverId", orderInfo.getServerId());
        map.put("roleId", orderInfo.getRoleId());
        map.put("roleName", orderInfo.getRoleName());
        map.put("orderId", orderInfo.getCpOrderId());
        map.put("ext", URIUtil.encodeURIComponent(orderInfo.getExt()));
        map.put("sign", this.paySign(orderInfo.getGameId(), map));
        map.put("orderId2", orderInfo.getOrderId());
        return map;
    }

    private String paySign(Long gameId, Map<String, String> map) {
        //查询游戏
        GameExt gameExt = gameExtService.getByGameId(gameId);
        if (Strings.isBlank(gameExt.getPayKey())) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append("payKey=").append(gameExt.getPayKey());
        sb.append("&userId=").append(map.get("userId"));
        sb.append("&money=").append(map.get("money"));
        sb.append("&time=").append(map.get("time"));
        sb.append("&serverId=").append(map.get("serverId"));
        sb.append("&orderId=").append(map.get("orderId"));
        //加密
        String sign;
        try {
            sign = SignUtil.MD5(sb.toString());
        } catch (Exception e) {
            throw new BaseException("支付加密异常");
        }
        return sign;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public void checkIsFirstRecharge(PlatformOrderDTO platformOrderDTO) {
        try {
            if (orderService.count(new LambdaQueryWrapper<Order>()
                    .eq(Order::getUserId, platformOrderDTO.getUserId())
                    .eq(Order::getStatus, OrderStateEnum.SUCCESS_PAY.getCode())
                    .ne(Order::getOrderId, platformOrderDTO.getOrderId())
            ) <= 0) {
                orderService.update(new LambdaUpdateWrapper<Order>()
                        .set(Order::getIsFirstRecharge, 1)
                        .eq(Order::getOrderId, platformOrderDTO.getOrderId()));
            }
        } catch (Exception e) {
            log.error("验证订单是否为首充异常, orderId : {}, e : {}", platformOrderDTO.getOrderId(), e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public void userRechargeTotal(PlatformOrderDTO platformOrderDTO) {
        try {
            //订单取消情况 做减法
            float amount = platformOrderDTO.getAmount().floatValue();
            int num = 1;
            //游戏用户角色统计更新
            if (Strings.isNotBlank(platformOrderDTO.getRoleId()) && !"0".equals(platformOrderDTO.getRoleId())) {
                gameUserRoleService.update(new LambdaUpdateWrapper<GameUserRole>()
                        .set(GameUserRole::getLastRechargeTime, platformOrderDTO.getPayTime())
                        .setSql("recharge_count=recharge_count+" + num)
                        .setSql("recharge_money=recharge_money+" + amount)
                        .eq(GameUserRole::getGameId, platformOrderDTO.getGameId())
                        .eq(GameUserRole::getRoleId, platformOrderDTO.getRoleId())
                        .eq(GameUserRole::getUserId, platformOrderDTO.getUserId()));
            }
            //游戏用户统计更新
            if (platformOrderDTO.getMgUserId() != null) {
                gameUserService.update(new LambdaUpdateWrapper<GameUser>()
                        .set(GameUser::getLastRechargeTime, platformOrderDTO.getPayTime())
                        .setSql("recharge_count=recharge_count+" + num)
                        .setSql("recharge_money=recharge_money+" + amount)
                        .eq(GameUser::getId, platformOrderDTO.getMgUserId()));
            }
            //用户统计更新
            if (platformOrderDTO.getUserId() != null) {
                userService.update(new LambdaUpdateWrapper<User>()
                        .set(User::getLastRechargeTime, platformOrderDTO.getPayTime())
                        .setSql("recharge_count=recharge_count+" + num)
                        .setSql("recharge_money=recharge_money+" + amount)
                        .eq(User::getId, platformOrderDTO.getUserId()));
            }
        } catch (Exception e) {
            log.error("用户充值统计逻辑操作异常, orderId : {}, e : {}", platformOrderDTO.getOrderId(), e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public void payMerchantTotal(PlatformOrderDTO platformOrderDTO) {
        try {
            //根据订单中的商户id获取商户信息
            PayMerchantDTO payMerchantInfo = payMerchantService.getByMerchantNo(platformOrderDTO.getMerchantNo());
            //商户额度数据统计
            BigDecimal amount = platformOrderDTO.getAmount();
            LocalDateTime updateTime = payMerchantInfo.getDayUpdateDate();
            LocalDateTime dateTime = LocalDateTime.now();
            String totalPayAmount, mouthPayAmount, weakerPayAmount, dayPayAmount;
            //累计
            totalPayAmount = " total_pay_amount = total_pay_amount + " + amount;
            mouthPayAmount = " mouth_pay_amount = mouth_pay_amount + " + amount;
            weakerPayAmount = " weaker_pay_amount = weaker_pay_amount + " + amount;
            dayPayAmount = " day_pay_amount = day_pay_amount + " + amount;
            //统计过期限, 重新赋值
            if (updateTime != null) {
                if (!DateUtils.isThisMonth(payMerchantInfo.getMouthUpdateDate())) {
                    mouthPayAmount = " mouth_pay_amount = " + amount;
                }
                if (!DateUtils.isThisWeek(payMerchantInfo.getWeakerUpdateDate())) {
                    weakerPayAmount = " weaker_pay_amount = " + amount;
                }
                if (!DateUtils.isToday(payMerchantInfo.getDayUpdateDate())) {
                    dayPayAmount = " day_pay_amount = " + amount;
                }
            }
            //更新商户表
            payMerchantService.update(new LambdaUpdateWrapper<PayMerchant>()
                    .set(PayMerchant::getMouthUpdateDate, dateTime)
                    .set(PayMerchant::getWeakerUpdateDate, dateTime)
                    .set(PayMerchant::getDayUpdateDate, dateTime)
                    .setSql(totalPayAmount)
                    .setSql(mouthPayAmount)
                    .setSql(weakerPayAmount)
                    .setSql(dayPayAmount)
                    .eq(PayMerchant::getMerchantNo, platformOrderDTO.getMerchantNo())
            );
        } catch (Exception e) {
            log.error("更新商户号统计异常, orderId : {}, e : {}", platformOrderDTO.getOrderId(), e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public void miPayTotal(PlatformOrderDTO platformOrderDTO) {
        try {
            //获取米大师统计信息
            GameAppletDTO gameAppletDTO = gameAppletService.getByGameId(platformOrderDTO.getGameId());
            GameAppletDTO.MiPayConfigBean miPayConfigBean = gameAppletDTO.getMiPayConfigBean();
            if (miPayConfigBean == null) {
                log.error("米大师支付统计失败, 米大师配置信息不存在");
                return;
            }
            MiPaySum miPaySum = miPaySumService.getOne(new LambdaQueryWrapper<MiPaySum>()
                    .eq(MiPaySum::getGameId, gameAppletDTO.getGameId())
                    .eq(MiPaySum::getAppId, gameAppletDTO.getAppId())
                    .eq(MiPaySum::getMiPayAppId, miPayConfigBean.getAppId()));
            //商户额度数据统计
            BigDecimal amount = platformOrderDTO.getAmount();
            LocalDateTime updateTime = miPaySum.getDayUpdateDate();
            LocalDateTime dateTime = LocalDateTime.now();
            String totalPayAmount, mouthPayAmount, weakerPayAmount, dayPayAmount;
            //累计
            totalPayAmount = " total_pay_amount = total_pay_amount + " + amount;
            dayPayAmount = " day_pay_amount = day_pay_amount + " + amount;
            weakerPayAmount = " weaker_pay_amount = weaker_pay_amount + " + amount;
            mouthPayAmount = " mouth_pay_amount = mouth_pay_amount + " + amount;
            //统计过期限, 重新赋值
            if (updateTime != null) {
                if (!DateUtils.isToday(miPaySum.getDayUpdateDate())) {
                    dayPayAmount = " day_pay_amount = " + amount;
                }
                if (!DateUtils.isThisWeek(miPaySum.getWeakerUpdateDate())) {
                    weakerPayAmount = " weaker_pay_amount = " + amount;
                }
                if (!DateUtils.isThisMonth(miPaySum.getMouthUpdateDate())) {
                    mouthPayAmount = " mouth_pay_amount = " + amount;
                }
            }
            //更新商户表
            miPaySumService.update(new LambdaUpdateWrapper<MiPaySum>()
                    .set(MiPaySum::getMouthUpdateDate, dateTime)
                    .set(MiPaySum::getWeakerUpdateDate, dateTime)
                    .set(MiPaySum::getDayUpdateDate, dateTime)
                    .setSql(totalPayAmount)
                    .setSql(mouthPayAmount)
                    .setSql(weakerPayAmount)
                    .setSql(dayPayAmount)
                    .eq(MiPaySum::getGameId, gameAppletDTO.getGameId())
                    .eq(MiPaySum::getAppId, gameAppletDTO.getAppId())
                    .eq(MiPaySum::getMiPayAppId, miPayConfigBean.getAppId()));
        } catch (Exception e) {
            log.error("更新米大师统计异常, orderId : {}, e : {}", platformOrderDTO.getOrderId(), e.getMessage());
        }
    }
}
