package com.ruoyi.xq.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.BeanConvertUtil;
import com.ruoyi.xq.domain.SysPush;
import com.ruoyi.xq.domain.SysPushLog;
import com.ruoyi.xq.domain.User;
import com.ruoyi.xq.dto.admin.syspush.SysPushImageContextDTO;
import com.ruoyi.xq.dto.admin.syspush.SysPushMasterDTO;
import com.ruoyi.xq.dto.admin.syspush.SysPushParams;
import com.ruoyi.xq.dto.common.syspush.SendSysPushResp;
import com.ruoyi.xq.dto.common.user.MinUserDTO;
import com.ruoyi.xq.enums.syspush.*;
import com.ruoyi.xq.enums.userinfo.UserGenderEnum;
import com.ruoyi.xq.mapper.SysPushMapper;
import com.ruoyi.xq.notice.data.*;
import com.ruoyi.xq.notice.enums.MessageBaseTypeEnum;
import com.ruoyi.xq.service.SysPushLogService;
import com.ruoyi.xq.service.SysPushService;
import com.ruoyi.xq.service.UserService;
import com.ruoyi.xq.util.Date77Util;
import com.ruoyi.yunxin.Yunxin;
import com.ruoyi.yunxin.enums.YxImTypeEnum;
import com.ruoyi.yunxin.resp.YxCommonR;
import com.ruoyi.yunxin.resp.YxDataR;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 推送系统消息Service业务层处理
 *
 * @author 77
 * @date 2024-03-20
 */
@Service
@Slf4j
public class SysPushServiceImpl extends ServiceImpl<SysPushMapper,SysPush> implements SysPushService {

    @Autowired
    private UserService userService;
    @Autowired
    private Yunxin yunxin;
    @Autowired
    private SysPushLogService sysPushLogService;

    @Override
    public void handStart(Long id) {
        boolean update = this.update(Wrappers.lambdaUpdate(SysPush.class)
                .eq(SysPush::getId, id)
                .eq(SysPush::getStatus, SystemPushStatusEnum.NO_RUN.getCode())
                .set(SysPush::getStatus, SystemPushStatusEnum.RUNNING.getCode()));
        if(!update){
            throw new ServiceException("正在执行中，请稍等");
        }
//        SysPushNotifyDTO sysPush =  new SysPushNotifyDTO();
//        sysPush.setSysPushId(id);
//        amqpProducer.sendCommonMq(sysPush);
    }

    @Override
    public void fastStart(Long id){
        boolean update = this.update(Wrappers.lambdaUpdate(SysPush.class)
                .eq(SysPush::getId, id)
                .eq(SysPush::getStatus, SystemPushStatusEnum.NO_RUN.getCode())
                .set(SysPush::getStatus, SystemPushStatusEnum.RUNNING.getCode()));
        if(!update){
            throw new ServiceException("正在执行中，请稍等");
        }
        this.doRun(id);
    }

    @Override
    public void doRun(Long id){
        SysPush sysPush = this.getById(id);
        Integer status = sysPush.getStatus();
        if(SystemPushStatusEnum.COMPLETE.getCode().equals(status)){
            throw new ServiceException("不允许重复执行");
        }
        boolean update = this.update(Wrappers.lambdaUpdate(SysPush.class)
                .eq(SysPush::getId, sysPush.getId())
                .eq(SysPush::getStatus, SystemPushStatusEnum.RUNNING.getCode())
                .set(SysPush::getStatus, SystemPushStatusEnum.COMPLETE.getCode()));
        if(!update){
            throw new ServiceException("队列正在执行中");
        }
        SystemPushGroupIdEnum groupId = SystemPushGroupIdEnum.getByCode(sysPush.getGroupId());
        if(groupId == null){
            this.update(Wrappers.lambdaUpdate(SysPush.class)
                    .eq(SysPush::getId, sysPush.getId())
                    .set(SysPush::getStatus,SystemPushStatusEnum.FAIL.getCode()));
            log.error("未找到执行的人! sysPush={}",JSON.toJSONString(sysPush));
            return;
        }
        List<MinUserDTO> minUserList = new ArrayList<>();
        switch (groupId) {
            case ONE_USER:
                User user = userService.getById(sysPush.getUserId());
                if(user != null){
                    minUserList.add(new MinUserDTO(user.getId(),user.getUsercode(),user.getNickname()));
                }
                break;
            case MEN_USER:
                List<User> menUserList = userService.list(Wrappers.lambdaQuery(User.class)
                        .select(User::getId, User::getUsercode, User::getNickname)
                        .eq(User::getGender, UserGenderEnum.MAN.getCode())
                        .eq(User::getStatus, 0));
                minUserList = menUserList.stream().map(i -> new MinUserDTO(i.getId(), i.getUsercode(), i.getNickname())).collect(Collectors.toList());
                break;
            case WOMEN_USER:
                List<User> womenUserList = userService.list(Wrappers.lambdaQuery(User.class)
                        .select(User::getId, User::getUsercode, User::getNickname)
                        .eq(User::getGender, UserGenderEnum.WOMEN.getCode())
                        .eq(User::getStatus, 0));
                minUserList = womenUserList.stream().map(i -> new MinUserDTO(i.getId(), i.getUsercode(), i.getNickname())).collect(Collectors.toList());
                break;
            case ALL_USER:
                List<User> allUserList = userService.list(Wrappers.lambdaQuery(User.class)
                        .select(User::getId, User::getUsercode, User::getNickname)
                        .eq(User::getStatus, 0));
                minUserList = allUserList.stream().map(i -> new MinUserDTO(i.getId(), i.getUsercode(), i.getNickname())).collect(Collectors.toList());
                break;
            default:
                break;
        }
        if(minUserList.isEmpty()){
            this.update(Wrappers.lambdaUpdate(SysPush.class)
                    .eq(SysPush::getId, sysPush.getId())
                    .set(SysPush::getStatus,SystemPushStatusEnum.FAIL.getCode()));
            log.error("未找到发送的人! sysPush={}",JSON.toJSONString(sysPush));
            return;
        }
        List<List<MinUserDTO>> partition = Lists.partition(minUserList, 500);
        for (List<MinUserDTO> minUsers : partition) {
            SysPushLog sysPushLog = new SysPushLog();
            sysPushLog.setSysPushId(sysPush.getId());
            sysPushLog.setUserJson(JSON.toJSONString(minUsers));
            sysPushLog.setNum(minUsers.size());
            try {
                List<Long> userIds = minUsers.stream().map(MinUserDTO::getUserId).collect(Collectors.toList());
                SendSysPushResp resp = this.sendSysPush(sysPush, userIds);
                YxDataR<YxCommonR> imResp = resp.getImResp();
                if(imResp != null && imResp.isSuccess()){
                    sysPushLog.setStatus(SystemPushLogStatusEnum.SEND_SUCCESS.getCode());
                }else{
                    sysPushLog.setStatus(SystemPushLogStatusEnum.SEND_FAIL.getCode());
                }
                sysPushLog.setResult(JSON.toJSONString(imResp));
            }catch (Exception e){
                log.error("发送失败!",e);
                sysPushLog.setStatus(SystemPushLogStatusEnum.SEND_FAIL.getCode());
                sysPushLog.setResult(e.getMessage());
            }
            sysPushLogService.save(sysPushLog);
        }
        this.update(Wrappers.lambdaUpdate(SysPush.class)
                .eq(SysPush::getId, sysPush.getId())
                .set(SysPush::getStatus,SystemPushStatusEnum.COMPLETE.getCode()));
    }

    @Override
    public SendSysPushResp sendSysPush(SysPush sysPush, List<Long> userIds){
        Integer type = sysPush.getType();
        SystemPushTypeEnum systemPushType = SystemPushTypeEnum.getByCode(type);
        if(systemPushType == null){
            throw new ServiceException("消息类型有误！");
        }
        String content = sysPush.getContent();
        Object body;
        YxImTypeEnum yxImTypeEnum;
        switch (systemPushType){
            case SIMPLE_TEXT:
                body = JSON.parseObject(content, NoticeMsgR.class);
                yxImTypeEnum = YxImTypeEnum.TXT;
                break;
            case TEXT:
                NoticeR<ExtNoticeData> noticeR = JSON.parseObject(content, new TypeReference<NoticeR<ExtNoticeData>>() {});
                noticeR.getData().setCurrentDate();
                body = noticeR;
                yxImTypeEnum = YxImTypeEnum.CUSTOM;
                break;
            case SIMPLE_IMAGE_TEXT:
                NoticeR<SimpleImageTextData> simpleImageTextR = JSON.parseObject(content, new TypeReference<NoticeR<SimpleImageTextData>>() {});
                yxImTypeEnum = YxImTypeEnum.CUSTOM;
                SimpleImageTextData data = simpleImageTextR.getData();
                if(data.getImage() != null){
                    data.setImage(data.getImage());
                }
                body = simpleImageTextR;
                break;
            case IMAGE_TEXT:
                NoticeR<MultipleImageTextData> multipleImageTextR = JSON.parseObject(content, new TypeReference<NoticeR<MultipleImageTextData>>() {});
                MultipleImageTextData multipleImageTextData = multipleImageTextR.getData();
                List<MultipleImageTextNode> list = multipleImageTextData.getList();
                for (MultipleImageTextNode node : list) {
                    if(node.getImage() != null){
                        node.setImage(node.getImage());
                    }
                }
                body = multipleImageTextR;
                yxImTypeEnum = YxImTypeEnum.CUSTOM;
                break;
            case ONLY_IMAGE:
                NoticeOnlyImageR imageR = JSON.parseObject(content, NoticeOnlyImageR.class);
                if(imageR.getUrl() != null){
                    imageR.setUrl(imageR.getUrl());
                }
                body = imageR;
                yxImTypeEnum = YxImTypeEnum.IMAGE;
                break;
            default:
                throw new ServiceException("消息类型有误");
        }
        YxDataR<YxCommonR> r = yunxin.batchSendToNotice(userIds, body, null, yxImTypeEnum);
        SendSysPushResp resp = new SendSysPushResp();
        resp.setImResp(r);
        return resp;
    }

    @Override
    public void closeSysPush(Long id) {
        this.update(Wrappers.lambdaUpdate(SysPush.class)
                .eq(SysPush::getId,id)
                .eq(SysPush::getStatus,SystemPushStatusEnum.NO_RUN.getCode())
                .set(SysPush::getStatus,SystemPushStatusEnum.CLOSE.getCode()));
    }

    @Override
    public void saveSysPush(SysPushParams params) {
        SysPushMasterDTO master = params.getMaster();
        Integer type = master.getType();
        SystemPushTypeEnum systemPushType = SystemPushTypeEnum.getByCode(type);
        if(systemPushType == null){
            throw new ServiceException("消息类型有误！");
        }
        SystemPushGroupIdEnum groupIdEnum = SystemPushGroupIdEnum.getByCode(master.getGroupId());
        if(groupIdEnum == null){
            throw new ServiceException("发送类型有误！");
        }
        SysPush sysPush = new SysPush();
        if(groupIdEnum == SystemPushGroupIdEnum.ONE_USER){
            User user = userService.getByUsercode(master.getUsercode());
            if(user == null){
                throw new ServiceException("用户编号不存在！");
            }
            sysPush.setUserId(user.getId());
            sysPush.setUsercode(user.getUsercode());
        }
        SystemPushSendTimeTypeEnum systemPushSendTime = SystemPushSendTimeTypeEnum.getByCode(master.getSendTimeType());
        if(systemPushSendTime == null){
            throw new ServiceException("发送方式有误！");
        }
        sysPush.setSendTimeType(systemPushSendTime.getCode());
        if(systemPushSendTime == SystemPushSendTimeTypeEnum.SCHEDULED_SEND){
            long between = Date77Util.diff(master.getSendTime(), LocalDateTime.now());
            if(between < 0){
                throw new ServiceException("定时发送时间不能少于当前时间");
            }
            if(between < 60){
                throw new ServiceException("定时发送最低限制为1分钟以后");
            }
            sysPush.setSendTime(master.getSendTime());
        }
        sysPush.setGroupId(master.getGroupId());
        sysPush.setType(master.getType());
        sysPush.setTitle(master.getTitle());
        sysPush.setRemark(master.getRemark());
        sysPush.setStatus(SystemPushStatusEnum.NO_RUN.getCode());
        String context = null;
        switch (systemPushType){
            case SIMPLE_TEXT:
                NoticeMsgR ok = NoticeMsgR.ok(master.getOnlyText());
                context = JSON.toJSONString(ok);
                break;
            case TEXT:
                ExtNoticeData data = new ExtNoticeData();
                data.setTitle(master.getTitle());
                data.setTxt1(master.getTextText1());
                List<ExtNoticeData.MessageExtDataList> fields = BeanConvertUtil.convertListTo(params.getTextContext(), ExtNoticeData.MessageExtDataList::new);
                data.setFields(fields);
                data.setTxt2(master.getTextText2());
                data.setLink_type(master.getTextLinkType());
                data.setLink_url(master.getTextLinkUrl());
                NoticeR<ExtNoticeData> noticeR = NoticeR.ok(MessageBaseTypeEnum.TXT, data);
                context = JSON.toJSONString(noticeR);
                break;
            case SIMPLE_IMAGE_TEXT:
                SimpleImageTextData simpleImageTextData = new SimpleImageTextData();
                simpleImageTextData.setAct_type(master.getOneImageTitleType());
                if(simpleImageTextData.getAct_type() == 1){ // 水印标题
                    simpleImageTextData.setAct_string(master.getOneImageText());
                    simpleImageTextData.setTitle(master.getOneImageText());
                    simpleImageTextData.setTxt(master.getOneImageText());
                }else if(simpleImageTextData.getAct_type() == 2){ // 正常标题
                    simpleImageTextData.setAct_string(master.getOneImageText());
                    simpleImageTextData.setTitle(master.getOneImageText());
                    simpleImageTextData.setTxt(master.getOneImageText());
                }
                simpleImageTextData.setImage(master.getOneImageImage());
                simpleImageTextData.setLink_type(master.getOneImageLinkType());
                simpleImageTextData.setLink_url(master.getOneImageLinkUrl());
                NoticeR<SimpleImageTextData> simpleImageTextR = NoticeR.ok(MessageBaseTypeEnum.SIMPLE_IMAGE_TEXT, simpleImageTextData);
                context = JSON.toJSONString(simpleImageTextR);
                break;
            case IMAGE_TEXT:
                List<SysPushImageContextDTO> imageContext = params.getImageContext();
                List<MultipleImageTextNode> multipleImageTextNodeList = new ArrayList<>();
                for (SysPushImageContextDTO dto : imageContext) {
                    MultipleImageTextNode multipleImageTextNode = new MultipleImageTextNode();
                    multipleImageTextNode.setImage(dto.getImage());
                    multipleImageTextNode.setTitle(dto.getTitle());
                    multipleImageTextNode.setLink_type(dto.getLinkType());
                    multipleImageTextNode.setLink_url(dto.getLinkUrl());
                    multipleImageTextNodeList.add(multipleImageTextNode);
                }
                MultipleImageTextData multipleImageTextData = new MultipleImageTextData();
                multipleImageTextData.setList(multipleImageTextNodeList);
                NoticeR<MultipleImageTextData> multipleImageTextDataNoticeR = NoticeR.ok(MessageBaseTypeEnum.MULTIPLE_IMAGE_TEXT, multipleImageTextData);
                context = JSON.toJSONString(multipleImageTextDataNoticeR);
                break;
            case ONLY_IMAGE:
                NoticeOnlyImageR noticeOnlyImageR = new NoticeOnlyImageR();
                noticeOnlyImageR.setUrl(master.getOnlyImage());
                context = JSON.toJSONString(noticeOnlyImageR);
                break;
            default:
                break;
        }
        sysPush.setContent(context);
        sysPush.setParams(JSON.toJSONString(params));
        this.save(sysPush);
        try {
            // 小于 < 12小时
            long between = Date77Util.diff(sysPush.getSendTime(), LocalDateTime.now());
//            SysPushDelayDto dto = new SysPushDelayDto();
//            dto.setSysPushId(sysPush.getId());
//            amqpProducer.sendCommonDelayMq(dto,(int)between+2);
        }catch (Exception e){
            log.error("系统推送任务发送失败!",e);
        }
    }
}
