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

import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.dal.MessageRouteButtonDao;
import cn.axzo.msg.center.dal.MessageRouteDetailDao;
import cn.axzo.msg.center.dal.MessageRouterConfigDao;
import cn.axzo.msg.center.domain.entity.MessageRouteButton;
import cn.axzo.msg.center.domain.entity.MessageRouteDetail;
import cn.axzo.msg.center.domain.entity.MessageRouterConfig;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO.MessageRouteButtonDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO.MessageRouteDetailDTO;
import cn.axzo.msg.center.message.service.MessageTemplateRouterService;
import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum;
import cn.axzo.msg.center.service.enums.RouterCategoryEnum;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
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;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @description
 *
 * @author cold_blade
 * @date 2023/9/28
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MessageTemplateRouterServiceImpl implements MessageTemplateRouterService {

    private final MessageRouteDetailDao messageRouteDetailDao;
    private final MessageRouteButtonDao messageRouteButtonDao;
    private final MessageRouterConfigDao messageRouterConfigDao;

    @Override
    public Optional<MessageTemplateRouterDTO> queryByTemplateCode(String templateCode) {
        if (StringUtils.isBlank(templateCode)) {
            log.info("templateCode is blank.");
        }
        MessageTemplateRouterDTO router = MessageTemplateRouterDTO.builder()
                .routeDetail(queryDetailRouter(templateCode))
                .routeButtons(listButtonRouters(templateCode))
                .build();
        return router.isValid() ? Optional.of(router) : Optional.empty();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdate(MessageTemplateRouterDTO router, boolean isUpdate, String templateCode) {
        if (Objects.isNull(router)) {
            log.info("template router is null.");
            return;
        }
        if (isUpdate) {
            // 更新的逻辑为删除旧的重新创建
            remove(templateCode);
        }
        if (!router.isValid()) {
            log.info("invalid message template router.");
            return;
        }
        saveDetailRouter(router.getRouteDetail());
        batchSaveButtonRouter(router.getRouteButtons());
    }

    @Override
    public Map<String, MessageTemplateRouterDTO> groupByTemplateCode(Collection<String> templateCodes) {
        if (CollectionUtils.isEmpty(templateCodes)) {
            log.info("templateCodes is empty.");
            return Collections.emptyMap();
        }
        Set<String> distinctTemplateCodes = Sets.newHashSet(templateCodes);
        Map<String, MessageRouteDetailDTO> detailRouters = listDetailRoutersGroupingByTemplateCode(distinctTemplateCodes);
        Map<String, List<MessageRouteButtonDTO>> buttonRouters = listButtonRoutersGroupingByTemplateCode(distinctTemplateCodes);
        return distinctTemplateCodes.stream()
                .filter(e -> detailRouters.containsKey(e) || buttonRouters.containsKey(e))
                .collect(Collectors.toMap(Function.identity(), e -> MessageTemplateRouterDTO.builder()
                        .routeDetail(detailRouters.get(e))
                        .routeButtons(buttonRouters.get(e))
                        .build())
                );
    }

    private MessageRouteDetailDTO queryDetailRouter(String templateCode) {
        MessageRouteDetail msgRouteDetail = selectRouteDetail(templateCode);
        if (Objects.isNull(msgRouteDetail)) {
            return null;
        }
        List<MessageRouterConfig> routerConfigs = listByRouterCode(msgRouteDetail.getTemplateCode());
        return MessageRouteDetailDTO.from(msgRouteDetail, routerConfigs);
    }

    private Map<String, MessageRouteDetailDTO> listDetailRoutersGroupingByTemplateCode(Collection<String> templateCodes) {
        Map<String, MessageRouteDetail> routeDetails = listRouteDetailsGroupingByTemplateCode(templateCodes);
        if (MapUtil.isEmpty(routeDetails)) {
            log.info("there is not any route detail. templateCodes:{}", templateCodes);
            return Collections.emptyMap();
        }
        Map<String, List<MessageRouterConfig>> routerConfigs = groupingByRouterCode(templateCodes);
        Map<String, MessageRouteDetailDTO> result = Maps.newHashMap();
        routeDetails.entrySet().stream()
                .filter(e -> routerConfigs.containsKey(e.getKey()))
                .forEach(e -> result.put(e.getKey(), MessageRouteDetailDTO.from(e.getValue(), routerConfigs.get(e.getKey()))));
        return result;
    }

    private List<MessageRouteButtonDTO> listButtonRouters(String templateCode) {
        List<MessageRouteButton> routeButtons = messageRouteButtonDao.lambdaQuery()
                .eq(MessageRouteButton::getTemplateCode, templateCode)
                .list();
        if (CollectionUtils.isEmpty(routeButtons)) {
            log.info("the is not any router button. templateCode:[{}]", templateCode);
            return Collections.emptyList();
        }
        List<String> btnCodes = routeButtons.stream().map(MessageRouteButton::getBtnCode).collect(Collectors.toList());
        Map<String, List<MessageRouterConfig>> btnRouterConfigs = groupingByRouterCode(btnCodes);
        return routeButtons.stream()
                .map(e -> MessageRouteButtonDTO.from(e, btnRouterConfigs.get(e.getBtnCode())))
                .collect(Collectors.toList());
    }

    private Map<String, List<MessageRouteButtonDTO>> listButtonRoutersGroupingByTemplateCode(
            Collection<String> templateCodes) {
        List<MessageRouteButton> routeButtons = messageRouteButtonDao.lambdaQuery()
                .in(MessageRouteButton::getTemplateCode, templateCodes)
                .list();
        if (CollectionUtils.isEmpty(routeButtons)) {
            log.info("the is not any router button. templateCodes:{}", templateCodes);
            return Collections.emptyMap();
        }
        Set<String> btnCodes = routeButtons.stream().map(MessageRouteButton::getBtnCode).collect(Collectors.toSet());
        Map<String, List<MessageRouterConfig>> btnRouterConfigs = groupingByRouterCode(btnCodes);
        return routeButtons.stream()
                .map(e -> MessageRouteButtonDTO.from(e, btnRouterConfigs.get(e.getBtnCode())))
                .collect(Collectors.groupingBy(MessageRouteButtonDTO::getTemplateCode));
    }

    private MessageRouteDetail selectRouteDetail(String templateCode) {
        List<MessageRouteDetail> msgRouteDetails = messageRouteDetailDao.lambdaQuery()
                .eq(MessageRouteDetail::getTemplateCode, templateCode)
                .orderByDesc(MessageRouteDetail::getId)
                .list();
        if (CollectionUtils.isEmpty(msgRouteDetails)) {
            return null;
        }
        if (msgRouteDetails.size() > 1) {
            log.warn("there is some dirty data in table:[message_route_detail]");
        }
        return msgRouteDetails.get(0);
    }

    private Map<String, MessageRouteDetail> listRouteDetailsGroupingByTemplateCode(Collection<String> templateCodes) {
        List<MessageRouteDetail> msgRouteDetails = messageRouteDetailDao.lambdaQuery()
                .in(MessageRouteDetail::getTemplateCode, templateCodes)
                .orderByDesc(MessageRouteDetail::getId)
                .list();
        if (CollectionUtils.isEmpty(msgRouteDetails)) {
            return Collections.emptyMap();
        }
        Map<String, List<MessageRouteDetail>> groupingBy = msgRouteDetails.stream()
                .collect(Collectors.groupingBy(MessageRouteDetail::getTemplateCode));
        Map<String, MessageRouteDetail> result = Maps.newHashMap();
        for (List<MessageRouteDetail> elem : groupingBy.values()) {
            MessageRouteDetail detail = elem.get(0);
            result.put(detail.getTemplateCode(), detail);
            if (msgRouteDetails.size() > 1) {
                log.warn("there is some dirty data in table:[message_route_detail]. templateCode:[{}]",
                        elem.get(0).getTemplateCode());
            }
        }
        return result;
    }

    private List<MessageRouterConfig> listByRouterCode(String routerCode) {
        if (StringUtils.isBlank(routerCode)) {
            log.info("routerCode is blank.");
            return Collections.emptyList();
        }
        return messageRouterConfigDao.lambdaQuery()
                .eq(MessageRouterConfig::getRouterCode, routerCode)
                .list();
    }

    private Map<String, List<MessageRouterConfig>> groupingByRouterCode(Collection<String> routerCodes) {
        if (CollectionUtils.isEmpty(routerCodes)) {
            log.info("routerCodes is empty.");
            return Collections.emptyMap();
        }
        return messageRouterConfigDao.lambdaQuery()
                .in(MessageRouterConfig::getRouterCode, routerCodes)
                .list().stream()
                .collect(Collectors.groupingBy(MessageRouterConfig::getRouterCode));
    }

    private void saveDetailRouter(MessageRouteDetailDTO detailRouter) {
        if (Objects.isNull(detailRouter)) {
            log.info("detailRouter is null.");
            return;
        }
        messageRouteDetailDao.save(detailRouter.fetchMessageRouteDetail());
        messageRouterConfigDao.saveBatch(detailRouter.fetchMessageRouterConfigs());
    }

    private void batchSaveButtonRouter(List<MessageRouteButtonDTO> buttonRouters) {
        if (CollectionUtils.isEmpty(buttonRouters)) {
            log.info("buttonRouters is empty.");
            return;
        }
        List<MessageRouteButtonWrapper> routeButtonWrappers = buttonRouters.stream()
                .filter(MessageRouteButtonDTO::isValid)
                .map(e -> MessageRouteButtonWrapper.of(e.fetchMessageRouteButton(), e))
                .collect(Collectors.toList());
        // 生成自定义按钮的唯一标识和顺序
        buildButtonCodeAndPriority(routeButtonWrappers);
        // 保存模板对应的按钮路由
        List<MessageRouteButton> routeButtons = routeButtonWrappers.stream()
                .map(MessageRouteButtonWrapper::getRouteButton)
                .collect(Collectors.toList());
        messageRouteButtonDao.saveBatch(routeButtons);
        List<MessageRouterConfig> routerConfigs = routeButtonWrappers.stream()
                .filter(e -> RouterCategoryEnum.JUMP.equals(e.getButtonRouter().getCategory()))
                .flatMap(this::routerConfigStream)
                .collect(Collectors.toList());
        messageRouterConfigDao.saveBatch(routerConfigs);
    }

    private void buildButtonCodeAndPriority(List<MessageRouteButtonWrapper> routeButtonWrappers) {
        for (int i = 0; i < routeButtonWrappers.size(); i++) {
            MessageRouteButton button = routeButtonWrappers.get(i).getRouteButton();
            if (RouterButtonSourceEnum.CUSTOM.equals(button.getSource())) {
                // 这里仅对自定义的按钮设置按钮标识
                button.setBtnCode(UUIDUtil.uuidString());
            }
            // 优先级默认从1开始
            button.setPriority(i + 1);
        }
    }

    private Stream<MessageRouterConfig> routerConfigStream(MessageRouteButtonWrapper routeButtonWrapper) {
        // 获取对应按钮的唯一标识
        String btnCode = routeButtonWrapper.getRouteButton().getBtnCode();
        // 转化为存储的数据模型
        List<MessageRouterConfig> routerConfigs = routeButtonWrapper.buttonRouter.fetchMessageRouterConfigs(btnCode);
        return routerConfigs.stream();
    }

    private void remove(String templateCode) {
        if (StringUtils.isBlank(templateCode)) {
            log.info("===remove=== templateCode is blank.");
            return;
        }
        Set<String> routerCodes = Sets.newHashSet();
        // 查询对应模板的路由按钮
        Set<String> btnCodes = messageRouteButtonDao.lambdaQuery()
                .eq(MessageRouteButton::getTemplateCode, templateCode)
                .select(MessageRouteButton::getBtnCode)
                .list().stream().map(MessageRouteButton::getBtnCode).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(btnCodes)) {
            // 逻辑删除该模板对应的路由按钮
            messageRouteButtonDao.lambdaUpdate().eq(MessageRouteButton::getTemplateCode, templateCode).remove();
            routerCodes.addAll(btnCodes);
        }
        // 逻辑删除业务详情路由
        boolean result = messageRouteDetailDao.lambdaUpdate()
                .eq(MessageRouteDetail::getTemplateCode, templateCode)
                .remove();
        if (result) {
            routerCodes.add(templateCode);
        }
        if (CollectionUtils.isEmpty(routerCodes)) {
            return;
        }
        // 逻辑删除该模板对应的路由按钮的URL相关配置
        messageRouterConfigDao.lambdaUpdate().in(MessageRouterConfig::getRouterCode, routerCodes).remove();
    }

    @Getter
    @AllArgsConstructor
    private static class MessageRouteButtonWrapper {
        private MessageRouteButton routeButton;
        private MessageRouteButtonDTO buttonRouter;

        static MessageRouteButtonWrapper of(MessageRouteButton routeButton, MessageRouteButtonDTO buttonRouter) {
            return new MessageRouteButtonWrapper(routeButton, buttonRouter);
        }
    }
}
