package com.hys.app.service.datasync.push.common;

import cn.hutool.core.util.StrUtil;
import com.hys.app.converter.datasync.MessagePushConverter;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.util.DateUtil;
import com.hys.app.framework.util.JsonUtil;
import com.hys.app.framework.util.StringUtil;
import com.hys.app.mapper.datasync.MessagePushMapper;
import com.hys.app.model.datasync.dos.MessagePushDO;
import com.hys.app.model.datasync.dto.MessagePushQueryParams;
import com.hys.app.model.base.Result;
import com.hys.app.model.datasync.enums.MessagePushStatusEnum;
import com.hys.app.model.datasync.enums.TargetSystemEnum;
import com.hys.app.model.datasync.vo.MessagePushVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 消息推送业务层实现
 *
 * @author 张崧
 * @since 2023-12-18 16:24:10
 */
@Service
@Slf4j
public class MessagePushManagerImpl extends BaseServiceImpl<MessagePushMapper, MessagePushDO> implements MessagePushManager {

    @Autowired
    private MessagePushConverter converter;

    @Autowired
    private MessagePushAsync messagePushAsync;

    @Autowired(required = false)
    private List<MessagePushStrategy> targetSystemStrategyList;

    private Map<TargetSystemEnum, MessagePushStrategy> targetSystemStrategyMap;

    @PostConstruct
    private void init() {
        targetSystemStrategyMap = targetSystemStrategyList == null ? Collections.emptyMap() :
                targetSystemStrategyList.stream().collect(Collectors.toMap(MessagePushStrategy::targetSystem, Function.identity()));
    }

    @Override
    public WebPage<MessagePushVO> list(MessagePushQueryParams queryParams) {
        WebPage<MessagePushDO> webPage = baseMapper.selectPage(queryParams);
        return converter.convert(webPage);
    }

    @Override
    public MessagePushVO getDetail(Long id) {
        return converter.convert(getById(id));
    }

    @Override
    public void convertAndPush(TargetSystemEnum targetSystem, String businessType, Object content, String summary) {
        // 先同步入库
        MessagePushDO messagePushDO = new MessagePushDO();
        messagePushDO.setTargetSystem(targetSystem);
        messagePushDO.setBusinessType(businessType);
        messagePushDO.setSummary(summary);
        // 如果是String类型不进行json转换，否则转换后会多出双引号
        messagePushDO.setContent(content instanceof String ? (String) content : JsonUtil.objectToJson(content));
        messagePushDO.setProduceTime(DateUtil.getDateline());
        messagePushDO.setStatus(MessagePushStatusEnum.Wait);
        messagePushDO.setFailCount(0);
        super.save(messagePushDO);

        // 再异步推送
        messagePushAsync.push(messagePushDO);
    }

    @Override
    public void scanFailedAndRetry() {
        Long lastId = 0L;
        while (true) {
            // 查询待推送的消息且失败次数小于5次的，每次查询100条
            List<MessagePushDO> list = baseMapper.selectWaitPushList(lastId, 100, 5);
            if (list.isEmpty()) {
                break;
            }
            lastId = list.get(list.size() - 1).getId();

            // 推送消息
            for (MessagePushDO messagePushDO : list) {
                this.pushSync(messagePushDO);
            }
        }
    }

    @Override
    public void pushSync(MessagePushDO messagePushDO) {
        MessagePushStrategy messagePushStrategy = targetSystemStrategyMap.get(messagePushDO.getTargetSystem());
        if (messagePushStrategy == null) {
            throw new ServiceException(StrUtil.format("未找到类型为：{}的推送策略", messagePushDO.getTargetSystem()));
        }

        try {
            Result pushResult = messagePushStrategy.push(messagePushDO);
            MessagePushStatusEnum status = pushResult.getSuccess() ? MessagePushStatusEnum.Success : MessagePushStatusEnum.Fail;
            baseMapper.updateStatus(messagePushDO.getId(), status, pushResult.getRemark());
        } catch (Exception e) {
            // 进入这里有可能是网络超时等原因
            log.error(StrUtil.format("消息推送失败，消息类型：{}，消息id：{}", messagePushDO.getBusinessType(), messagePushDO.getId()), e);
            baseMapper.updateStatus(messagePushDO.getId(), MessagePushStatusEnum.Fail, StringUtil.formatException(e));
        }
    }

}

