package com.czg.service.market.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.service.ShopInfoService;
import com.czg.config.RabbitPublisher;
import com.czg.config.RedisCst;
import com.czg.constants.ParamCodeCst;
import com.czg.exception.CzgException;
import com.czg.market.dto.AcPushEventDTO;
import com.czg.market.entity.AcPushEvent;
import com.czg.market.entity.SmsPushEventUser;
import com.czg.market.service.AcPushEventService;
import com.czg.market.service.MkShopCouponRecordService;
import com.czg.service.RedisService;
import com.czg.service.market.mapper.AcPushEventMapper;
import com.czg.service.market.mapper.SmsPushEventUserMapper;
import com.czg.system.service.SysParamsService;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 公众号推送任务 服务层实现。
 *
 * @author ww
 * @since 2025-10-20
 */
@Slf4j
@Service
public class AcPushEventServiceImpl extends ServiceImpl<AcPushEventMapper, AcPushEvent> implements AcPushEventService {
    @Resource
    private SmsPushEventUserMapper eventUserMapper;
    @DubboReference
    private ShopInfoService shopInfoService;
    @DubboReference
    private SysParamsService paramsService;
    @Resource
    private MkShopCouponRecordService couponRecordService;
    @Resource
    private RabbitPublisher rabbitPublisher;
    @Resource
    private RedisService redisService;

    @Override
    public Page<AcPushEventDTO> getPushEventPage(Integer page, Integer size, Long shopId, Integer status, Long id) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(AcPushEvent::getShopId, shopId)
                .eq(AcPushEvent::getId, id)
                .eq(AcPushEvent::getStatus, status)
                .eq(AcPushEvent::getIsDel, 0)
                .orderBy(AcPushEvent::getCreateTime).desc();
        Page<AcPushEventDTO> page1 = pageAs(new Page<>(page, size), queryWrapper, AcPushEventDTO.class);
        for (AcPushEventDTO record : page1.getRecords()) {
            if (record.getUserType() != 2) {
                continue;
            }
            SmsPushEventUser eventUser = eventUserMapper.selectOneByQuery(
                    new QueryWrapper()
                            .eq(SmsPushEventUser::getType, "ac")
                            .eq(SmsPushEventUser::getEventId, record.getId())
            );
            record.setPushEventUser(eventUser);
        }
        return page1;
    }

    @Override
    public void addPushEvent(AcPushEventDTO param) {
        AssertUtil.isNull(param.getUserType(), "请选择用户范围后进行推送");
        if (param.getUserType() == 2 && param.getPushEventUser() == null) {
            throw new CzgException("范围推送时，必须指定用户范围");
        } else if (param.getUserType() == 3 && param.getUserId() == null) {
            throw new CzgException("指定推送时，必须指定用户");
        }
        if (param.getSendType() == 2) {
            AssertUtil.isNull(param.getSendTime(), "定时发送时，必须指定发送时间");
        } else {
            param.setSendTime(LocalDateTime.now());
        }
        checkPushEventSendCount(param.getShopId());
        AcPushEvent pushEvent = BeanUtil.toBean(param, AcPushEvent.class);
        save(pushEvent);
        if (param.getUserType() == 2) {
            SmsPushEventUser eventUser = param.getPushEventUser();
            eventUser.setType("ac");
            eventUser.setShopId(param.getShopId());
            eventUser.setEventId(pushEvent.getId());
            eventUserMapper.insert(eventUser);
        }
        param.setId(pushEvent.getId());
        Long mainShopId = shopInfoService.getMainIdByShopId(param.getShopId());
        // 推送消息 进行 消息发送
        rabbitPublisher.sendApplySmsMsg(mainShopId + "," + pushEvent.getId(), "sendWechatTemp");
        if (param.getSendType() == 1 && StrUtil.isNotBlank(param.getCoupon())) {
            couponRecordService.acGrantCoupon(param);
        }
    }

    @Override
    public void sendPushEventCoupon(String eventId) {
        AcPushEvent acPushEvent = getOne(new QueryWrapper().eq(AcPushEvent::getId, eventId));
        AssertUtil.isNull(acPushEvent, "券发放失败，推送事件不存在");
        if (StrUtil.isNotBlank(acPushEvent.getCoupon())) {
            AcPushEventDTO pushEvent = BeanUtil.toBean(acPushEvent, AcPushEventDTO.class);
            SmsPushEventUser eventUser = eventUserMapper.selectOneByQuery(
                    new QueryWrapper()
                            .eq(SmsPushEventUser::getType, "ac")
                            .eq(SmsPushEventUser::getEventId, eventId)
            );
            pushEvent.setPushEventUser(eventUser);
            couponRecordService.acGrantCoupon(pushEvent);
        }
    }

    @Override
    public void upPushEvent(AcPushEventDTO param) {
        AssertUtil.isNull(param.getUserType(), "请选择用户范围后进行推送");
        if (param.getUserType() == 2 && param.getPushEventUser() == null) {
            throw new CzgException("范围推送时，必须指定用户范围");
        } else if (param.getUserType() == 3 && param.getUserId() == null) {
            throw new CzgException("指定推送时，必须指定用户");
        }
        if (param.getSendType() == 2) {
            AssertUtil.isNull(param.getSendTime(), "定时发送时，必须指定发送时间");
        } else {
            param.setSendTime(LocalDateTime.now());
        }
        checkPushEventSendCount(param.getShopId());
        AcPushEvent pushEvent = BeanUtil.toBean(param, AcPushEvent.class);
        updateById(pushEvent);
        eventUserMapper.deleteByQuery(new QueryWrapper()
                .eq(SmsPushEventUser::getType, "ac")
                .eq(SmsPushEventUser::getEventId, pushEvent.getId()));
        if (param.getUserType() == 2) {
            SmsPushEventUser eventUser = param.getPushEventUser();
            eventUser.setType("ac");
            eventUser.setShopId(param.getShopId());
            eventUser.setEventId(pushEvent.getId());
            eventUserMapper.insert(eventUser);
        }
        Long mainShopId = shopInfoService.getMainIdByShopId(param.getShopId());
        // 推送消息 进行 消息发送
        rabbitPublisher.sendApplySmsMsg(mainShopId + "," + pushEvent.getId(), "sendWechatTemp");
        if (param.getSendType() == 1 && StrUtil.isNotBlank(param.getCoupon())) {
            couponRecordService.acGrantCoupon(param);
        }
    }

    @Override
    public void deletePushEvent(Long id) {
        AcPushEvent pushEvent = getById(id);
        AssertUtil.isNull(pushEvent, "记录不存在");
        if (pushEvent.getStatus() == 2) {
            throw new CzgException("已发送的记录不能删除");
        }
        redisService.del(RedisCst.classKeyExpired.EXPIRED_WECHAT + id);
        pushEvent.setIsDel(1);
        updateById(pushEvent);
    }

    private void checkPushEventSendCount(Long shopId) {
        String maxSendCount = paramsService.getSysParamValue(ParamCodeCst.Wechat.Ac.AC_DAY_COUNT);
        if (StrUtil.isBlank(maxSendCount)) {
            return;
        }
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime todayEnd = LocalDate.now().atTime(23, 59, 59);

        Long maxCount = null;
        try {
            maxCount = Long.parseLong(maxSendCount);
            if (maxCount < 0) {
                throw new CzgException("每日最大发送量参数不能为负数");
            }
        } catch (NumberFormatException e) {
            log.error("每日最大发送量参数格式错误，必须为整数", e);
        }
        Long sendCount = count(
                new QueryWrapper()
                        .eq(AcPushEvent::getShopId, shopId)
                        .ne(AcPushEvent::getStatus, -1)
                        .between(AcPushEvent::getSendTime, todayStart, todayEnd)
        );
        if (maxCount != null && sendCount >= maxCount) {
            throw new CzgException("今日已发送" + maxCount + "条，不能继续发送");
        }
    }
}
