package cn.axzo.msg.center.message.send.processor;

import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.api.request.GeneralMessageReq;
import cn.axzo.msg.center.dal.CommonMessageRecordDao;
import cn.axzo.msg.center.domain.entity.CommonMessageRecord;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.domain.entity.MessageRecord;
import cn.axzo.msg.center.domain.enums.BizActionCategory;
import cn.axzo.msg.center.message.domain.dto.SendImMessageDTO;
import cn.axzo.msg.center.message.domain.param.GeneralMessagePushParam;
import cn.axzo.msg.center.message.service.BizEventMappingService;
import cn.axzo.msg.center.message.service.GeneralMessageService;
import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.GeneralMessageStateEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest;
import cn.axzo.msg.center.service.pending.response.PushMessageReturnDTO;
import cn.axzo.msg.center.utils.PersonIdentityUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author syl
 * @date 2023/12/18
 */
@Slf4j
@Component
public class NotificationProcessor implements ISendProcessor {

    @Autowired
    private GeneralMessageService generalMessageService;

    @Autowired
    private MessageSendTwiceRecordService messageSendTwiceRecordService;

    @Autowired
    private BizEventMappingService bizEventMappingService;

    @Autowired
    private CommonMessageRecordDao commonMessageRecordDao;

    @Override
    public void check(SendContext context) {
        AssertUtil.notNull(context.getMessageParam(), "通知消息的参数错误，请检查");
        AssertUtil.isTrue(CollUtil.isNotEmpty(context.getToIdMessageRecordMap()), "通知消息的人员信息不存在");
        AssertUtil.notNull(context.getReachConfig(), "触达配置不能为空");
        AssertUtil.isTrue(BizActionCategory.NOTIFICATION.getCode().equals(context.getReachConfig()
                .getCategory()), "消息动作不匹配");
        bizEventMappingService.checkTemplateCodeByBizEvent(context.getReachConfig());
    }

    @Override
    public void send(SendContext context) {
        // 检查
        this.check(context);
        log.info("NotificationProcessor start to async send im message. bizCode= [{}]"
                        + ", templateCode= [{}]", context.getBizEvent().getBizCode(),
                context.getReachConfig().getTemplateCode());
        // 发送
        this.doBatchSendMessage(context);
    }

    public void doBatchSendMessage(SendContext context) {
        GeneralMessageReq request = context.getMessageParam();
        Map<Long, MessageRecord> toIdMessageRecordMap = context.getToIdMessageRecordMap();
        String templateCode = context.getReachConfig().getTemplateCode();
        try {
            // 发送IM消息
            GeneralMessageSendRequest sendImMsgRequest = buildSendRequest(request, templateCode,
                    toIdMessageRecordMap.keySet());
            // IM消息的发送是基于personId+应用终端的(eg: 工人端/企业端)
            List<SendImMessageDTO> result = generalMessageService
                    .batchSendMessage(sendImMsgRequest);
            if (CollectionUtils.isEmpty(result)) {
                log.info("bizEvent there is not any person successfully send im message. bizCode:[{}]",
                        request.getRelationId());
                return;
            }
            // 记录发送了IM消息的旧消息
            recordSendImMessage(toIdMessageRecordMap, result, request.getToldIdPersonIdMap());
        } catch (Exception e) {
            log.warn("bizEvent broke out some exception while sending im message. bizCode:[{}]",
                    request.getRelationId(), e);
        }
    }

    private void recordSendImMessage(Map<Long, MessageRecord> toIdMessageRecordMap,
            List<SendImMessageDTO> sendImResult,
            Map<Long, Long> toIdPersonIdMap) {
        // 成功发送了IM消息的personId集合
        Set<Long> sucSendImMsgPersonIds = sendImResult.stream()
                .map(SendImMessageDTO::getPersonId).collect(Collectors.toSet());
        // 从入参中的toIdPersonIdMap中筛选发送成功的entry
        Map<Long, Long> subToIdPersonIdMap = toIdPersonIdMap.entrySet().stream()
                // 过滤掉personId维度IM消息发送失败的entry
                .filter(e -> sucSendImMsgPersonIds.contains(e.getValue()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        Map<Long, Long> msgRecordIdPersonIdMap = toIdMessageRecordMap.entrySet().stream()
                .filter(e -> subToIdPersonIdMap.containsKey(e.getKey()))
                .collect(Collectors
                        .toMap(e -> e.getValue().getId(), e -> subToIdPersonIdMap.get(e.getKey())));
        log.info("bizEvent record message that has been send im message. msgIds:{}",
                msgRecordIdPersonIdMap.keySet());
        // 双发记录
        messageSendTwiceRecordService.batchSave(msgRecordIdPersonIdMap);
    }


    private GeneralMessageSendRequest buildSendRequest(GeneralMessageReq request,
            String templateCode,
            Collection<Long> subReceiverIds) {
        IdentityTypeEnum identityType = PersonIdentityUtil.toIdentityType(request.getReceiveType());
        List<PersonDTO> receivers = subReceiverIds.stream()
                .filter(request.getToldIdPersonIdMap()::containsKey)
                .map(e -> PersonDTO.from(request.getToldIdPersonIdMap().get(e), e, identityType))
                .collect(Collectors.toList());
        OrganizationTypeEnum orgType =
                Objects.isNull(request.getTerminalType()) ? OrganizationTypeEnum.UNKNOWN :
                        OrganizationTypeEnum.valueOf(request.getTerminalType().name());
        return GeneralMessageSendRequest.builder()
                .templateCode(templateCode)
                .receiver(receivers)
                .orgType(orgType)
                .orgName(request.getTerminalName())
                .orgId(request.getTerminalId())
                .bizCode(Optional.ofNullable(request.getBizId()).map(String::valueOf).orElse(""))
                .routerParams(Optional.ofNullable(request.getRouterParams())
                        .map(v -> JSONObject.parseObject(JSON.toJSONString(v)))
                        .orElseGet(JSONObject::new))
                .bizExtParams(Optional.ofNullable(request.getMsgParams())
                        .map(v -> JSONObject.parseObject(JSON.toJSONString(v)))
                        .orElseGet(JSONObject::new))
                .build();
    }


    @Override
    public void checkReach(ReachContext context) {
        AssertUtil.notNull(context.getMessageParam(), "通知消息的参数错误，请检查");
        AssertUtil.notNull(context.getReachConfig(), "触达配置不能为空");
        AssertUtil.isTrue(BizActionCategory.NOTIFICATION.getCode().equals(context.getReachConfig()
                .getCategory()), "消息动作不匹配");
    }
    @Override
    public void reach(ReachContext context) {
        // 检查
        this.checkReach(context);
        log.info("NotificationProcessor new send im message. bizCode= [{}]"
                        + ", templateCode= [{}]", context.getBizEvent().getBizCode(),
                context.getReachConfig().getTemplateCode());


    }

    @Override
    public void save(ReachContext context) {
        this.checkReach(context);
        MessageBaseTemplate template = bizEventMappingService.checkTemplateCodeByBizEvent(context.getReachConfig());
        context.setTemplate(template);
        List<CommonMessageRecord> records = convertRecords(context);
        commonMessageRecordDao.saveBatch(records);
        List<PushMessageReturnDTO> msgReturnList = records.stream()
                .map(r -> PushMessageReturnDTO.builder().identityCode(r.getIdentityCode()).build())
                .collect(Collectors.toList());
        context.setMsgReturnList(msgReturnList);
    }

    private List<CommonMessageRecord> convertRecords(ReachContext context) {
        MessageBaseTemplate template = context.getTemplate();
        GeneralMessagePushParam messageParam = context.getMessageParam();
        return messageParam.getReceivers().stream().map(r -> {
                    CommonMessageRecord.CommonMessageRecordBuilder builder = CommonMessageRecord.builder()
                            .identityCode(UUIDUtil.uuidString());
                    if (Objects.nonNull(messageParam.getInitiator())) {
                        builder.senderPersonId(messageParam.getInitiator().getId());
                    }
                    builder.receiverPersonId(r.getId());
                    builder.bizEventCode(messageParam.getBizEventCode());
                    builder.templateCode(template.getCode());
                    builder.title(parseString(template.getTitle(), messageParam.getBizExtParams()));
                    builder.content(parseString(template.getContent(), messageParam.getBizExtParams()));
                    builder.orgType(messageParam.getOrgType());
                    builder.workspaceId(messageParam.getWorkspaceId());
                    builder.ouId(messageParam.getOuId());
                    builder.state(GeneralMessageStateEnum.HAS_BEEN_SENT);
                    builder.bizCode(messageParam.getBizCode());
                    builder.routerParams(messageParam.getRouterParams());
                    builder.bizExtParams(messageParam.getBizExtParams());
                    return builder.build();
                }).collect(Collectors.toList());

    }
}
