package cn.newfeifan.mall.module.distri.service.integral;

import cn.newfeifan.mall.framework.common.exception.ErrorCode;
import cn.newfeifan.mall.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.newfeifan.mall.module.distri.constant.DistriConstants;
import cn.newfeifan.mall.module.distri.controller.app.consumptionchangelog.vo.ConsumptionChangeLogSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.app.ptprofitlog.vo.PtProfitLogSaveReqVO;
import cn.newfeifan.mall.module.distri.dal.dataobject.duser.DuserDO;
import cn.newfeifan.mall.module.distri.dal.mysql.duser.DuserMapper;
import cn.newfeifan.mall.module.distri.dal.mysql.ptprofit.PtProfitMapper;
import cn.newfeifan.mall.module.distri.enums.CaclEnum;
import cn.newfeifan.mall.module.distri.enums.ConsumptionEnum;
import cn.newfeifan.mall.module.distri.enums.SocialStatusUpdateEnum;
import cn.newfeifan.mall.module.distri.service.consumptionchangelog.ConsumptionChangeLogService;
import cn.newfeifan.mall.module.distri.service.ptprofitlog.PtProfitLogService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.util.*;

import cn.newfeifan.mall.module.distri.controller.app.integral.vo.*;
import cn.newfeifan.mall.module.distri.dal.dataobject.integral.IntegralDO;
import cn.newfeifan.mall.framework.common.pojo.PageResult;
import cn.newfeifan.mall.framework.common.util.object.BeanUtils;

import cn.newfeifan.mall.module.distri.dal.mysql.integral.IntegralMapper;

import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.newfeifan.mall.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.newfeifan.mall.module.distri.enums.ErrorCodeConstants.*;

/**
 * 推荐用户积分 Service 实现类
 *
 * @author 非繁人
 */
@Service
@Validated
public class IntegralServiceImpl implements IntegralService {

    @Resource
    private DuserMapper duserMapper;

    @Resource
    private IntegralMapper integralMapper;

    @Resource
    private PtProfitMapper ptProfitMapper;

    @Resource
    private PtProfitLogService ptProfitLogService;

    @Resource
    private ConsumptionChangeLogService consumptionChangeLogService;

    @Override
    public Long createIntegral(IntegralSaveReqVO createReqVO) {
        // 插入
        IntegralDO integral = BeanUtils.toBean(createReqVO, IntegralDO.class);
        integralMapper.insert(integral);
        // 返回
        return integral.getId();
    }

    @Override
    public void updateIntegral(IntegralSaveReqVO updateReqVO) {
        // 校验存在
        validateIntegralExists(updateReqVO.getId());
        // 更新
        IntegralDO updateObj = BeanUtils.toBean(updateReqVO, IntegralDO.class);
        integralMapper.updateById(updateObj);
    }

    @Override
    public void deleteIntegral(Long id) {
        // 校验存在
        validateIntegralExists(id);
        // 删除
        integralMapper.deleteById(id);
    }

    private void validateIntegralExists(Long id) {
        if (integralMapper.selectById(id) == null) {
            throw exception(INTEGRAL_NOT_EXISTS);
        }
    }

    @Override
    public IntegralDO getIntegral(Long id) {
        return integralMapper.selectOne(new LambdaQueryWrapperX<IntegralDO>().eq(IntegralDO::getUserId,id));
    }

    @Override
    public PageResult<IntegralDO> getIntegralPage(IntegralPageReqVO pageReqVO) {
        return integralMapper.selectPage(pageReqVO);
    }

    @Override
    public IntegralDO selectByUser(Long descendant) {
        return integralMapper.selectOne(new LambdaQueryWrapper<IntegralDO>().eq(IntegralDO::getUserId, descendant));
    }

    /**
     * add by Ben
     * 修改用户积分
     *
     * @param generateUserId 要修改积分的用户
     * @param userId 要修改积分的用户
     * @param caclEnum 导致积分变化的事件类型枚举
     * @param amount 用户可用积分变化金额，可为负数
     * @param freezeAmount 用户冻结积分变化金额，可为负数
     * @return 返回是否执行成功。执行失败，通常是就是用户积分不足，没积分抵扣
     */
    @Override
    public Boolean updateUserIntegral(Long generateUserId,//导致产生积分变动的用户ID
            Long userId, CaclEnum caclEnum, Long amount, Long freezeAmount,
                                      Long tradeOrderId,
                                      String OrderNum
                                      ) {
        Boolean success = null;

        //修改用户积分钱包的积分
        int effectRowNum = ptProfitMapper.updateUserIntegral(amount,amount,freezeAmount,freezeAmount,userId);

        if (effectRowNum==1) {//如果成功修改积分记录
            success = true;

            //用户修改后的当前可用的钱包余额
            Long afterAmount = null;
            //用户修改后的冻结积分钱包余额
            Long afterFreezeAmount = null;

            //查询用户修改后的积分
            Map<String, Object> map = ptProfitMapper.selectUserIntegral(userId);
            Object currentQuotaObj = map.get("current_quota");
            Object freezeQuotaObj = map.get("freeze_quota");

            afterAmount = (Long)currentQuotaObj;
            afterFreezeAmount = (Long)freezeQuotaObj;

//            if(afterAmount<0||afterFreezeAmount<0){
//                throw new RuntimeException("用户钱包为负数：用户IO为"+userId+"的用户可用钱包为"+afterAmount+",冻结钱包为"+afterFreezeAmount);
//            }

            //记录用户的积分变动日志
            ptProfitLogService.addMessage(generateUserId,//导致产生积分变动的用户ID
                    userId, caclEnum, amount, afterAmount,
                    freezeAmount, afterFreezeAmount,null,tradeOrderId,OrderNum);

        }else if(effectRowNum<1){//如果钱包积分不足，不够抵扣
            success = false;
        } else if (effectRowNum>1) {
//            success = false;
            throw new RuntimeException("用户IO为"+userId+"的用户钱包不只一个！");
        }

        return success;
    }


    /**
     * 修改用户身价
     * @param userId 用户ID
     * @param socialStatusUpdateEnum 身价变动类别
     * @param socialStatusPoint 身价变化值
     * @param ObjectId 导致身价变动的对应业务对象ID，如订单ID、收藏记录ID、新注册用户ID等
     * @return 升级后的身价名称，如果返回null，表明没升级
     */
    public String updateUserSocialStatus(Long userId, SocialStatusUpdateEnum socialStatusUpdateEnum, Integer socialStatusPoint,
                                   Long ObjectId){
        //升级后的身价名称，如果返回null，表明没升级
        String newSocialStatusName = null;
        //用户身价升了几级
        Integer socialStatusUpgradation = 0;

        DuserDO duser = duserMapper.selectOne("user_id", userId);
        Long oldSocialStatus = duser.getSocialStatusId();//升级前身价等级

        return newSocialStatusName;
    }

    @Override
    public void updateIntegralFreezeHighQuota(Long generateUserId, Long userId, CaclEnum caclEnum, Long totalFreezeHighQuota, Long tradeOrderId, String orderNum) {
        IntegralDO integralDO = integralMapper.selectOne(new LambdaQueryWrapper<IntegralDO>().eq(IntegralDO::getUserId, userId));
        integralDO.setFreezeHighQuota(integralDO.getFreezeHighQuota() + totalFreezeHighQuota);
        integralMapper.updateById(integralDO);

        //记录用户的积分变动日志
        PtProfitLogSaveReqVO profitLogSaveReqVO = PtProfitLogSaveReqVO.builder()
                .freezeHighQuota(totalFreezeHighQuota)
                .afterFreezeHighQuota(integralDO.getFreezeHighQuota())
                .userId(userId)
                .generateUserId(generateUserId)
                .profitStatus(caclEnum.getType())
                .orderId(tradeOrderId)
                .orderNo(orderNum)
                .build();
        ptProfitLogService.createPtProfitLog(profitLogSaveReqVO);
    }

    @Override
    public void updateUserIntegral(Long userId, CaclEnum caclEnum, Long payIntegral, Long tradeOrderId, String orderNum) {
        //退还订单支付过期积分
        IntegralDO integralDO = integralMapper.selectOne(new LambdaQueryWrapper<IntegralDO>().eq(IntegralDO::getUserId, userId));
        integralDO.setCurrentQuota(integralDO.getCurrentQuota() + payIntegral);

        integralMapper.updateById(integralDO);

        ptProfitLogService.addMessage(userId,userId,caclEnum,payIntegral,integralDO.getCurrentQuota(),0L,0L,null,tradeOrderId,orderNum);
    }

    @Override
    public void updateUserConsumptionPoints(Long userId, ConsumptionEnum consumptionEnum, Long consumptionPoints, Long orderId, String orderNum) {
        IntegralDO integralDO = integralMapper.selectOne(IntegralDO::getUserId, userId);

        // 修改用户消费积分
        integralDO.setConsumptionPoints(integralDO.getConsumptionPoints() + consumptionPoints);
        integralMapper.updateById(integralDO);

        consumptionChangeLogService.createConsumptionChangeLog(
                ConsumptionChangeLogSaveReqVO.builder()
                        .userId(userId)
                        .generateUserId(userId)
                        .consumptionStatus(consumptionEnum.getType())
                        .consumptionPoints(consumptionPoints)
                        .afterConsumptionPoints(integralDO.getConsumptionPoints())
                        .orderId(orderId)
                        .orderNo(orderNum)
                        .build());
    }

    @Override
    public Boolean quotaTransition(Long quota) {
        Long userId = getLoginUserId();

        // 输入的佣金要放大才能与数据库中匹配
        quota = quota * DistriConstants.INTEGRAL_PAGE2DB;
        IntegralDO integralDO = integralMapper.selectOne(IntegralDO::getUserId,userId);

        if(integralDO.getCurrentQuota() < quota){
            ErrorCode ERROR = new ErrorCode(1_002_030_003, "可用积分不足");
            throw exception(ERROR);
        }

        integralDO.setCurrentQuota(integralDO.getCurrentQuota() - quota);
        integralDO.setConsumptionPoints(integralDO.getConsumptionPoints() + quota);
        integralMapper.updateById(integralDO);

        consumptionChangeLogService.createConsumptionChangeLog(
                ConsumptionChangeLogSaveReqVO.builder()
                        .userId(userId)
                        .consumptionStatus(ConsumptionEnum.COMMISSION_TRANSITION.getType())
                        .consumptionPoints(quota)
                        .afterConsumptionPoints(integralDO.getConsumptionPoints())
                        .build());

        ptProfitLogService.createPtProfitLog(
                PtProfitLogSaveReqVO.builder()
                        .amount(-quota)
                        .afterAmount(integralDO.getCurrentQuota())
                        .userId(userId)
                        .generateUserId(userId)
                        .profitStatus(CaclEnum.COMMISSION_TRANSITION.getType())
                        .build());

        return true;
    }

    @Override
    public Boolean updateUserConsumptionPoints(Long generateUserId, Long userId, Long amount, Long tradeOrderId, String orderNum) {
        IntegralDO integralDO = integralMapper.selectOne(IntegralDO::getUserId, userId);
        // 消费分不足
        if(integralDO.getConsumptionPoints() < amount){
            return false;
        }

        // 修改用户消费积分
        integralDO.setConsumptionPoints(integralDO.getConsumptionPoints() - amount);
        integralMapper.updateById(integralDO);

        consumptionChangeLogService.createConsumptionChangeLog(
                ConsumptionChangeLogSaveReqVO.builder()
                        .userId(userId)
                        .generateUserId(generateUserId)
                        .consumptionStatus(ConsumptionEnum.PAY_ORDER.getType())
                        .consumptionPoints(-amount)
                        .afterConsumptionPoints(integralDO.getConsumptionPoints())
                        .orderId(tradeOrderId)
                        .orderNo(orderNum)
                        .build());

        return true;
    }
}