package co.yixiang.yshop.module.shop.service.popup;

import cn.hutool.core.util.ObjectUtil;
import co.yixiang.yshop.framework.common.constant.ShopConstants;
import co.yixiang.yshop.framework.common.pojo.PageResult;
import co.yixiang.yshop.framework.common.util.date.DateUtils;
import co.yixiang.yshop.framework.redis.util.RedissonUtil;
import co.yixiang.yshop.module.shop.controller.admin.popup.vo.PopupCreateReqVO;
import co.yixiang.yshop.module.shop.controller.admin.popup.vo.PopupPageReqVO;
import co.yixiang.yshop.module.shop.controller.admin.popup.vo.PopupUpdateReqVO;
import co.yixiang.yshop.module.shop.controller.app.popup.vo.PopupInfoRespVO;
import co.yixiang.yshop.module.shop.convert.popup.PopupConvert;
import co.yixiang.yshop.module.shop.dal.dataobject.popup.PopupDO;
import co.yixiang.yshop.module.shop.dal.mysql.popup.PopupMapper;
import co.yixiang.yshop.module.shop.enums.common.StateEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

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

/**
 * 弹窗 Service 实现类
 *
 * @author moxiangrong
 */
@Service
@Validated
@Slf4j
public class PopupServiceImpl implements PopupService {

    @Resource
    private PopupMapper popupMapper;

    @Resource
    private RedissonUtil redissonUtil;

    @Override
    public Long createPopup(PopupCreateReqVO createReqVO) {
        PopupDO popup = PopupConvert.INSTANCE.convert(createReqVO);
        // 校验弹窗时间是否交叉
        if (this.checkPopup(popup)) throw exception(POPUP_NOT_REPEAT);
        // 判断状态
        Long time = this.judgeState(popup);
        // 插入
        popupMapper.insert(popup);
        if (ObjectUtil.isNotNull(time)) {
            this.addQueue(popup.getId(), time);
        }
        // 返回
        return popup.getId();
    }

    @Override
    public void updatePopup(PopupUpdateReqVO updateReqVO) {
        // 校验存在
        this.validatePopupExists(updateReqVO.getId());
        PopupDO updateObj = PopupConvert.INSTANCE.convert(updateReqVO);
        // 校验弹窗时间是否交叉
        if (this.checkPopup(updateObj)) throw exception(POPUP_NOT_REPEAT);
        // 判断状态
        Long time = this.judgeState(updateObj);
        // 更新
        popupMapper.updateById(updateObj);
        if (ObjectUtil.isNotNull(time)) {
            this.removeQueue(updateObj.getId());
            this.addQueue(updateObj.getId(), time);
        }
    }

    /**
     * 校验弹窗时间是否交叉
     *
     * @param popup
     * @return
     */
    private Boolean checkPopup(PopupDO popup) {
        return CollectionUtils.isNotEmpty(popupMapper.checkPopup(PopupConvert.INSTANCE.convertParam(popup)));
    }

    /**
     * 判断状态并返回延迟时间
     *
     * @param popup
     * @return
     */
    private Long judgeState(PopupDO popup) {
        Long time = null;
        if (DateUtils.isBelong(popup.getStartTime(), popup.getEndTime())) {
            popup.setState(StateEnum.OPEN.getValue());
            time = DateUtils.getMoreMillisecondAfter(popup.getEndTime());
        } else if (DateUtils.isBefore(popup.getStartTime())) {
            popup.setState(StateEnum.CLOSE.getValue());
            time = DateUtils.getMoreMillisecondAfter(popup.getStartTime());
        } else {
            popup.setState(StateEnum.CLOSE.getValue());
        }
        return time;
    }

    @Override
    public void deletePopup(Long id) {
        // 校验存在
        this.validatePopupExists(id);
        // 删除
        popupMapper.deleteById(id);
        this.removeQueue(id);
    }

    private PopupDO validatePopupExists(Long id) {
        PopupDO popupDO = popupMapper.selectById(id);
        if (ObjectUtil.isNull(popupDO)) {
            throw exception(POPUP_NOT_EXISTS);
        }
        return popupDO;
    }

    @Override
    public PopupDO getPopup(Long id) {
        return popupMapper.selectById(id);
    }

    @Override
    public List<PopupDO> getPopupList(Collection<Long> ids) {
        return popupMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<PopupDO> getPopupPage(PopupPageReqVO pageReqVO) {
        return popupMapper.selectPage(pageReqVO);
    }

    @Override
    public void stateChange(Long id) {
        // 校验存在
        PopupDO popupDO = this.validatePopupExists(id);
        PopupDO updateDO = new PopupDO();
        updateDO.setId(id);
        updateDO.setStartTime(popupDO.getStartTime());
        updateDO.setEndTime(popupDO.getEndTime());
        Long time = this.judgeState(updateDO);
        updateDO.setState(StateEnum.CLOSE.getValue().equals(popupDO.getState())
                ? StateEnum.OPEN.getValue() : StateEnum.CLOSE.getValue());
        popupMapper.updateById(updateDO);
        if (ObjectUtil.isNotNull(time)) {
            this.addQueue(id, time);
        }
    }

    @Override
    public PopupInfoRespVO getPopupInfo() {
        PopupDO popupDO = popupMapper.selectOne(new LambdaQueryWrapper<PopupDO>()
                .eq(PopupDO::getState, StateEnum.OPEN.getValue()));
        return PopupConvert.INSTANCE.convert1(popupDO);
    }

    /**
     * 加入延时队列
     *
     * @param id
     * @param time
     */
    private void addQueue(Long id, long time) {
        log.info("添加延时队列 ，弹窗广告id：{}, 延时时间：{}", id, time);
        redissonUtil.delayedOfferThrow(ShopConstants.POPUP_CHANGE, id, time, TimeUnit.MILLISECONDS, POPUP_ADD_QUEUE_ERROR);
    }

    /**
     * 从延时队列中移除
     *
     * @param id
     */
    private void removeQueue(Long id) {
        redissonUtil.delayedRemoveThrow(ShopConstants.POPUP_CHANGE, id, POPUP_REMOVE_QUEUE_ERROR);
        log.info("移除延时队列成功 ，弹窗广告id：{}", id);
    }

}
