package com.yonge.cooleshow.biz.dal.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yonge.cooleshow.biz.dal.entity.ActivityReward;
import com.yonge.cooleshow.biz.dal.entity.CouponInfo;
import com.yonge.cooleshow.biz.dal.entity.CouponInventory;
import com.yonge.cooleshow.biz.dal.entity.CouponIssue;
import com.yonge.cooleshow.biz.dal.entity.SysUser;
import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
import com.yonge.cooleshow.biz.dal.enums.coupon.CouponInventoryEnum;
import com.yonge.cooleshow.biz.dal.enums.coupon.CouponUseStateEnum;
import com.yonge.cooleshow.biz.dal.enums.coupon.CouponValidTypeEnum;
import com.yonge.cooleshow.biz.dal.mapper.CouponInfoMapper;
import com.yonge.cooleshow.biz.dal.mapper.CouponInventoryMapper;
import com.yonge.cooleshow.biz.dal.mapper.CouponIssueMapper;
import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
import com.yonge.cooleshow.biz.dal.queryInfo.CouponInfoQuery;
import com.yonge.cooleshow.biz.dal.queryInfo.CouponInventoryQuery;
import com.yonge.cooleshow.biz.dal.service.ActivityRewardService;
import com.yonge.cooleshow.biz.dal.service.CouponInfoService;
import com.yonge.cooleshow.biz.dal.vo.coupon.CouponInfoWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponInventoryWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.coupon.CouponOrderWrapper;
import com.yonge.cooleshow.common.enums.EStatus;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.base.util.ThreadPool;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券信息 服务实现类
 * </p>
 *
 * @author Eric
 * @since 2022-09-02
 */
@Service
public class CouponInfoServiceImp extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private CouponInventoryMapper couponInventoryMapper;
    @Autowired
    private CouponIssueMapper couponIssueMapper;
    @Autowired
    private ActivityRewardService activityRewardService;

    /**
     * 分页查询优惠券信息
     *
     * @param page  IPage<CouponInfoWrapper>
     * @param query CouponInfoQuery
     * @return IPage<CouponInfoWrapper>
     */
    @Override
    public IPage<CouponInfoWrapper> queryCouponPageInfo(IPage<CouponInfoWrapper> page, CouponInfoQuery query) {

        // 优惠券信息
        List<CouponInfoWrapper> wrappers = getBaseMapper().selectCouponPageInfo(page, query);

        if (CollectionUtils.isEmpty(wrappers)) {
            // 没有数据，直接返回
            return page.setRecords(Lists.newArrayList());
        }

        List<Long> couponIds = wrappers.stream()
                .map(CouponInfoWrapper::getId).distinct().collect(Collectors.toList());

        // 查询优惠券发放人数
        Map<Long, Integer> issueNumMap = getBaseMapper().selectCouponIssueStatInfo(couponIds,
                        CouponInfoQuery.IssueStatQuery.builder()
                                //.timestamp(DateTime.now().getMillis())
                                .excludeUseState(Lists.newArrayList(CouponUseStateEnum.WITHDRAW)).build()).stream()
                .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getTotal, (o, n) -> n));

        // 关联奖品数
        Map<Long, Integer> rewardNumMap = getBaseMapper().selectActivityCouponStatInfo(couponIds).stream()
                .collect(Collectors.toMap(StatGroupWrapper::getId, StatGroupWrapper::getTotal, (o, n) -> n));

        // 更新用户信息
        List<Long> userIds = wrappers.stream().map(CouponInfoWrapper::getUpdatedBy).distinct().collect(Collectors.toList());

        Map<Long, String> userNameMap = sysUserMapper.selectBatchIds(userIds).stream()
                .collect(Collectors.toMap(SysUser::getId, x -> {

                    if (StringUtils.isNotEmpty(x.getUsername())) {
                        return x.getUsername();
                    }
                    return x.getRealName();
                }, (o, n) -> n));

        List<Long> updateIds = Lists.newArrayList();
        for (CouponInfoWrapper item : wrappers) {

            if (CouponValidTypeEnum.TIME_PERIOD == item.getValidType()
                    && DateTime.now().getMillis() > item.getEndTime()) {

                updateIds.add(item.getId());
                // 更新显示状态为禁用
                item.setStatus(EStatus.DISABLE.getValue());
            }

            item.rewardNum(rewardNumMap.getOrDefault(item.getId(), 0))
                    .updatedUser(userNameMap.getOrDefault(item.getUpdatedBy(), ""))
                    .setIssueNum(issueNumMap.getOrDefault(item.getId(), 0));
        }

        // 增加优惠券时间过期处理流程
        if (CollectionUtils.isNotEmpty(updateIds)) {

            ThreadPool.getExecutor().submit(() -> {

                CouponInfoWrapper wrapper = CouponInfoWrapper.builder()
                        .status(EStatus.DISABLE.getValue())
                        .build();

                getBaseMapper().update(JSON.parseObject(wrapper.jsonString(), CouponInfo.class),
                        Wrappers.<CouponInfo>lambdaQuery().in(CouponInfo::getId, updateIds));

            });

        }

        return page.setRecords(wrappers);
    }

    /**
     * 优惠券信息
     *
     * @param id 优惠券ID
     * @return CouponInfo
     */
    @Override
    public CouponInfo queryCouponInfoById(Long id) {

        CouponInfo couponInfo = getBaseMapper().selectById(id);
        if (Objects.isNull(couponInfo)) {
            throw new BizException("无效的优惠券ID");
        }

        return couponInfo;
    }

    /**
     * 新增或更新优惠券信息
     *
     * @param couponInfo CouponInfo
     * @param stateUpdate 更新标识
     * @return CouponInfo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public CouponInfo saveOrUpdateCouponInfo(CouponInfo couponInfo, Boolean stateUpdate) {

        if (Objects.isNull(couponInfo.getId())) {

            // 新增优惠券
            save(couponInfo);

            // 添加库存变更记录
            if (couponInfo.getInventory() > 0) {

                CouponInventoryWrapper wrapper = CouponInventoryWrapper.builder()
                        .userId(couponInfo.getCreatedBy())
                        .couponId(couponInfo.getId())
                        .dataType(CouponInventoryEnum.ADDITION)
                        .number(couponInfo.getInventory())
                        .remark("")
                        .status(EStatus.ENABLE.getValue())
                        .build();

                couponInventoryMapper.insert(JSON.parseObject(wrapper.jsonString(), CouponInventory.class));
            }
        } else {

            CouponInfo info = getById(couponInfo.getId());
            if (Objects.isNull(info)) {
                throw new BizException("无效的优惠券ID");
            }

            if (stateUpdate) {

                // 优惠券状态更新
                if (EStatus.ENABLE.match(couponInfo.getStatus())
                        && CouponValidTypeEnum.TIME_PERIOD == info.getValidType()
                        && DateTime.now().getMillis() > info.getEndTime()) {
                    throw new BizException("已过期优惠券不允许修改");
                }

                // 优惠券已关联奖品，不能停用
                int ret = activityRewardService.count(Wrappers.<ActivityReward>lambdaQuery()
                        .eq(ActivityReward::getCouponId, couponInfo.getId()));
                if (ret > 0 && EStatus.DISABLE.match(couponInfo.getStatus())) {
                    throw new BizException("优惠券已被奖品关联");
                }

            } else {

                // 优惠券已过期
                if (CouponValidTypeEnum.TIME_PERIOD == info.getValidType()
                        && DateTime.now().getMillis() > info.getEndTime()) {

                    throw new BizException("已过期优惠券不允许修改");
                }

                // 优惠券未过期，已发放不允许修复
                List<Long> couponIds = Lists.newArrayList(info.getId());

                int ret = 0;
                // 查询优惠券发放人数
                ret += getBaseMapper().selectCouponIssueStatInfo(couponIds,
                                CouponInfoQuery.IssueStatQuery.builder()
                                        .excludeUseState(Lists.newArrayList(CouponUseStateEnum.WITHDRAW)).build()).stream()
                        .mapToInt(StatGroupWrapper::getTotal).sum();
                if (ret > 0) {
                    throw new BizException("已发放优惠券不允许修改");
                }

                // 关联奖品数
                ret += getBaseMapper().selectActivityCouponStatInfo(couponIds).stream()
                        .mapToInt(StatGroupWrapper::getTotal).sum();

                if (ret > 0) {
                    throw new BizException("已关联奖品优惠券不允许修改");
                }

                // 启用状态不允许修改
                if (EStatus.ENABLE.match(info.getStatus())) {

                    throw new BizException("已启用优惠券不允许修改");
                }

            }

            // 更新优惠券
            updateById(couponInfo);
        }

        return getById(couponInfo.getId());
    }

    /**
     * 优惠券库存量调整信息
     *
     * @param page  IPage<CouponInventoryWrapper>
     * @param query CouponInventoryQuery
     * @return IPage<CouponInventoryWrapper>
     */
    @Override
    public IPage<CouponInventoryWrapper> queryCouponInventoryPageInfo(IPage<CouponInventoryWrapper> page, CouponInventoryQuery query) {

        // 优惠券库存调整信息
        List<CouponInventoryWrapper> wrappers = getBaseMapper().selectCouponInventoryPageInfo(page, query);

        if (CollectionUtils.isEmpty(wrappers)) {
            // 没有数据，直接返回
            return page.setRecords(Lists.newArrayList());
        }

        return page.setRecords(wrappers);
    }

    /**
     * 查询订单优惠券信息
     *
     * @param userId 用户ID
     * @param query  CouponInfoQuery.CouponOrderQuery
     * @return CouponOrderWrapper
     */
    @Override
    public CouponOrderWrapper queryUserOrderCouponInfo(Long userId, CouponInfoQuery.CouponOrderQuery query) {

        CouponOrderWrapper wrapper = CouponOrderWrapper.builder()
                .discountedPrices(0D)
                .couponInfos(Lists.newArrayList())
                .build();

        // 查询用户订单优惠券信息
        List<CouponOrderWrapper.CouponInfo> couponInfos = getBaseMapper().selectUserOrderCouponInfo(userId, query);

        // 计算抵扣金额
        double discountPrices = 0;
        // 订单金额
        double amount = query.getAmount();

        for (CouponOrderWrapper.CouponInfo item : couponInfos) {

            if (amount >= item.getUseLimit()) {

                // 重置数据选中状态
                item.setSelected(true);
                // 重新计算优惠券门槛金额
                amount += item.getUseLimit();
                // 叠加优惠券优惠金额
                discountPrices += item.getDiscountPrice();
            }
        }

        return wrapper.discountedPrices(discountPrices).couponInfos(couponInfos);
    }

    /**
     * 更新用户订单优惠券状态
     *
     * @param wrapper CouponOrderWrapper
     * @return int 受影响的行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateUserOrderCouponInfo( CouponOrderWrapper wrapper) {

        // 更新数据信息
        List<CouponOrderWrapper.CouponIssue> couponIssues = Lists.newArrayList();

        if (wrapper.getReset()) {

            // 重置订单优惠券使用状态
            List<CouponIssue> issues = couponIssueMapper.selectList(Wrappers.<CouponIssue>lambdaQuery()
                    .eq(CouponIssue::getOrderNo, wrapper.getOrderNo()));

            for (CouponIssue item : issues) {

                couponIssues.add(CouponOrderWrapper.CouponIssue.builder()
                        .id(item.getId())
                        .useState(CouponUseStateEnum.USABLE)
                        .useTime(0L)
                        .orderNo("")
                        .useType(null)
                        .build());
            }
        } else {

            // 更新优惠券为已使用
            List<CouponOrderWrapper.CouponInfo> couponInfos = wrapper.getCouponInfos().stream()
                    .filter(CouponOrderWrapper.CouponInfo::getSelected).collect(Collectors.toList());

            for (CouponOrderWrapper.CouponInfo item : couponInfos) {

                couponIssues.add(CouponOrderWrapper.CouponIssue.builder()
                                .id(item.getIssueId())
                                .useState(CouponUseStateEnum.USED)
                                .useTime(DateTime.now().getMillis())
                                .useType(wrapper.getUseType())
                                .orderNo(wrapper.getOrderNo())
                        .build());
            }
        }

        for (CouponOrderWrapper.CouponIssue item : couponIssues) {

            couponIssueMapper.updateById(JSON.parseObject(item.jsonString(), CouponIssue.class));
        }

        return CollectionUtils.size(couponIssues);
    }

    /**
     * 新增优惠券库存量调整信息
     *
     * @param inventory CouponInventory
     * @return int 入库行数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int saveOrUpdateCouponInventoryInfo(CouponInventory inventory) {

        // 优惠券信息
        CouponInfo couponInfo = getBaseMapper().selectById(inventory.getCouponId());

        if (Objects.isNull(couponInfo)) {
            throw new BizException("无效的优惠券ID");
        }

        // 比较当前库存量是否有效
        if (CouponInventoryEnum.REDUCE == inventory.getDataType()
                && couponInfo.getInventory() < inventory.getNumber()) {

            throw new BizException("当前优惠券库存为: " + couponInfo.getInventory());
        }

        int ret = couponInfo.getInventory();
        switch (inventory.getDataType()) {
            case ADDITION:
                ret += inventory.getNumber();
                break;
            case REDUCE:
                 ret -= inventory.getNumber();
                break;
            default:
                break;
        }

        // 调整优惠券库存量信息
        CouponInfoWrapper wrapper = CouponInfoWrapper.builder()
                .id(couponInfo.getId())
                .inventory(ret)
                .updatedBy(inventory.getUserId())
                .updateTime(DateTime.now().getMillis())
                .build();
        getBaseMapper().updateById(JSON.parseObject(wrapper.jsonString(), CouponInfo.class));

        // 新增优惠券库存量记录
        return couponInventoryMapper.insert(inventory);
    }

    @Override
    public int updateStock(Long couponId, int size, InOrOutEnum inOrOut) {
        return baseMapper.updateStock(couponId,size,inOrOut);
    }
}
