package com.kxs.system.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kxs.common.core.constant.SecurityConstants;
import com.kxs.common.core.constant.enums.ErrorTypeEnum;
import com.kxs.common.core.constant.enums.KindTypeEnum;
import com.kxs.common.core.exception.GlobalCustomerException;
import com.kxs.common.core.util.R;
import com.kxs.common.core.util.RetOps;
import com.kxs.common.security.util.SecurityUtils;
import com.kxs.system.api.dto.kxsapp.camp.CampOpenNumStatDTO;
import com.kxs.system.api.dto.kxsapp.camp.PrizePoolDTO;
import com.kxs.system.api.model.KxsCamp;
import com.kxs.system.api.model.KxsCampBonusLog;
import com.kxs.system.api.model.KxsCampGroup;
import com.kxs.system.api.model.KxsCampUser;
import com.kxs.system.api.vo.admin.camp.CampUserExcelVO;
import com.kxs.system.api.vo.admin.camp.SysCampPageVO;
import com.kxs.system.api.vo.kxsapp.camp.CampGetByIdVO;
import com.kxs.system.api.vo.kxsapp.camp.CampPageVO;
import com.kxs.system.biz.constant.enums.CampSourceTypeEnum;
import com.kxs.system.biz.constant.enums.CampStatusEnum;
import com.kxs.system.biz.constant.enums.SysErrorTypeEnum;
import com.kxs.system.biz.mapper.KxsCampMapper;
import com.kxs.system.biz.service.KxsCampBonusLogService;
import com.kxs.system.biz.service.KxsCampGroupService;
import com.kxs.system.biz.service.KxsCampService;
import com.kxs.system.biz.service.KxsCampUserService;
import com.kxs.user.api.feign.RemoteKxsUserService;
import com.kxs.user.api.model.KxsUser;
import com.pig4cloud.plugin.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 训练营(KxsCamp)表服务实现类
 *
 * @author 系统
 * @since 2024-04-08 15:44:02
 */
@Service("kxsCampService")
@RequiredArgsConstructor
@Slf4j
public class KxsCampServiceImpl extends ServiceImpl<KxsCampMapper, KxsCamp> implements KxsCampService {

    private final RemoteKxsUserService remoteKxsUserService;

    private final KxsCampUserService kxsCampUserService;

    private final KxsCampBonusLogService campBonusLogService;

    private final KxsCampGroupService kxsCampGroupService;


    @Override
    public R addData(KxsCamp param) {

        R<KxsUser> kxsUserR = remoteKxsUserService.loadUserById(param.getCreateUserId(), SecurityConstants.FROM_IN);
        KxsUser user = RetOps.of(kxsUserR)
                .getData()
                .orElseThrow(() -> new GlobalCustomerException(ErrorTypeEnum.USER_NOT_FOUND.getDescription()));

        param.setCreateUsername(user.getUsername());
        param.setCreateCode(user.getUserCode());

        return R.ok(baseMapper.insert(param));
    }

    @Override
    public R updateData(KxsCamp param) {

        if (Objects.isNull(param.getId())) {
            return R.failed(SysErrorTypeEnum.PARAM_ERROR.getDescription());
        }
        KxsCamp kxsCamp = baseMapper.selectById(param.getId());
        Optional.ofNullable(kxsCamp).orElseThrow(() -> new GlobalCustomerException(SysErrorTypeEnum.NOT_DATA.getDescription()));

        R<KxsUser> kxsUserR = remoteKxsUserService.loadUserById(param.getCreateUserId(), SecurityConstants.FROM_IN);
        KxsUser user = RetOps.of(kxsUserR)
                .getData()
                .orElseThrow(() -> new GlobalCustomerException(ErrorTypeEnum.USER_NOT_FOUND.getDescription()));

        param.setCreateUsername(user.getUsername());
        param.setCreateCode(user.getUserCode());

        return R.ok(baseMapper.updateById(param));
    }

    @Override
    public R importUserData(List<CampUserExcelVO> campUserExcelVOList, BindingResult bindingResult) {

        if (campUserExcelVOList.isEmpty()) {
            return R.failed(SysErrorTypeEnum.IMPORT_EMPTY.getDescription());
        }
        //判断是否有重复创客
        boolean hasDuplicates = campUserExcelVOList.stream()
                .map(CampUserExcelVO::getUserCode)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet()
                .stream()
                .anyMatch(e -> e.getValue() > 1);
        if(hasDuplicates){
            return R.failed("导入数据中存在重复的创客编码，请检查后重新导入");
        }

        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        // 执行数据插入操作
        for (CampUserExcelVO excel : campUserExcelVOList) {
            Set<String> errorMsg = new HashSet<>();
            //查询用户
            R<KxsUser> kxsUserR = remoteKxsUserService.loadUserByCode(excel.getUserCode(), SecurityConstants.FROM_IN);
            KxsUser user = RetOps.of(kxsUserR)
                    .getData()
                    .orElse(null);
            KxsCamp kxsCamp = baseMapper.selectById(excel.getCampId());
            Optional.ofNullable(kxsCamp).orElseThrow(() -> new GlobalCustomerException(excel.getCampId() + "训练营编号不存在，请重新上传！"));
            if (Objects.equals(kxsCamp.getStatus(), CampStatusEnum.STATUS_END.getType())) {
                throw new GlobalCustomerException(excel.getCampId() + "训练营已结束，不允许导入！");
            }
            if (Objects.equals(kxsCamp.getStatus(), CampStatusEnum.STATUS_CLOSE.getType())) {
                throw new GlobalCustomerException(excel.getCampId() + "请先开启训练营后再导入！");
            }
            //查询分组
            KxsCampGroup kxsCampGroup = kxsCampGroupService.getOne(Wrappers.<KxsCampGroup>lambdaQuery()
                    .eq(KxsCampGroup::getCampId, excel.getCampId())
                    .eq(KxsCampGroup::getGroupId, excel.getGroupId()));
            Optional.ofNullable(kxsCampGroup).orElseThrow(() -> new GlobalCustomerException(excel.getGroupId() + "小组编号不存在，请重新上传！"));

            if (Objects.isNull(user)) {
                errorMsg.add(excel.getUserCode() + "创客未找到！");
            } else {
                if (user.getRealStatus() != 1) {
                    errorMsg.add(excel.getUserCode() + "创客未实名！");
                }
                //判断创客是否已在其他训练营
                KxsCamp camp = baseMapper.selectUserCamp(user.getId());
                if (camp != null) {
                    errorMsg.add(excel.getUserCode() + "已在训练营！");
                }
            }


            // 数据合法情况
            if (CollUtil.isEmpty(errorMsg)) {
                KxsCampUser campUser = new KxsCampUser();
                campUser.setCampId(excel.getCampId());
                campUser.setGroupId(excel.getGroupId());
                campUser.setUserId(user.getId());
                campUser.setUsername(user.getUsername());
                campUser.setUserCode(user.getUserCode());
                kxsCampUserService.save(campUser);
            } else {
                // 数据不合法
                errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
            }
        }

        this.refreshCampUsers();
        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(JSON.toJSONString(errorMessageList));
        }
        return R.ok();
    }


    /**
     * 刷新营地用户
     */
    private void refreshCampUsers(){
        //刷新参与人数
        List<KxsCamp> kxsCamps = baseMapper.selectList(Wrappers.<KxsCamp>lambdaQuery().lt(KxsCamp::getStatus, CampStatusEnum.STATUS_END.getType()));
        for (KxsCamp kxsCamp : kxsCamps) {
            long count = kxsCampUserService.count(Wrappers.<KxsCampUser>lambdaQuery().eq(KxsCampUser::getCampId, kxsCamp.getId()));
            baseMapper.update(null, Wrappers.<KxsCamp>lambdaUpdate().set(KxsCamp::getUserNum, count).eq(KxsCamp::getId, kxsCamp.getId()));
        }
    }

    @Override
    public IPage<CampPageVO> getByPage(Page<CampPageVO> page) {

        Long userId = SecurityUtils.getUser().getId();
        return baseMapper.getByPage(page, userId);
    }

    @Override
    public R getByData(Integer id) {
        KxsCamp kxsCamp = baseMapper.selectById(id);
        Optional.ofNullable(kxsCamp).orElseThrow(() -> new GlobalCustomerException(SysErrorTypeEnum.NOT_DATA.getDescription()));
        CampGetByIdVO campGetByIdVO = new CampGetByIdVO();
        BeanUtil.copyProperties(kxsCamp, campGetByIdVO);
        return R.ok(campGetByIdVO);
    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void prizePoolIsRefreshed(PrizePoolDTO param) {

        log.info("开始处理兑换券下单订单号:{}统计到训练营奖金池", param.getOrderNo());

        //查询此用户是否参加进行中的训练营
        KxsCamp kxsCamp = baseMapper.selectUserCamp(param.getUserId());
        R<KxsUser> kxsUserR = remoteKxsUserService.loadUserById(param.getUserId(), SecurityConstants.FROM_IN);
        KxsUser user = RetOps.of(kxsUserR)
                .getData()
                .orElseThrow(() -> new GlobalCustomerException(ErrorTypeEnum.USER_NOT_FOUND.getDescription()));
        //参与训练营的创客ID
        Integer campUserId = param.getUserId();
        if (kxsCamp == null) {
            kxsCamp = baseMapper.selectUserCamp(user.getPid());
            campUserId = user.getPid();
        }
        if (kxsCamp == null) {
            return;
        }
        //大小盟主订单、盟主券续费的商品
        if (param.getProductId() == 27 || param.getProductId() == 28 || param.getProductId() == 39 || param.getProductId() == 40) {
            //给训练营加金额
            kxsCamp.setOrderNum(kxsCamp.getOrderNum() + 1);
            baseMapper.updateById(kxsCamp);

            //给创客团队加金额
            KxsCampUser kxsCampUser = kxsCampUserService.getOne(Wrappers.<KxsCampUser>lambdaQuery()
                    .eq(KxsCampUser::getCampId, kxsCamp.getId())
                    .eq(KxsCampUser::getUserId, campUserId));
            kxsCampUser.setTeamLeaderNum(kxsCampUser.getTeamLeaderNum() + 1);
            kxsCampUserService.updateById(kxsCampUser);
            log.info("用户/上级{}参加训练营{}，订单ID{}, 订单数{}", param.getUserId(), kxsCamp.getId(), param.getId(), kxsCamp.getOrderNum());

            KxsCampBonusLog kxsCampBonusLog = new KxsCampBonusLog();
            kxsCampBonusLog.setCampId(kxsCamp.getId());
            kxsCampBonusLog.setCampUserId(kxsCampUser.getUserId());
            kxsCampBonusLog.setGroupId(kxsCampUser.getGroupId());
            kxsCampBonusLog.setSourceId(user.getId());
            kxsCampBonusLog.setUsername(user.getUsername());
            kxsCampBonusLog.setUserCode(user.getUserCode());
            kxsCampBonusLog.setRemark("商城下单");
            kxsCampBonusLog.setSourceNum(1);
            kxsCampBonusLog.setKind(KindTypeEnum.ADD.getType());
            //27|39 大盟主下单和续费
            kxsCampBonusLog.setSourceType(param.getProductId() == 27 || param.getProductId() == 39 ? CampSourceTypeEnum.S_DMZ.getType() : CampSourceTypeEnum.S_XMZ.getType());


            campBonusLogService.save(kxsCampBonusLog);
        }else{

            //下单组数
            BigDecimal number = new BigDecimal(param.getBuyCount());
            //订单奖金
            BigDecimal amount = NumberUtil.mul(number, 60);
            //给训练营加金额
            kxsCamp.setBonusPool(NumberUtil.add(kxsCamp.getBonusPool(), amount));
            kxsCamp.setOrderNum(kxsCamp.getOrderNum() + number.intValue());
            baseMapper.updateById(kxsCamp);

            //给创客团队加金额
            KxsCampUser kxsCampUser = kxsCampUserService.getOne(Wrappers.<KxsCampUser>lambdaQuery()
                    .eq(KxsCampUser::getCampId, kxsCamp.getId())
                    .eq(KxsCampUser::getUserId, campUserId));
            kxsCampUser.setTeamOrderPool(NumberUtil.add(kxsCampUser.getTeamOrderPool(), amount));
            kxsCampUser.setTeamOrderNum(kxsCampUser.getTeamOrderNum() + number.intValue());
            kxsCampUserService.updateById(kxsCampUser);

            KxsCampBonusLog kxsCampBonusLog = new KxsCampBonusLog();
            kxsCampBonusLog.setCampId(kxsCamp.getId());
            kxsCampBonusLog.setCampUserId(kxsCampUser.getUserId());
            kxsCampBonusLog.setGroupId(kxsCampUser.getGroupId());
            kxsCampBonusLog.setSourceId(user.getId());
            kxsCampBonusLog.setUsername(user.getUsername());
            kxsCampBonusLog.setUserCode(user.getUserCode());
            kxsCampBonusLog.setRemark("商城下单");
            kxsCampBonusLog.setSourceNum(param.getBuyCount());
            kxsCampBonusLog.setKind(KindTypeEnum.ADD.getType());
            if (param.getProductId() == 10) {
                kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_DQ.getType()); //电签券
            } else if (param.getProductId() == 11) {
                kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_DJ.getType()); //大机券
            } else if(param.getProductId() != 10000){
                kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_ZH.getType()); //组合包
            }
            //盟主兑换券特殊处理，总共不能超过5组进入训练营的统计
            if(param.getProductId() == 10000){
                KxsCampBonusLog bonusLog = campBonusLogService.getOne(Wrappers.<KxsCampBonusLog>query()
                        .select("IFNULL(SUM(source_num), 0) as source_num")
                        .eq("camp_id", kxsCamp.getId())
                        .eq("source_id", user.getId())
                        .eq("source_type", CampSourceTypeEnum.S_MZQ.getType()));
                if(bonusLog.getSourceNum() >= 5){
                    throw new GlobalCustomerException("用户"+user.getId()+"盟主兑换券已经兑换过5组，无法继续加入统计");
                }
                long subCount = 5 - bonusLog.getSourceNum();
                kxsCampBonusLog.setSourceNum(param.getBuyCount() > subCount ? (int) subCount : param.getBuyCount());
                kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_MZQ.getType());
                kxsCampBonusLog.setRemark("盟主兑换");
            }
            campBonusLogService.save(kxsCampBonusLog);
            log.info("用户/上级{}参加训练营{}，订单ID{}, 奖金池{}，订单数{}", param.getUserId(), kxsCamp.getId(), param.getId(), kxsCamp.getBonusPool(), kxsCamp.getOrderNum());
        }


    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void campOpenNumStat(CampOpenNumStatDTO param) {

        log.info("开始处理激活数据:{}", JSON.toJSONString(param));
        //绑定时间
        LocalDateTime bindTime = LocalDateTimeUtil.parse(param.getBindTime(), DatePattern.UTC_SIMPLE_PATTERN);
        //查询当前用户参加的训练营
        KxsCamp kxsCamp = baseMapper.selectUserCamp(param.getUserId());
        R<KxsUser> kxsUserR = remoteKxsUserService.loadUserById(param.getUserId(), SecurityConstants.FROM_IN);
        KxsUser user = RetOps.of(kxsUserR)
                .getData()
                .orElseThrow(() -> new GlobalCustomerException(ErrorTypeEnum.USER_NOT_FOUND.getDescription()));
//        //参与训练营的创客ID
        Integer campUserId = param.getUserId();
//        if (kxsCamp == null) {
//            kxsCamp = baseMapper.selectUserCamp(user.getPid());
//            campUserId = user.getPid();
//        }
        if (kxsCamp == null) {
            return;
        }
        //判断绑定时间
        if (kxsCamp.getStartTime().isAfter(bindTime)) {
            log.info("机具绑定时间不在训练营之内:{}", JSON.toJSONString(param));
            return;
        }
        int sourceNum = 1;
        //给训练营加金额
        kxsCamp.setOpenNum(kxsCamp.getOpenNum() + sourceNum);
        baseMapper.updateById(kxsCamp);

        //给创客团队加金额
        KxsCampUser kxsCampUser = kxsCampUserService.getOne(Wrappers.<KxsCampUser>lambdaQuery()
                .eq(KxsCampUser::getCampId, kxsCamp.getId())
                .eq(KxsCampUser::getUserId, campUserId));

        switch (param.getActType()) {
            case 1 -> kxsCampUser.setTeamPosNum(kxsCampUser.getTeamPosNum() + sourceNum);
            case 2 -> kxsCampUser.setTeamGdNum(kxsCampUser.getTeamGdNum() + sourceNum);
            case 3 -> kxsCampUser.setTeamLkbNum(kxsCampUser.getTeamLkbNum() + sourceNum);
        }
        kxsCampUser.setTeamOpenNum(kxsCampUser.getTeamPosNum() + kxsCampUser.getTeamGdNum() + kxsCampUser.getTeamLkbNum());
        kxsCampUserService.updateById(kxsCampUser);

        KxsCampBonusLog kxsCampBonusLog = new KxsCampBonusLog();
        kxsCampBonusLog.setCampId(kxsCamp.getId());
        kxsCampBonusLog.setCampUserId(kxsCampUser.getUserId());
        kxsCampBonusLog.setGroupId(kxsCampUser.getGroupId());
        kxsCampBonusLog.setSourceId(user.getId());
        kxsCampBonusLog.setUsername(user.getUsername());
        kxsCampBonusLog.setUserCode(user.getUserCode());
        kxsCampBonusLog.setRemark("开通商户");
        kxsCampBonusLog.setSourceNum(sourceNum);
        kxsCampBonusLog.setKind(KindTypeEnum.ADD.getType());
        switch (param.getActType()) {
            case 1 -> kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_POS.getType());
            case 2 -> kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_GD.getType());
            case 3 -> kxsCampBonusLog.setSourceType(CampSourceTypeEnum.S_LKB.getType());
        }
        campBonusLogService.save(kxsCampBonusLog);
        log.info("用户{}参加训练营{},开机数+{}", param.getUserId(), kxsCamp.getId(), sourceNum);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeByCampUserIds(List<Integer> ids) {

        kxsCampUserService.removeByIds(ids);

        refreshCampUsers();

        return Boolean.TRUE;
    }

    @Override
    public IPage<KxsCamp> getBySysPage(Page<SysCampPageVO> page, KxsCamp param) {

        return baseMapper.getBySysPage(page, param);
    }


    @Override
    public R changeStatus(KxsCamp param) {

        if (!Objects.equals(param.getStatus(), CampStatusEnum.STATUS_NORMAL.getType()) && !Objects.equals(param.getStatus(), CampStatusEnum.STATUS_END.getType())) {
            return R.failed(SysErrorTypeEnum.PARAM_ERROR.getDescription());
        }
        KxsCamp camp = baseMapper.selectById(param.getId());
        Optional.ofNullable(camp).orElseThrow(() -> new GlobalCustomerException("训练营不存在"));

        if (Objects.equals(camp.getStatus(), CampStatusEnum.STATUS_END.getType())) {
            return R.failed("已结束的训练营无法再次开启");
        }
        if (Objects.equals(camp.getStatus(), param.getStatus())) {
            return R.failed("训练营已开启");
        }

        camp.setStatus(param.getStatus());

        //开启活动刷新实际开始时间
        if (Objects.equals(param.getStatus(), CampStatusEnum.STATUS_NORMAL.getType())) {
            camp.setStartTime(LocalDateTime.now());
        }
        //结束活动刷新实际结束时间
        if (Objects.equals(param.getStatus(), CampStatusEnum.STATUS_END.getType())) {
            camp.setEndTime(LocalDateTime.now());
        }

        return R.ok(baseMapper.updateById(camp));
    }


}

