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

import cn.newfeifan.mall.module.distri.controller.admin.consumptionerroruser.vo.ConsumptionErrorUserSaveReqVO;
import cn.newfeifan.mall.module.distri.dal.dataobject.applicationforwithdrawal.ApplicationForWithdrawalDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.integralchangelog.IntegralChangeLogDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.integraltopuplog.IntegralTopUpLogDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.integraltransferlog.IntegralTransferLogDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.integral.IntegralDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.usertopupconsumptionpointsorder.UserTopUpConsumptionPointsOrderDO;
import cn.newfeifan.mall.module.distri.enums.IntegralEnum;
import cn.newfeifan.mall.module.distri.service.applicationforwithdrawal.ApplicationForWithdrawalService;
import cn.newfeifan.mall.module.distri.service.integralchangelog.IntegralChangeLogService;
import cn.newfeifan.mall.module.distri.service.consumptionerroruser.ConsumptionErrorUserService;
import cn.newfeifan.mall.module.distri.service.integraltopuplog.IntegralTopUpLogService;
import cn.newfeifan.mall.module.distri.service.integraltransferlog.IntegralTransferLogService;
import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
import cn.newfeifan.mall.module.distri.service.usertopupconsumptionpointsorder.UserTopUpConsumptionPointsOrderService;
import cn.newfeifan.mall.module.member.dal.dataobject.user.MemberUserDO;
import cn.newfeifan.mall.module.member.service.user.MemberUserService;
import cn.newfeifan.mall.module.system.controller.admin.auth.vo.AuthSmsSendReqVO;
import cn.newfeifan.mall.module.system.dal.dataobject.user.AdminUserDO;
import cn.newfeifan.mall.module.system.enums.sms.SmsSceneEnum;
import cn.newfeifan.mall.module.system.service.auth.AdminAuthService;
import cn.newfeifan.mall.module.system.service.user.AdminUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import cn.newfeifan.mall.module.distri.controller.admin.consumptionerrorlog.vo.*;
import cn.newfeifan.mall.module.distri.dal.dataobject.consumptionerrorlog.ConsumptionErrorLogDO;
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.consumptionerrorlog.ConsumptionErrorLogMapper;

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

/**
 * 消费分异动日志记录 Service 实现类
 *
 * @author 非繁人
 */
@Service
@Validated
public class ConsumptionErrorLogServiceImpl implements ConsumptionErrorLogService {

    @Resource
    private ConsumptionErrorLogMapper consumptionErrorLogMapper;
    @Resource
    private ConsumptionErrorUserService consumptionErrorUserService;
    @Resource
    private IntegralTopUpLogService integralTopUpLogService;
    @Resource
    private IntegralChangeLogService integralChangeLogService;
    @Resource
    private IntegralTransferLogService integralTransferLogService;
    @Resource
    private ApplicationForWithdrawalService applicationForWithdrawalService;
    @Resource
    private IntegralService integralService;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private AdminUserService adminUserService;
    @Resource
    private AdminAuthService authService;
    @Resource
    private UserTopUpConsumptionPointsOrderService userTopUpConsumptionPointsOrderService;

    @Override
    public Long createConsumptionErrorLog(ConsumptionErrorLogSaveReqVO createReqVO) {
        // 插入
        ConsumptionErrorLogDO consumptionErrorLog = BeanUtils.toBean(createReqVO, ConsumptionErrorLogDO.class);
        consumptionErrorLogMapper.insert(consumptionErrorLog);
        // 返回
        return consumptionErrorLog.getId();
    }

    @Override
    public void updateConsumptionErrorLog(ConsumptionErrorLogSaveReqVO updateReqVO) {
        // 校验存在
        validateConsumptionErrorLogExists(updateReqVO.getId());
        // 更新
        ConsumptionErrorLogDO updateObj = BeanUtils.toBean(updateReqVO, ConsumptionErrorLogDO.class);
        consumptionErrorLogMapper.updateById(updateObj);
    }

    @Override
    public void deleteConsumptionErrorLog(Long id) {
        // 校验存在
        validateConsumptionErrorLogExists(id);
        // 删除
        consumptionErrorLogMapper.deleteById(id);
    }

    private void validateConsumptionErrorLogExists(Long id) {
        if (consumptionErrorLogMapper.selectById(id) == null) {
            throw exception(CONSUMPTION_ERROR_LOG_NOT_EXISTS);
        }
    }

    @Override
    public ConsumptionErrorLogDO getConsumptionErrorLog(Long id) {
        return consumptionErrorLogMapper.selectById(id);
    }

    @Override
    public PageResult<ConsumptionErrorLogDO> getConsumptionErrorLogPage(ConsumptionErrorLogPageReqVO pageReqVO) {
        return consumptionErrorLogMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calcEveryDayConsumptionPointsErrorLog() {
        // 获取所有用户钱包
        List<IntegralDO> integralDOS = integralService.selectAllUser();

        // 每个用户的充值记录
        Map<Long, Long> topUpLogMap = integralTopUpLogService.selectList().stream()
                .collect(Collectors.groupingBy(
                        IntegralTopUpLogDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralTopUpLogDO::getPracticalIntegralPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));

        // 用户的佣金转消费分记录
//        Map<Long, Long> changLogMap = integralChangeLogService.selectListByStatus(IntegralEnum.COMMISSION_TRANSITION.getType()).stream()
//                .collect(Collectors.groupingBy(
//                        IntegralChangeLogDO::getUserId, // 按userid分组
//                        Collectors.reducing(0L, // 初始化累加器
//                                IntegralChangeLogDO::getIntegralPoints, // 获取每个对象的consumptionPoints
//                                Long::sum // 累加函数
//                        )
//                ));
        // 用户的佣金转让记录(进账)
        Map<Long, Long> recipientLogMap = integralTransferLogService.selectList().stream()
                .collect(Collectors.groupingBy(
                        IntegralTransferLogDO::getRecipientUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralTransferLogDO::getRecipientPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));

        // 用户的提现记录
        Map<Long, Long> withdrawalMap = applicationForWithdrawalService.selectList().stream()
                .collect(Collectors.groupingBy(
                        ApplicationForWithdrawalDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                ApplicationForWithdrawalDO::getWithdrawConsumption, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));

        Map<Long, Long> topUpMap = userTopUpConsumptionPointsOrderService.getUserTopUpConsumptionPointsOrderList().stream()
                .collect(Collectors.groupingBy(
                        UserTopUpConsumptionPointsOrderDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                UserTopUpConsumptionPointsOrderDO::getPracticalConsumptionPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));

        // 初始化所有用户消费分
        Map<Long, Long> userTotalMap = integralDOS.stream()
                .collect(Collectors.toMap(
                        IntegralDO::getUserId,
                        consumptionPoints -> 0L)
                );

        // 遍历得到所有用户的应得消费分
        topUpLogMap.forEach((userId, topUpPoints) -> userTotalMap.merge(userId, topUpPoints, Long::sum));
//        changLogMap.forEach((userId, topUpPoints) -> userTotalMap.merge(userId, topUpPoints, Long::sum));
        recipientLogMap.forEach((userId, topUpPoints) -> userTotalMap.merge(userId, topUpPoints, Long::sum));
        withdrawalMap.forEach((userId, topUpPoints) -> userTotalMap.merge(userId, topUpPoints, Long::sum));
        topUpMap.forEach((userId, topUpPoints) -> userTotalMap.merge(userId, topUpPoints, Long::sum));

        // 用户可用消费分
        Map<Long, Long> currentMap = integralDOS.stream()
                .collect(Collectors.toMap(
                        IntegralDO::getUserId,
                        IntegralDO::getRedCurrentQuota)
                );

        // 用户已用(购买商品的)消费分
        Map<Long, Long> uesdMap = integralChangeLogService.selectListByStatus(IntegralEnum.PAY_ORDER.getType()).stream()
                .collect(Collectors.groupingBy(
                        IntegralChangeLogDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralChangeLogDO::getIntegralPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));
        // 扣除退款退回的
        Map<Long, Long> refundMap = integralChangeLogService.selectListByStatus(IntegralEnum.REFUND_ORDER.getType()).stream()
                .collect(Collectors.groupingBy(
                        IntegralChangeLogDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralChangeLogDO::getIntegralPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));
        uesdMap.forEach((k, v) -> uesdMap.put(k, v * -1));
        refundMap.forEach((k, v) -> uesdMap.put(k,  uesdMap.get(k) - v));

        // 扣除手动取消退回的
        Map<Long, Long> cancelMap = integralChangeLogService.selectListByStatus(IntegralEnum.CANCEL_ORDER_BACK_CONSUMPTION_POINTS.getType()).stream()
                .collect(Collectors.groupingBy(
                        IntegralChangeLogDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralChangeLogDO::getIntegralPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));
        // 扣除支付超时退回的
        Map<Long, Long> orderOverTimeMap = integralChangeLogService.selectListByStatus(IntegralEnum.ORDER_CANCEL_BY_SYSTEM_REFUND_INTEGRAL.getType()).stream()
                .collect(Collectors.groupingBy(
                        IntegralChangeLogDO::getUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralChangeLogDO::getIntegralPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));
        cancelMap.forEach((k, v) -> uesdMap.put(k,  uesdMap.get(k) - v));
        orderOverTimeMap.forEach((k, v) -> uesdMap.put(k,  uesdMap.get(k) - v));

        // 用户转让的消费分(出账)
        Map<Long, Long> transferLogMap = integralTransferLogService.selectList().stream()
                .collect(Collectors.groupingBy(
                        IntegralTransferLogDO::getTransferUserId, // 按userid分组
                        Collectors.reducing(0L, // 初始化累加器
                                IntegralTransferLogDO::getConsumptionPoints, // 获取每个对象的consumptionPoints
                                Long::sum // 累加函数
                        )
                ));
        transferLogMap.forEach((k, v) -> transferLogMap.put(k, v * -1));

        Map<Long, Long> nowConsumptionPointsMap = new HashMap<>();
        currentMap.forEach((userId, topUpPoints) -> nowConsumptionPointsMap.merge(userId, topUpPoints, Long::sum));

        uesdMap.forEach((userId, topUpPoints) -> nowConsumptionPointsMap.merge(userId, topUpPoints, Long::sum));

        transferLogMap.forEach((userId, consumptionPoints) -> nowConsumptionPointsMap.merge(userId, consumptionPoints, Long::sum));

        AtomicLong errorConsumptionPoints = new AtomicLong(0L);     // 错误消费分
        AtomicInteger errorUserCount = new AtomicInteger(0);             // 错误用户数

        Map<Long, Long> userMap = new HashMap<>();
        // 遍历所有的用户,判断哪个用户的消费分有误
        userTotalMap.forEach((userId, topUpPoints) -> {
            // 如果实际消费分大于应得消费分,则视为异动用户
            if (nowConsumptionPointsMap.get(userId) > topUpPoints) {
                long points = nowConsumptionPointsMap.get(userId) - topUpPoints;
                errorConsumptionPoints.addAndGet(points);
                errorUserCount.addAndGet(1);

                // 记录用户
                userMap.put(userId, points);
                System.out.println("用户id:" + userId + "应得消费分:" + topUpPoints + " 实际消费分:" + currentMap.get(userId));
            }
        });

        // 插入异动日志
        if (errorConsumptionPoints.get() > 0 && errorUserCount.get() > 0) {
            ConsumptionErrorLogDO consumptionErrorLogDO = new ConsumptionErrorLogDO()
                    .setErrorConsumptionPointsTotal(errorConsumptionPoints.get())
                    .setErrorUsersCount(errorUserCount.get());
            consumptionErrorLogMapper.insert(consumptionErrorLogDO);

            userMap.forEach((userId, points) -> {
                MemberUserDO user = memberUserService.getUser(userId);
                ConsumptionErrorUserSaveReqVO consumptionErrorLogUserDO = ConsumptionErrorUserSaveReqVO.builder()
                        .consumptionErrorLogId(consumptionErrorLogDO.getId())
                        .userId(userId)
                        .userName(user.getUsername())
                        .consumptionPointsTotal(userTotalMap.get(userId))
                        .nowConsumptionPointsTotal(nowConsumptionPointsMap.get(userId))
                        .currentConsumptionPointsTotal(currentMap.get(userId))
                        .usedConsumptionPointsTotal(uesdMap.get(userId))
                        .transferConsumptionPointsTotal(transferLogMap.get(userId))
                        .errorConsumptionPointsTotal(points)
                        .build();
                consumptionErrorUserService.createConsumptionErrorUser(consumptionErrorLogUserDO);
            });

            // 短信通知系统管理员
            for (AdminUserDO superAdminUser : adminUserService.getSuperAdminUsers()) {
                System.out.println("系统管理员手机号:" + superAdminUser.getMobile());
                authService.sendSmsCode(AuthSmsSendReqVO.builder()
                        .mobile(superAdminUser.getMobile())
                        .scene(SmsSceneEnum.CONSUMPTION_POINTS_ERROR_WARNING.getScene())
                        .build());
            }
        }
    }

}