package cn.axzo.msg.center.message.service.impl;

import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.MessageBaseTemplateDao;
import cn.axzo.msg.center.dal.RelationTemplateMapDao;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.domain.entity.MessageRelation;
import cn.axzo.msg.center.domain.entity.MessageRouter;
import cn.axzo.msg.center.domain.entity.MessageTemplate;
import cn.axzo.msg.center.domain.entity.RelationTemplateMap;
import cn.axzo.msg.center.inside.notices.service.MessageRelationService;
import cn.axzo.msg.center.inside.notices.service.MessageRouterService;
import cn.axzo.msg.center.inside.notices.service.MessageTemplateService;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO.MessageRouteDetailDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO.MessageRouterConfigDTO;
import cn.axzo.msg.center.message.domain.param.RelationTemplateMapInitParam;
import cn.axzo.msg.center.message.service.MessageTemplateGroupService;
import cn.axzo.msg.center.message.service.MessageTemplateRouterService;
import cn.axzo.msg.center.message.service.RelationTemplateMapService;
import cn.axzo.msg.center.service.enums.BizDetailJumpStrategyEnum;
import cn.axzo.msg.center.service.enums.BizDetailShowStrategyEnum;
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
import cn.axzo.msg.center.service.enums.PushTerminalEnum;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.utils.JSONObjectUtil;
import cn.axzo.msg.center.utils.MessageRouterUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 新老模板映射关系service
 *
 * @author cold_blade
 * @date 2023/10/23
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RelationTemplateMapServiceImpl implements RelationTemplateMapService {

    private final MessageRouterService messageRouterService;
    private final MessageRelationService messageRelationService;
    private final MessageTemplateService messageTemplateService;
    private final RelationTemplateMapDao relationTemplateMapDao;
    private final MessageBaseTemplateDao messageBaseTemplateDao;
    private final MessageTemplateGroupService messageTemplateGroupService;
    private final MessageTemplateRouterService messageTemplateRouterService;

    @Override
    public Optional<String> queryByRelationId(Long relationId) {
        if (Objects.isNull(relationId)) {
            log.info("relationId is null");
            return Optional.empty();
        }
        return Optional.ofNullable(relationTemplateMapDao.lambdaQuery()
                .eq(RelationTemplateMap::getOriginalRelationId, relationId)
                .eq(RelationTemplateMap::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .one()
        ).map(RelationTemplateMap::getTemplateCode);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void mapRelationAndTemplate(Map<Long, String> map) {
        if (Objects.isNull(map) || map.isEmpty()) {
            return;
        }
        relationTemplateMapDao.lambdaUpdate()
                .in(RelationTemplateMap::getOriginalRelationId, map.keySet())
                .eq(RelationTemplateMap::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .remove();
        List<RelationTemplateMap> rows = map.entrySet().stream()
                .map(e -> {
                    RelationTemplateMap row = new RelationTemplateMap();
                    row.setOriginalRelationId(e.getKey());
                    row.setTemplateCode(e.getValue());
                    return row;
                }).collect(Collectors.toList());
        relationTemplateMapDao.saveBatch(rows);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void init(RelationTemplateMapInitParam param) {
        List<MessageRelation> allRelations = messageRelationService.getAllRelations();
        if (MapUtil.isNotEmpty(param.getRelationPushTerminalMap())) {
            // 指定了需要创建并关联的relationId
            allRelations = allRelations.stream()
                    .filter(e -> param.getRelationPushTerminalMap().containsKey(e.getId()))
                    .collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(allRelations)) {
            log.info("not found any valid relation id.");
            return;
        }
        // relationId与templateId的map
        Map<Long, Long> relationIdTemplateIdMap = allRelations.stream()
                .collect(Collectors.toMap(MessageRelation::getId, MessageRelation::getTemplateId));
        // 获取已经建立关联关系的relationId集合
        Set<Long> mappedRelationIds = getAllOriginalRelationIds();
        // 过滤掉已经建立关联关系的relationId
        relationIdTemplateIdMap = relationIdTemplateIdMap.entrySet().stream()
                .filter(e -> !mappedRelationIds.contains(e.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        if (MapUtil.isEmpty(relationIdTemplateIdMap)) {
            log.info("{} has been mapped.", param.getRelationPushTerminalMap().keySet());
            return;
        }
        // 获取生效中的模板
        Map<Long, MessageTemplate> messageTemplates = messageTemplateService.getAllTemplates().stream()
                .collect(Collectors.toMap(MessageTemplate::getId, Function.identity()));
        // 过滤掉relationId对应的消息模板已经删除的场景
        relationIdTemplateIdMap = relationIdTemplateIdMap.entrySet().stream()
                .filter(e -> messageTemplates.containsKey(e.getValue()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        // 获取真实有效且待建立关联关系的relationId对应的消息模板路由
        Map<Long, List<MessageRouter>> messageRouters = messageRouterService.getRouterMapByRelationIds(
                        Lists.newArrayList(relationIdTemplateIdMap.keySet())).stream()
                .collect(Collectors.groupingBy(MessageRouter::getRelationId));
        // 转化为新模板的数据模型wrapper
        List<CreateNewTemplateAndMapWrapper> wrappers = relationIdTemplateIdMap.entrySet().stream()
                .map(e -> convert(e.getKey(), messageTemplates.get(e.getValue()), messageRouters.get(e.getKey()),
                        param.getRelationPushTerminalMap()))
                .collect(Collectors.toList());
        // 批量插入
        messageBaseTemplateDao.saveBatch(wrappers.stream()
                .map(CreateNewTemplateAndMapWrapper::getBaseTemplate).collect(Collectors.toList()));
        // TODO: [cold_blade] P0
//        messageTemplateRouterService.batchInsert(wrappers.stream()
//                .filter(e -> CollectionUtils.isNotEmpty(e.getRouters()))
//                .flatMap(e -> e.getRouters().stream()).collect(Collectors.toList()));
        List<String> groupNodes = Lists.newArrayList(param.getGroupNodeCode());
        Map<String, List<String>> templateGroupNodeMap = wrappers.stream()
                .map(CreateNewTemplateAndMapWrapper::getBaseTemplate)
                .collect(Collectors.toMap(MessageBaseTemplate::getCode, e -> groupNodes));
        messageTemplateGroupService.templateGroup(templateGroupNodeMap);
        Map<Long, String> relationIdTemplateCodeMap = wrappers.stream()
                .collect(Collectors
                        .toMap(CreateNewTemplateAndMapWrapper::getRelationId, e -> e.getBaseTemplate().getCode()));
        mapRelationAndTemplate(relationIdTemplateCodeMap);
    }

    private CreateNewTemplateAndMapWrapper convert(Long relationId, MessageTemplate template, List<MessageRouter> routers,
                                                   Map<Long, List<PushTerminalEnum>> relationPushTerminalMap) {
        MessageBaseTemplate baseTemplate = convert(template);
        if (MapUtil.isNotEmpty(relationPushTerminalMap) && relationPushTerminalMap.containsKey(relationId)) {
            baseTemplate.setPushTerminal(JSONObjectUtil.toJSONString(relationPushTerminalMap.get(relationId)));
        }
        CreateNewTemplateAndMapWrapper wrapper = CreateNewTemplateAndMapWrapper.builder()
                .baseTemplate(baseTemplate)
                .relationId(relationId)
                .build();
        if (CollectionUtils.isNotEmpty(routers)) {
            wrapper.setMsgTemplateRouter(convert2MessageTemplateRouter(routers, baseTemplate.getCode()));
        }
        return wrapper;
    }

    private MessageTemplateRouterDTO convert2MessageTemplateRouter(List<MessageRouter> routers, String templateCode) {
        List<MessageRouterConfigDTO> routerConfigs = routers.stream()
                .map(e -> MessageRouterConfigDTO.builder()
                        .routerCode(templateCode)
                        .terminalType(TerminalTypeEnum.codeOf(e.getRouterType().getCode()))
                        .url(e.getRouterUrl())
                        .build())
                .collect(Collectors.toList());
        MessageRouteDetailDTO detailRouter = MessageRouteDetailDTO.builder()
                .name(MessageRouterUtil.DETAIL_ROUTER_DESC)
                .templateCode(templateCode)
                .showStrategy(BizDetailShowStrategyEnum.JUMP_TO)
                .jumpStrategy(BizDetailJumpStrategyEnum.DETAIL)
                .routerConfigs(routerConfigs)
                .build();
        return MessageTemplateRouterDTO.builder().routeDetail(detailRouter).build();
    }

    private MessageBaseTemplate convert(MessageTemplate srcTemplate) {
        MessageBaseTemplate template = new MessageBaseTemplate();
        template.setCode(UUIDUtil.uuidString());
        template.setName("template_" + srcTemplate.getId());
        template.setMsgCategory(MessageCategoryEnum.GENERAL_MESSAGE);
        template.setTitle(srcTemplate.getTitle());
        template.setContent(srcTemplate.getContent());
        template.setCardContent(JSONObjectUtil.toJSONString(null));
        template.setPushTerminal(JSONObjectUtil.toJSONString(PushTerminalEnum.values()));
        template.setCreatorId(0L);
        template.setUpdaterId(0L);
        return template;
    }

    private Set<Long> getAllOriginalRelationIds() {
        return relationTemplateMapDao.lambdaQuery()
                .select(RelationTemplateMap::getOriginalRelationId)
                .list().stream().map(RelationTemplateMap::getOriginalRelationId).collect(Collectors.toSet());
    }

    @Setter
    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    private static class CreateNewTemplateAndMapWrapper {
        private Long relationId;
        private MessageBaseTemplate baseTemplate;
        private MessageTemplateRouterDTO msgTemplateRouter;
    }
}
