package cn.iocoder.hake.module.promotion.service.reward;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.hake.framework.common.enums.CommonStatusEnum;
import cn.iocoder.hake.framework.common.pojo.PageResult;
import cn.iocoder.hake.framework.common.util.object.BeanUtils;
import cn.iocoder.hake.module.product.api.category.ProductCategoryApi;
import cn.iocoder.hake.module.product.api.spu.ProductSpuApi;
import cn.iocoder.hake.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.hake.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.hake.module.promotion.controller.admin.reward.vo.RewardActivityBaseVO;
import cn.iocoder.hake.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
import cn.iocoder.hake.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
import cn.iocoder.hake.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
import cn.iocoder.hake.module.promotion.dal.dataobject.reward.RewardActivityDO;
import cn.iocoder.hake.module.promotion.dal.mysql.reward.RewardActivityMapper;
import cn.iocoder.hake.module.promotion.enums.common.PromotionProductScopeEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;

import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
import static cn.iocoder.hake.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.hake.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.hake.module.promotion.enums.ErrorCodeConstants.*;

/**
 * 满减送活动 Service 实现类
 *
 * @author hake
 */
@Service
@Validated
public class RewardActivityServiceImpl implements RewardActivityService {

    @Resource
    private RewardActivityMapper rewardActivityMapper;

    @Resource
    private ProductCategoryApi productCategoryApi;
    @Resource
    private ProductSpuApi productSpuApi;

    @Override
    public Long createRewardActivity(RewardActivityCreateReqVO createReqVO) {
        // 1.1 校验商品范围
        validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues());
        // 1.2 校验商品是否冲突
        validateRewardActivitySpuConflicts(null, createReqVO);

        // 插入
        RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class)
                .setStatus(CommonStatusEnum.ENABLE.getStatus());
        rewardActivityMapper.insert(rewardActivity);
        // 返回
        return rewardActivity.getId();
    }

    @Override
    public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) {
        // 1.1 校验存在
        RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId());
        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能修改噢
            throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
        }
        // 1.2 校验商品范围
        validateProductScope(updateReqVO.getProductScope(), updateReqVO.getProductScopeValues());
        // 1.3 校验商品是否冲突
        validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO);

        // 2. 更新
        RewardActivityDO updateObj = BeanUtils.toBean(updateReqVO, RewardActivityDO.class);
        rewardActivityMapper.updateById(updateObj);
    }

    @Override
    public void closeRewardActivity(Long id) {
        // 校验存在
        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动，不能关闭噢
            throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
        }

        // 更新
        rewardActivityMapper.updateById(new RewardActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));
    }

    @Override
    public void deleteRewardActivity(Long id) {
        // 校验存在
        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
        if (dbRewardActivity.getStatus().equals(CommonStatusEnum.ENABLE.getStatus())) { // 未关闭的活动，不能删除噢
            throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
        }

        // 删除
        rewardActivityMapper.deleteById(id);
    }

    private RewardActivityDO validateRewardActivityExists(Long id) {
        RewardActivityDO activity = rewardActivityMapper.selectById(id);
        if (activity == null) {
            throw exception(REWARD_ACTIVITY_NOT_EXISTS);
        }
        return activity;
    }

    /**
     * 校验商品参加的活动是否冲突
     *
     * @param id             活动编号
     * @param rewardActivity 请求
     */
    private void validateRewardActivitySpuConflicts(Long id, RewardActivityBaseVO rewardActivity) {
        // 1. 获得开启的所有的活动
        List<RewardActivityDO> list = rewardActivityMapper.selectList(RewardActivityDO::getStatus, CommonStatusEnum.ENABLE.getStatus());
        if (id != null) { // 排除自己这个活动
            list.removeIf(activity -> id.equals(activity.getId()));
        }

        // 2. 完全不允许重叠
        for (RewardActivityDO item : list) {
            // 2.1 校验满减送活动时间是否冲突，如果时段不冲突那么不同的时间段内则可以存在相同的商品范围
            if (!LocalDateTimeUtil.isOverlap(item.getStartTime(), item.getEndTime(),
                    rewardActivity.getStartTime(), rewardActivity.getEndTime())) {
                continue;
            }
            // 2.2 校验商品范围是否重叠
            // 情况一：如果与该时间段内商品范围为全部的活动冲突，或 rewardActivity 商品范围为全部，那么则直接校验不通过
            // 例如说，rewardActivity 是全部活动，结果有个 db 里的 activity 是某个分类，它也是冲突的。也就是说，当前时间段内，有且仅有只能有一个活动！
            if (PromotionProductScopeEnum.isAll(item.getProductScope()) ||
                    PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
                throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(),
                        PromotionProductScopeEnum.isAll(item.getProductScope()) ?
                                "该活动商品范围为全部已覆盖包含本活动范围" : "本活动商品范围为全部已覆盖包含了该活动商品范围");
            }
            // 情况二：如果与该时间段内商品范围为类别的活动冲突
            if (PromotionProductScopeEnum.isCategory(item.getProductScope())) {
                // 校验分类是否冲突
                if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
                    if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) {
                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "商品分类范围重叠");
                    }
                }
                // 校验商品分类是否冲突
                if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
                    List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(rewardActivity.getProductScopeValues()).getCheckedData();
                    if (!intersectionDistinct(item.getProductScopeValues(),
                            convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) {
                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "该活动商品分类范围已包含本活动所选商品");
                    }
                }
            }
            // 情况三：如果与该时间段内商品范围为商品的活动冲突
            if (PromotionProductScopeEnum.isSpu(item.getProductScope())) {
                // 校验商品是否冲突
                if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
                    if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) {
                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "活动商品范围所选商品重叠");
                    }
                }
                // 校验商品分类是否冲突
                if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
                    List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(item.getProductScopeValues()).getCheckedData();
                    if (!intersectionDistinct(rewardActivity.getProductScopeValues(),
                            convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) {
                        throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "本活动商品分类范围包含了该活动所选商品");
                    }
                }
            }
        }
    }

    private void validateProductScope(Integer productScope, List<Long> productScopeValues) {
        if (Objects.equals(PromotionProductScopeEnum.SPU.getScope(), productScope)) {
            productSpuApi.validateSpuList(productScopeValues).checkError();
        } else if (Objects.equals(PromotionProductScopeEnum.CATEGORY.getScope(), productScope)) {
            productCategoryApi.validateCategoryList(productScopeValues).checkError();
        }
    }

    @Override
    public RewardActivityDO getRewardActivity(Long id) {
        return rewardActivityMapper.selectById(id);
    }

    @Override
    public PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO) {
        return rewardActivityMapper.selectPage(pageReqVO);
    }

    @Override
    public List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds) {
        // 1. 查询商品分类
        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds).getCheckedData();
        if (CollUtil.isEmpty(spuList)) {
            return Collections.emptyList();
        }
        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);

        // 2. 查询出指定 spuId 的 spu 参加的活动
        List<RewardActivityDO> activityList = rewardActivityMapper.selectListBySpuIdAndStatusAndNow(
                spuIds, convertSet(spuList, ProductSpuRespDTO::getCategoryId), CommonStatusEnum.ENABLE.getStatus());
        if (CollUtil.isEmpty(activityList)) {
            return Collections.emptyList();
        }

        // 3. 转换成 Response DTO
        return convertList(activityList, activity -> {
            RewardActivityMatchRespDTO activityDTO = BeanUtils.toBean(activity, RewardActivityMatchRespDTO.class);
            // 3.1 设置对应匹配的 spuIds
            activityDTO.setSpuIds(new ArrayList<>());
            for (Long spuId : spuIds) {
                if (PromotionProductScopeEnum.isAll(activityDTO.getProductScope())) {
                    activityDTO.getSpuIds().add(spuId);
                } else if (PromotionProductScopeEnum.isSpu(activityDTO.getProductScope())) {
                    if (CollUtil.contains(activityDTO.getProductScopeValues(), spuId)) {
                        activityDTO.getSpuIds().add(spuId);
                    }
                } else if (PromotionProductScopeEnum.isCategory(activityDTO.getProductScope())) {
                    ProductSpuRespDTO spu = spuMap.get(spuId);
                    if (spu != null && CollUtil.contains(activityDTO.getProductScopeValues(), spu.getCategoryId())) {
                        activityDTO.getSpuIds().add(spuId);
                    }
                }
            }

            // 3.2 设置每个 Rule 的描述
            activityDTO.setRules(convertList(activity.getRules(), rule ->
                    BeanUtils.toBean(rule, RewardActivityMatchRespDTO.Rule.class)
                            .setDescription(getRewardActivityRuleDescription(activityDTO.getConditionType(), rule))));
            return activityDTO;
        });
    }

}
