package co.yixiang.yshop.module.product.service.teamworkinfo;

import cn.hutool.core.util.ObjectUtil;
import co.yixiang.yshop.framework.common.constant.ShopConstants;
import co.yixiang.yshop.framework.common.util.date.DateUtils;
import co.yixiang.yshop.framework.mybatis.core.query.LambdaQueryWrapperX;
import co.yixiang.yshop.module.product.controller.admin.teamworkinfo.param.TeamworkRefundParam;
import co.yixiang.yshop.module.product.controller.app.teamworkinfo.vo.AppTeamworkProductDetailRespVO;
import co.yixiang.yshop.module.product.controller.app.teamworkinfo.vo.AppWaitTeamworkReqVO;
import co.yixiang.yshop.module.product.controller.app.teamworkinfo.vo.AppWaitTeamworkRespVO;
import co.yixiang.yshop.module.product.controller.app.teamworkinfo.vo.AppWaitTeamworkUserRespVO;
import co.yixiang.yshop.module.product.dal.dataobject.campaigndetail.CampaignDetailDO;
import co.yixiang.yshop.module.product.dal.dataobject.campaigninfo.CampaignInfoDO;
import co.yixiang.yshop.module.product.dal.mysql.campaigndetail.CampaignDetailMapper;
import co.yixiang.yshop.module.product.dal.mysql.campaigninfo.CampaignInfoMapper;
import co.yixiang.yshop.module.product.enums.teamworkinfo.TeamworkStateEnum;
import co.yixiang.yshop.module.product.enums.teamworkinfo.TeamworkTypeEnum;
import co.yixiang.yshop.module.product.enums.teamworkinfo.VirtuallyEnum;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import co.yixiang.yshop.module.product.controller.admin.teamworkinfo.vo.*;
import co.yixiang.yshop.module.product.dal.dataobject.teamworkinfo.TeamworkInfoDO;
import co.yixiang.yshop.framework.common.pojo.PageResult;

import co.yixiang.yshop.module.product.convert.teamworkinfo.TeamworkInfoConvert;
import co.yixiang.yshop.module.product.dal.mysql.teamworkinfo.TeamworkInfoMapper;

import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
import static co.yixiang.yshop.module.product.enums.ErrorCodeConstants.*;

/**
 * 拼团信息 Service 实现类
 *
 * @author moxiangrong
 */
@Service
@Validated
@Slf4j
public class TeamworkInfoServiceImpl implements TeamworkInfoService {

    @Resource
    private TeamworkInfoMapper teamworkInfoMapper;
    @Resource
    private CampaignInfoMapper campaignInfoMapper;
    @Resource
    private CampaignDetailMapper campaignDetailMapper;

    @Resource
    private RedissonClient redissonClient;


    @Override
    public Long toTeamwork(TeamworkInfoCreateVO createReqVO) {
        CampaignDetailDO campaignDetailDO = campaignDetailMapper.selectById(createReqVO.getCampaignDetailId());
        CampaignInfoDO campaignInfoDO = campaignInfoMapper.selectById(campaignDetailDO.getCampaignId());
        TeamworkInfoDO teamworkInfoDO = null;
        // 判断拼团类型
        if (ObjectUtil.isNull(createReqVO.getTeamworkId()) ||
                TeamworkTypeEnum.INITIATE.equals(createReqVO.getTeamworkType())) {
            teamworkInfoDO = new TeamworkInfoDO();
            teamworkInfoDO.setCampaignDetailId(campaignDetailDO.getId());
            teamworkInfoDO.setCampaignId(campaignInfoDO.getId());
            teamworkInfoDO.setRegimentalCommanderId(createReqVO.getUid());
            teamworkInfoDO.setPeople(1);
            teamworkInfoDO.setState(TeamworkStateEnum.IN_PROGRESS.getValue());
            teamworkInfoMapper.insert(teamworkInfoDO);
            addQueue(teamworkInfoDO.getId(), campaignInfoDO.getEffectiveTime() * 60 * 1000);
        } else {
            teamworkInfoDO = validateTeamworkInfoExists(createReqVO.getTeamworkId());
            // 拼团成功
            if (teamworkInfoDO.getPeople() + 1 >= campaignInfoDO.getPerson()) {
                removeQueue(teamworkInfoDO.getId());
                teamworkInfoDO.setState(TeamworkStateEnum.SUCCESS.getValue());
            }
            teamworkInfoDO.setPeople(teamworkInfoDO.getPeople() + 1);
            teamworkInfoMapper.updateById(teamworkInfoDO);
        }
        return teamworkInfoDO.getId();
    }


    private TeamworkInfoDO validateTeamworkInfoExists(Long id) {
        TeamworkInfoDO teamworkInfoDO = teamworkInfoMapper.selectById(id);
        if (ObjectUtil.isNull(teamworkInfoDO)) {
            throw exception(TEAMWORK_INFO_NOT_EXISTS);
        }
        return teamworkInfoDO;
    }


    @Override
    public PageResult<TeamworkInfoRespVO> getTeamworkInfoPage(TeamworkInfoPageReqVO pageReqVO) {
        Page<TeamworkInfoRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<TeamworkInfoRespVO> list = teamworkInfoMapper.getTeamworkInfoPage(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public PageResult<TeamworkOrderInfoRespVO> getTeamworkOrderInfoPage(TeamworkOrderInfoPageReqVO pageReqVO) {
        Page<TeamworkOrderInfoRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<TeamworkOrderInfoRespVO> list = teamworkInfoMapper.getTeamworkOrderInfoPage(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<Long> getNotTeamwork(Long campaignId) {
        return teamworkInfoMapper.getNotTeamwork(campaignId);
    }

    @Override
    public void updateTeamworkFail(Long campaignId) {
        teamworkInfoMapper.teamworkFail(campaignId);
    }

    @Override
    public void teamworkFail(Long teamworkId, Integer number) {
        TeamworkInfoDO teamworkInfoDO = validateTeamworkInfoExists(teamworkId);
        // 退回活动库存
        CampaignDetailDO campaignDetailDO = campaignDetailMapper.selectById(teamworkInfoDO.getCampaignDetailId());
        campaignDetailMapper.updateById(new CampaignDetailDO().setId(campaignDetailDO.getId())
                .setStock(campaignDetailDO.getStock() + number));
        // 修改状态为拼团失败
        teamworkInfoMapper.updateById(new TeamworkInfoDO().setId(teamworkId).setState(TeamworkStateEnum.FAIL.getValue()));
    }

    @Override
    public void teamworkSuccess(Long teamworkId) {
        teamworkInfoMapper.updateById(new TeamworkInfoDO()
                .setState(TeamworkStateEnum.SUCCESS.getValue()).setId(teamworkId));
    }

    @Override
    public Long teamworkEndTime(Long teamworkId) {
        TeamworkInfoDO teamworkInfoDO = this.validateTeamworkInfoExists(teamworkId);
        if (!TeamworkStateEnum.IN_PROGRESS.getValue().equals(teamworkInfoDO.getState())) {
            return null;
        }
        CampaignInfoDO campaignInfoDO = campaignInfoMapper.selectById(teamworkInfoDO.getCampaignId());
        // 计算取消时间
        LocalDateTime cancelTime =
                DateUtils.getMoreMinuteAfter(teamworkInfoDO.getCreateTime(), campaignInfoDO.getEffectiveTime());
        return DateUtils.toTimeStamp(cancelTime);
    }

    @Override
    public Integer getTeamworkState(Long teamworkId) {
        return this.validateTeamworkInfoExists(teamworkId).getState();
    }

    @Override
    public List<AppWaitTeamworkRespVO> getWaitTeamworkList(AppWaitTeamworkReqVO reqVO) {
        List<AppWaitTeamworkRespVO> waitTeamworkRespVOS = teamworkInfoMapper.getWaitTeamworkList(reqVO);
        if (CollectionUtils.isEmpty(waitTeamworkRespVOS)) return new ArrayList<>();
        List<Long> ids = waitTeamworkRespVOS.stream().map(AppWaitTeamworkRespVO::getId).collect(Collectors.toList());
        List<AppWaitTeamworkUserRespVO> waitTeamworkUserRespVOS = teamworkInfoMapper.getWaitTeamworkHeadList(ids);
        Map<Long, AppWaitTeamworkUserRespVO> map =
                waitTeamworkUserRespVOS.stream().collect(Collectors
                        .toMap(AppWaitTeamworkUserRespVO::getId, Function.identity()));
        for (AppWaitTeamworkRespVO respVO : waitTeamworkRespVOS) {
            respVO.setUser(map.get(respVO.getId()));
            respVO.setCloseTime(DateUtils.getMoreMinuteAfter(respVO.getCreateTime(), respVO.getEffectiveTime()));
        }
        return waitTeamworkRespVOS;
    }

    @Override
    public List<AppWaitTeamworkUserRespVO> getUserTeamworkList(Long id) {
        return teamworkInfoMapper.getWaitTeamworkUserList(id);
    }

    @Override
    public AppTeamworkProductDetailRespVO getProductDetail(Long id) {
        AppTeamworkProductDetailRespVO detailRespVO = teamworkInfoMapper.getProductDetail(id);
        if (ObjectUtil.isNull(detailRespVO)) {
            return new AppTeamworkProductDetailRespVO();
        }
        TeamworkInfoDO teamworkInfoDO = validateTeamworkInfoExists(id);
        CampaignInfoDO campaignInfoDO = campaignInfoMapper.selectById(teamworkInfoDO.getCampaignId());
        List<AppWaitTeamworkUserRespVO> users = teamworkInfoMapper.getWaitTeamworkUserList(id);
        // 判断是否虚拟成团
        if (VirtuallyEnum.YES.getValue().equals(campaignInfoDO.getIsVirtually()) &&
                TeamworkStateEnum.SUCCESS.getValue().equals(teamworkInfoDO.getState())) {
            // 虚拟人数
            Integer virtualNumber = campaignInfoDO.getPerson() - teamworkInfoDO.getPeople();
            // 随机查询头像
            List<Long> userIds = users.stream().map(AppWaitTeamworkUserRespVO::getUid).collect(Collectors.toList());
            List<String> avatars = teamworkInfoMapper.randomAvatar(userIds, virtualNumber);
            for (String avatar : avatars) users.add(new AppWaitTeamworkUserRespVO().setNickname(avatar));
        }
        detailRespVO.setUsers(users);
        return detailRespVO;
    }

    @Override
    public void joinJudge(Long uid, Long teamworkId) {
        if (CollectionUtils.isNotEmpty(teamworkInfoMapper.joinJudge(uid, teamworkId)))
            throw exception(TEAMWORK_REPEAT_JOIN_ERROR);
    }

    @Override
    public TeamworkInfoDO get(Long id) {
        return validateTeamworkInfoExists(id);
    }

    @Override
    public CampaignInfoDO getCampaignInfoById(Long id) {
        TeamworkInfoDO teamworkInfoDO = validateTeamworkInfoExists(id);
        return campaignInfoMapper.selectById(teamworkInfoDO.getCampaignId());
    }

    @Override
    public void refundHandle(TeamworkRefundParam param) {
        TeamworkInfoDO teamworkInfoDO = validateTeamworkInfoExists(param.getId());
        TeamworkInfoDO updateDO = new TeamworkInfoDO();
        updateDO.setId(teamworkInfoDO.getId());
        // 如果当前拼团只有一人，直接结束拼团
        if (teamworkInfoDO.getPeople() == 1) {
            updateDO.setState(TeamworkStateEnum.FAIL.getValue());
        } else {
            updateDO.setPeople(teamworkInfoDO.getPeople() - 1);
            // 下一个人成为团长
            updateDO.setRegimentalCommanderId(teamworkInfoMapper.getMember(param.getUid(), param.getId()));
        }
        teamworkInfoMapper.updateById(updateDO);
    }


    /**
     * 加入延时队列
     *
     * @param id
     * @param time
     */
    private void addQueue(Long id, long time) {
        try {
            log.info("添加延时队列 ，拼团id：{}, 延时时间：{}", id, time);
            RBlockingDeque<Long> blockingDeque = redissonClient.getBlockingDeque(ShopConstants.TEAMWORK_CHANGE);
            RDelayedQueue<Long> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            delayedQueue.offer(id, time, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            throw exception(CAMPAIGN_ADD_QUEUE_ERROR, e.getMessage());
        }
    }

    /**
     * 从延时队列中移除
     *
     * @param id
     */
    private void removeQueue(Long id) {
        try {
            RBlockingDeque<Long> blockingDeque = redissonClient.getBlockingDeque(ShopConstants.TEAMWORK_CHANGE);
            RDelayedQueue<Long> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            delayedQueue.remove(id);
            log.info("移除延时队列成功 ，拼团id：{}", id);
        } catch (Exception e) {
            e.printStackTrace();
            throw exception(CAMPAIGN_REMOVE_QUEUE_ERROR, e.getMessage());
        }
    }

}
