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

import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.common.enums.ServiceErrorCodeEnum;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.common.redis.RedisUtil;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.common.utils.PageHelperUtil;
import cn.axzo.msg.center.dal.MessageBaseTemplateDao;
import cn.axzo.msg.center.dal.MessageTemplateV3Dao;
import cn.axzo.msg.center.domain.entity.MessageBaseTemplate;
import cn.axzo.msg.center.domain.entity.MessageTemplateButtonV3;
import cn.axzo.msg.center.domain.entity.MessageTemplateGroupV3;
import cn.axzo.msg.center.domain.entity.MessageTemplateV3;
import cn.axzo.msg.center.inside.notices.config.DetailStyleInfo;
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO;
import cn.axzo.msg.center.message.domain.param.MessageTemplateV3SaveOrUpdateParam;
import cn.axzo.msg.center.message.service.CardStyleService;
import cn.axzo.msg.center.message.service.MessageGroupNodeService;
import cn.axzo.msg.center.message.service.MessageTemplateButtonV3Service;
import cn.axzo.msg.center.message.service.MessageTemplateGroupService;
import cn.axzo.msg.center.message.service.MessageTemplateGroupV3Service;
import cn.axzo.msg.center.message.service.MessageTemplateRouterService;
import cn.axzo.msg.center.message.service.MessageTemplateV3Service;
import cn.axzo.msg.center.message.service.group.GroupTemplateService;
import cn.axzo.msg.center.service.card.response.CardStyleDetailResponse;
import cn.axzo.msg.center.service.domain.card.StateImageConfig;
import cn.axzo.msg.center.service.dto.MessageBaseTemplateDTO;
import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO;
import cn.axzo.msg.center.service.dto.MessageTemplateButtonV3DTO;
import cn.axzo.msg.center.service.dto.MessageTemplateCardJumpV3DTO;
import cn.axzo.msg.center.service.dto.StateImageConfigDTO;
import cn.axzo.msg.center.service.enums.CardBizState;
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
import cn.axzo.msg.center.service.enums.PushTerminalEnum;
import cn.axzo.msg.center.service.enums.StatusEnum;
import cn.axzo.msg.center.service.enums.YesOrNo;
import cn.axzo.msg.center.service.pending.card.domain.CardElementConfig;
import cn.axzo.msg.center.service.template.request.MessageTemplateV3PageRequest;
import cn.axzo.msg.center.service.template.response.MessageDetailStyle;
import cn.axzo.msg.center.service.template.response.MessageTemplateGroupV3DTO;
import cn.axzo.msg.center.service.template.response.MessageTemplateV3DetailResponse;
import cn.axzo.msg.center.service.template.response.MessageTemplateV3PageResponse;
import cn.axzo.msg.center.utils.JSONObjectUtil;
import cn.axzo.msg.center.utils.MessageTemplateCategoryUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import cn.azxo.framework.common.model.Page;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
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.function.Function;
import java.util.stream.Collectors;

/**
 * @description
 *
 * @author cold_blade
 * @date 2023/10/5
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MessageTemplateV3ServiceImpl implements MessageTemplateV3Service {

    private static final String SAVE_TEMPLATE_MUTEX_KEY = "msg-center:template:save:%s";
    private static final long TRY_LOCK_TIMEOUT_MILLS = 1000;
    private static final int RETRY_CNT_MAX = 3;

    private static final int MAX_NUM_ONCE_QUERY = 1000;

    private final RedisUtil redisUtil;
    private final MessageBaseTemplateDao messageBaseTemplateDao;
    private final MessageGroupNodeService messageGroupNodeService;
    private final MessageTemplateGroupService messageTemplateGroupService;
    private final MessageTemplateRouterService messageTemplateRouterService;
    private final PendingMessageBizConfig pendingBizConfig;
    private final GroupTemplateService groupTemplateService;
    private final MessageTemplateV3Dao messageTemplateV3Dao;
    private final MessageTemplateGroupV3Service messageTemplateGroupV3Service;
    private final MessageTemplateButtonV3Service messageTemplateButtonV3Service;
    private final CardStyleService cardStyleService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createTemplate(MessageTemplateV3SaveOrUpdateParam param) {
        log.info("MessageTemplateV3ServiceImpl#createTemplate,param:{}", JSON.toJSONString(param));
        //校验
        param.checkCreate();
        //重新设置系统类型的btnCode
        param.rechangeBtnCode();

        //1 创建模板基础数据
        String templateCode = saveTemplate(param);
        //2 创建模板与分类的关联关系数据
        messageTemplateGroupV3Service.saveTemplateGroup(templateCode,param.getGroups());
        //3 保存按钮列表
        messageTemplateButtonV3Service.saveTemplateButton(templateCode, param.getButtons());
        //4 创建模板与分类的关联关系数据
        messageTemplateGroupService.templateGroup(templateCode, param.getLeafGroupNodes());
        return templateCode;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTemplate(MessageTemplateV3SaveOrUpdateParam param) {
        log.info("MessageTemplateV3ServiceImpl#updateTemplate,param:{}", JSON.toJSONString(param));
        //校验
        param.checkCreate();
        //重新设置系统类型的btnCode
        param.rechangeBtnCode();

        //1 更新模板基础数据
        updateMessageTemplate(param);
        //2 更新分组
        messageTemplateGroupV3Service.updateTemplateGroup(param.getTemplateCode(), param.getGroups());
        //3 更新按钮列表
        messageTemplateButtonV3Service.updateTemplateButton(param.getTemplateCode(), param.getButtons());
        //4 更新模板分类信息
        updateTemplateGroupRelation(param);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTemplate(MessageTemplateV3SaveOrUpdateParam param) {
        log.info("MessageTemplateV3ServiceImpl#deleteTemplate,param:{}", JSON.toJSONString(param));
        if (Objects.isNull(param) || StringUtils.isBlank(param.getTemplateCode())) {
            log.info("messageTemplateCode is blank");
            return;
        }
        messageTemplateV3Dao.lambdaUpdate()
                .eq(MessageTemplateV3::getCode, param.getTemplateCode())
                .eq(MessageTemplateV3::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .set(MessageTemplateV3::getDisplayOnList, YesOrNo.NO.getCode())
                .update();
    }

    @Override
    public MessageTemplateV3DetailResponse detail(String templateCode) {
        log.info("MessageTemplateV3ServiceImpl#deleteTemplate,templateCode:{}", templateCode);
        if (StringUtils.isBlank(templateCode)) {
            return null;
        }
        MessageTemplateV3 baseTemplate = messageTemplateV3Dao.lambdaQuery()
                .eq(MessageTemplateV3::getCode, templateCode)
                .eq(MessageTemplateV3::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .one();
        if (Objects.isNull(baseTemplate)) {
            log.info("there is not any template match the templateCode:[{}]", templateCode);
            return null;
        }
        // 卡片分组
        List<MessageTemplateGroupV3> templateGroupV3List = messageTemplateGroupV3Service.listMessageTemplateGroupByCode(templateCode);

        //按钮列表
        List<MessageTemplateButtonV3> buttonV3List = messageTemplateButtonV3Service.listMessageTemplateButtonByCode(templateCode);

        // 获取模板关联分类的path列表
        List<String> groupNodePaths = messageTemplateGroupService.listMessageTemplateGroupPaths(
                Lists.newArrayList(templateCode)).getOrDefault(templateCode, Collections.emptyList());

        return convert(baseTemplate, templateGroupV3List, buttonV3List, groupNodePaths);
    }

    @Override
    public void updateStatus(Long operatorId, String templateCode, StatusEnum status) {
        if (Objects.isNull(operatorId) || StringUtils.isBlank(templateCode) || Objects.isNull(status)) {
            log.info("the param is invalid. operatorId:[{}], code:[{}], status:[{}]", operatorId, templateCode, status);
            return;
        }
        messageTemplateV3Dao.lambdaUpdate()
                .eq(MessageTemplateV3::getCode, templateCode)
                .eq(MessageTemplateV3::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .set(MessageTemplateV3::getStatus, status)
                .set(MessageTemplateV3::getUpdatePersonId, operatorId)
                .update();
    }

    @Override
    public Optional<MessageTemplateDTO> queryEnableTemplateByCode(String msgTemplateCode) {
        if (StringUtils.isBlank(msgTemplateCode)) {
            log.info("msgTemplateCode is blank.");
            return Optional.empty();
        }
        MessageTemplateV3 msgBaseTemplate = messageTemplateV3Dao.lambdaQuery()
                .eq(MessageTemplateV3::getCode, msgTemplateCode)
                .eq(MessageTemplateV3::getIsDelete, 0)
                .eq(MessageTemplateV3::getStatus, StatusEnum.ENABLE)
                .one();
        if (Objects.isNull(msgBaseTemplate)) {
            log.info("not find the [{}] template.", msgTemplateCode);
            return Optional.empty();
        }
        // 获取模板的路由配置信息
        MessageTemplateRouterDTO msgTemplateRouter = messageTemplateRouterService.queryByTemplateCode(msgTemplateCode)
                .orElse(null);
        return Optional.of(MessageTemplateDTO.from(msgBaseTemplate, msgTemplateRouter));
    }

    @Override
    public List<MessageTemplateDTO> listByTemplateCodes(List<String> msgTemplateCodes) {
        if (CollectionUtils.isEmpty(msgTemplateCodes)) {
            log.info("msgTemplateCodes is empty.");
            return Collections.emptyList();
        }
        List<MessageTemplateV3> msgBaseTemplates = messageTemplateV3Dao.lambdaQuery()
                .in(MessageTemplateV3::getCode, msgTemplateCodes)
                .eq(MessageTemplateV3::getIsDelete, 0)
                .list();
        if (Objects.isNull(msgBaseTemplates)) {
            log.info("not find the [{}] templates.", msgTemplateCodes);
            return Collections.emptyList();
        }
        Map<String, MessageTemplateRouterDTO> templateRouterMap = messageTemplateRouterService
                .groupByTemplateCode(msgTemplateCodes);
        return msgBaseTemplates.stream()
                .map(e -> MessageTemplateDTO.from(e, templateRouterMap.get(e.getCode())))
                .collect(Collectors.toList());
    }

    @Override
    public Page<MessageTemplateV3PageResponse> page(MessageTemplateV3PageRequest request) {
        IPage<MessageTemplateV3> result = pageQueryBaseTemplate(request);
        if (Objects.isNull(result)
                || CollectionUtils.isEmpty(result.getRecords())) {
            return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize());
        }
        List<String> templateCodes = result.getRecords().stream()
                .map(MessageTemplateV3::getCode)
                .collect(Collectors.toList());
        Map<String, List<String>> groupNodePaths = messageTemplateGroupService
                .listMessageTemplateGroupPaths(templateCodes);
        Map<String, String> codeNameMap = messageGroupNodeService.groupNodeNamePaths(groupNodePaths.values().stream()
                .flatMap(Collection::stream).collect(Collectors.toList()));
        List<MessageTemplateV3PageResponse> records = result.getRecords().stream()
                .map(e -> convert(e, groupNodePaths, codeNameMap))
                .collect(Collectors.toList());
        return Page.toPage(request.getPage(), request.getPageSize(), result.getTotal(), records);
    }


    @Override
    public List<MessageTemplateV3PageResponse> listByCodes(Collection<String> templateCodes) {
        if (CollectionUtils.isEmpty(templateCodes)) {
            log.info("the templateCodes is empty.");
            return Collections.emptyList();
        }
        AssertUtil.isTrue(templateCodes.size() <= MAX_NUM_ONCE_QUERY, "the collection of templateCodes is too large");
        // 查询模板基础数据
        List<MessageTemplateV3> records = messageTemplateV3Dao.lambdaQuery()
                .in(MessageTemplateV3::getCode, templateCodes)
                .eq(MessageTemplateV3::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
        // 查询模板的分类结点编码path
        templateCodes = records.stream().map(MessageTemplateV3::getCode).collect(Collectors.toList());
        Map<String, List<String>> groupNodePaths = messageTemplateGroupService
                .listMessageTemplateGroupPaths(templateCodes);
        // 将模板分类结点编码path转化为分类名称path
        Map<String, String> codeNameMap = messageGroupNodeService.groupNodeNamePaths(groupNodePaths.values().stream()
                .flatMap(Collection::stream).collect(Collectors.toList()));
        // 转化为页面展示的数据模型
        return records.stream()
                .map(e -> convert(e, groupNodePaths, codeNameMap))
                .collect(Collectors.toList());
    }

    @Override
    public List<String> listTemplateCodesByIds(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            log.info("ids is empty.");
            return Collections.emptyList();
        }
        return messageBaseTemplateDao.lambdaQuery()
                .in(MessageBaseTemplate::getId, ids)
                .select(MessageBaseTemplate::getCode)
                .list().stream()
                .map(MessageBaseTemplate::getCode)
                .collect(Collectors.toList());
    }


    @Override
    public List<MessageDetailStyle> listTemplateDetailStyles() {
        return new ArrayList<>(pendingBizConfig.getMessageDetailStyles());
    }

    private String saveTemplate(MessageTemplateV3SaveOrUpdateParam param) {
        String templateCode = UUIDUtil.uuidString();
        MessageTemplateV3 template = convert(param);
        log.info("templateV3#saveTemplate,template:{}", JSON.toJSONString(template));
        boolean result = doSaveTemplate(template, templateCode);
        int retryCnt = 0;
        while (!result && retryCnt++ < RETRY_CNT_MAX) {
            // 默认重试{@cod RETRY_CNT_MAX}次，若{@code RETRY_CNT_MAX}次后依然失败就报错
            templateCode = UUIDUtil.uuidString();
            result = doSaveTemplate(template, templateCode);
        }
        AssertUtil.isTrue(result, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc());
        return templateCode;
    }

    public boolean doSaveTemplate(MessageTemplateV3 template, String templateCode) {
        String opKey = String.format(SAVE_TEMPLATE_MUTEX_KEY, templateCode);
        String requestId = UUIDUtil.uuidRawString();
        try {
            boolean lockResult = redisUtil.getLockOps().getLockUntilTimeout(opKey, requestId, TRY_LOCK_TIMEOUT_MILLS);
            AssertUtil.isTrue(lockResult, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc());
            if (isTemplateExist(templateCode)) {
                return false;
            }
            template.setCode(templateCode);
            log.info("templateV3#doSaveTemplate,template:{}", JSON.toJSONString(template));
            messageTemplateV3Dao.save(template);
            return true;
        } finally {
            redisUtil.getLockOps().releaseLock(opKey, requestId);
        }
    }

    private boolean isTemplateExist(String templateCode) {
        return messageBaseTemplateDao.lambdaQuery()
                .eq(MessageBaseTemplate::getCode, templateCode)
                .eq(MessageBaseTemplate::getIsDelete, 0)
                .count() > 0;
    }

    private MessageTemplateV3 convert(MessageTemplateV3SaveOrUpdateParam param) {
        MessageTemplateV3 template = new MessageTemplateV3();
        template.setName(param.getTemplateName());
        template.setMsgCategory(param.getMsgCategory());
        template.setTitle(param.getTitle());
        template.setContent(param.getContent());
        template.setDetailStyleCode(param.getDetailStyleCode());
        template.setIcon(param.getIcon());
        template.setPushTerminal(JSONObjectUtil.toJSONString(param.getPushTerminals()));
        template.setMinAppVersion(param.getMinAppVersion());
        template.setCreatePersonId(param.getCreatePersonId());
        template.setCreatePersonName(param.getCreatePersonName());
        template.setUpdatePersonId(param.getUpdatePersonId());
        template.setUpdatePersonName(param.getUpdatePersonName());
        template.setPushData(param.getPushData());
        if (Objects.nonNull(param) && Objects.nonNull(param.getCardJump())) {
            if (Objects.nonNull(param.getCardJump().getCardUrlOpenStrategy())) {
                template.setCardUrlOpenStrategy(param.getCardJump().getCardUrlOpenStrategy());
            }
            if (Objects.nonNull(param.getCardJump().getCardUrlConfig())) {
                template.setCardUrlConfig(param.getCardJump().getCardUrlConfig());
                if (template.getCardUrlConfig().getUrlConfig() != null) {
                    template.getCardUrlConfig().getUrlConfig().setFieldsNullIfUrlAbsent();
                }
            }
        }
        template.setStatus(param.getStatus());
        //推送终端/最低版本
        template.setAppVersionConfigs(param.getAppVersionConfigs());
        //消息通道:IM/待办
        template.setChannels(param.getChannels());
        //卡片样式编码
        template.setCardStyleCode(param.getCardStyleCode());
        //卡片样式扩展字段
        template.setCardExtFields(param.getCardExtFields());
        //配置状态戳
        template.setStateImageConfigs(param.getStateImageConfigs());
        return template;
    }

    private void updateMessageTemplate(MessageTemplateV3SaveOrUpdateParam param) {
        JSONObject pushData = param.getPushData();
        boolean updated = messageTemplateV3Dao.lambdaUpdate()
                .eq(MessageTemplateV3::getCode, param.getTemplateCode())
                .eq(MessageTemplateV3::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .set(StringUtils.isNotBlank(param.getTemplateName()),
                        MessageTemplateV3::getName, param.getTemplateName())
                .set(CollectionUtils.isNotEmpty(param.getPushTerminals()), MessageTemplateV3::getPushTerminal, JSON.toJSONString(param.getPushTerminals()))
                .set(StringUtils.isNotBlank(param.getTitle()), MessageTemplateV3::getTitle, param.getTitle())
//                .set(MessageBaseTemplate::getCardContent, JSONObjectUtil.toJSONString(param.getMsgCardContentItems()))
                .set(MessageTemplateV3::getContent, param.getContent())
                .set(Objects.nonNull(param.getCardJump()) && Objects.nonNull(param.getCardJump().getCardUrlOpenStrategy())
                        , MessageTemplateV3::getCardUrlOpenStrategy, param.getCardJump().getCardUrlOpenStrategy())
                .set(Objects.nonNull(param.getCardJump()) && Objects.nonNull(param.getCardJump().getCardUrlConfig())
                        , MessageTemplateV3::getCardUrlConfig, JSON.toJSONString(param.getCardJump().getCardUrlConfig()))
                .set(StringUtils.isNotBlank(param.getDetailStyleCode()),
                        MessageTemplateV3::getDetailStyleCode, param.getDetailStyleCode())
                .set(MessageTemplateV3::getUpdatePersonId, param.getOperatorId())
                .set(MessageTemplateV3::getUpdatePersonName, param.getOperatorName())
                .set(MessageTemplateV3::getIcon, param.getIcon())
                .set(StringUtils.isNotBlank(param.getMinAppVersion()), MessageTemplateV3::getMinAppVersion, param.getMinAppVersion())
                .set(Objects.nonNull(pushData), MessageTemplateV3::getPushData,
                        pushData == null ? null : pushData.toJSONString())
                //推送终端/最低版本
                .set(MessageTemplateV3::getAppVersionConfigs, JSON.toJSONString(param.getAppVersionConfigs()))
                //消息通道:IM/待办
                .set( MessageTemplateV3::getChannels, JSON.toJSONString(param.getChannels()))
                //卡片样式编码
                .set(StringUtils.isNotBlank(param.getCardStyleCode()), MessageTemplateV3::getCardStyleCode, param.getCardStyleCode())
                //卡片样式扩展字段
                .set( MessageTemplateV3::getCardExtFields, JSON.toJSONString(param.getCardExtFields()))
                //配置状态戳
                .set( MessageTemplateV3::getStateImageConfigs, JSON.toJSONString(param.getStateImageConfigs()))
                .update();
        BizAssertions.assertTrue(updated, "模版不存在:", param.getTemplateCode());
    }

    private void updateTemplateGroupRelation(MessageTemplateV3SaveOrUpdateParam param) {
        if (CollectionUtils.isEmpty(param.getLeafGroupNodes())) {
            messageTemplateGroupService.deleteTemplateGroup(param.getTemplateCode());
            return;
        }
        messageTemplateGroupService.updateTemplateGroup(param.getTemplateCode(), param.getLeafGroupNodes());
    }

    private IPage<MessageTemplateV3> pageQueryBaseTemplate(MessageTemplateV3PageRequest request) {
        List<String> templateCodes = Lists.newArrayList();
        if (StringUtils.isNotBlank(request.getGroupNodeCode())) {
            templateCodes = groupTemplateService.collectTemplateCodes(request.getGroupNodeCode());
            if (CollectionUtils.isEmpty(templateCodes)) {
                // 入参中的分类没有关联任何模板,直接返回查询结果
                return null;
            }
        }
        if (StringUtils.isNotBlank(request.getTemplateCode())) {
            if (CollectionUtils.isNotEmpty(templateCodes)
                    && !templateCodes.contains(request.getTemplateCode())) {
                // 分页查询的入参中没指定的模板编码与分类映射的模板编码无交集
                return null;
            }
            // 取两者的交集
            templateCodes = Lists.newArrayList(request.getTemplateCode());
        }
        IPage<MessageTemplateV3> pageRequest = request.toPage();
        String title = request.getTitle();
        if (title != null) {
            title = title.trim();
        }
        String content = request.getContent();
        if (content != null) {
            content = content.trim();
        }

        return messageTemplateV3Dao.lambdaQuery()
                .like(StringUtils.isNotBlank(title), MessageTemplateV3::getTitle, title)
                .like(StringUtils.isNotBlank(content), MessageTemplateV3::getContent, content)
                .apply(BooleanUtils.isFalse(request.pushSwitchOn), "(json_extract(push_data, '$.switchOn') is null or json_extract(push_data, '$.switchOn') = false)")
                .apply(BooleanUtils.isTrue(request.pushSwitchOn), "json_extract(push_data, '$.switchOn') = true")
                .like(StringUtils.isNotBlank(request.getTemplateName()),
                        MessageTemplateV3::getName, request.getTemplateName())
                .in(CollectionUtils.isNotEmpty(templateCodes), MessageTemplateV3::getCode, templateCodes)
                .in(CollectionUtils.isNotEmpty(request.getCategories()), MessageTemplateV3::getMsgCategory,
                        request.getCategories())
                .eq(Objects.nonNull(request.getStatus()), MessageTemplateV3::getStatus, request.getStatus())
                .eq(MessageTemplateV3::getIsDelete, 0)
                .eq(MessageTemplateV3::getDisplayOnList, YesOrNo.YES.getCode())
                .orderByDesc(MessageTemplateV3::getCreateAt)
                .page(pageRequest);
    }

    private MessageBaseTemplateDTO convert(MessageBaseTemplate record) {
        return MessageBaseTemplateDTO.builder()
                .name(record.getName())
                .code(record.getCode())
                .msgCategory(record.getMsgCategory())
                .title(record.getTitle())
                .content(record.getContent())
                .cardContentItems(JSONObjectUtil.parseArray(record.getCardContent(), MessageCardContentItemDTO.class))
                .icon(record.getIcon())
                .pushTerminals(JSON.parseArray(record.getPushTerminal(), PushTerminalEnum.class))
                .createTimestamp(record.getCreateAt().getTime())
                .build();
    }

    private MessageTemplateV3PageResponse convert(MessageTemplateV3 record, Map<String, List<String>> groupNodePaths,
                                                Map<String, String> codeNameMap) {
        MessageDetailStyle styleType = pendingBizConfig
                .findMessageDetailType(record.getDetailStyleCode())
                .orElse(null);
        MessageTemplateV3PageResponse response = new MessageTemplateV3PageResponse();
        if (styleType != null) {
            response.setStyleType(styleType);
        } else if (record.getMsgCategory() == MessageCategoryEnum.BIZ_PENDING_MESSAGE) {
            response.setStyleType(new MessageDetailStyle(
                    DetailStyleInfo.BIZ_COMMON_CODE, MessageCategoryEnum.BIZ_PENDING_MESSAGE, "业务待办能用", null));
        } else if (record.getMsgCategory() == MessageCategoryEnum.APPROVAL_PENDING_MESSAGE) {
            response.setStyleType(new MessageDetailStyle(
                    DetailStyleInfo.FLOW_COMMON_CODE, MessageCategoryEnum.APPROVAL_PENDING_MESSAGE, "审批待办能用", null));
        }
        response.setTemplateName(record.getName());
        response.setTemplateCode(record.getCode());
        response.setTitle(record.getTitle());
        response.setContent(record.getContent());
        response.setCategory(record.getMsgCategory());
        response.setStatus(record.getStatus());
        List<String> nodeNamePaths = groupNodePaths.getOrDefault(record.getCode(), Collections.emptyList()).stream()
                .filter(codeNameMap::containsKey)
                .map(codeNameMap::get)
                .collect(Collectors.toList());
        response.setGroupNodeNamePaths(nodeNamePaths);
        response.setCreatePersonId(record.getCreatePersonId());
        response.setCreatePersonName(record.getCreatePersonName());
        response.setUpdatePersonId(record.getUpdatePersonId());
        response.setUpdatePersonName(record.getUpdatePersonName());
        response.setPushData(record.getPushData());
        return response;
    }

    private MessageTemplateV3DetailResponse convert(MessageTemplateV3 record, List<MessageTemplateGroupV3> messageTemplateGroups,
                                                    List<MessageTemplateButtonV3> buttonV3List, List<String> groupNodePaths) {
        List<String> groupNodeCodes = Lists.newArrayList();
        if (CollUtil.isNotEmpty(groupNodePaths)) {
            // 将path解析中解析出分类结点（路径叶结点）的编码
            groupNodeCodes = groupNodePaths.stream()
                    .map(GroupTreeNodePathDTO::parseLeafNodeCode)
                    .collect(Collectors.toList());
        }
        MessageDetailStyle styleType = pendingBizConfig
                .findMessageDetailType(record.getDetailStyleCode())
                .orElse(null);
        MessageTemplateCardJumpV3DTO cardJumpV3DTO = MessageTemplateCardJumpV3DTO.builder().build();
        if (Objects.nonNull(record)) {
            cardJumpV3DTO = MessageTemplateCardJumpV3DTO.builder().cardUrlConfig(record.getCardUrlConfig()).cardUrlOpenStrategy(record.getCardUrlOpenStrategy()).build();
        }

        List<MessageTemplateButtonV3DTO> buttons = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(buttonV3List)) {
            buttons = buttonV3List.stream().map(item -> {
                MessageTemplateButtonV3DTO messageTemplateButtonV3DTO = BeanUtil.copyProperties(item, MessageTemplateButtonV3DTO.class);
                messageTemplateButtonV3DTO.setIdentityCode(item.getCode());
                messageTemplateButtonV3DTO.setBtnCode(item.getCode());
                return messageTemplateButtonV3DTO;
            }).collect(Collectors.toList());
        }

        return MessageTemplateV3DetailResponse.builder()
                .templateCode(record.getCode())
                .templateName(record.getName())
                .detailStyle(styleType)
                .category(MessageTemplateCategoryUtil.parentCategory(record.getMsgCategory()))
                .subCategory(MessageTemplateCategoryUtil.parseAndReturnSubCategory(record.getMsgCategory()))
                .msgCateGory(record.getMsgCategory())
                .leafGroupNodes(groupNodeCodes)
                .pushTerminals(JSON.parseArray(record.getPushTerminal(), PushTerminalEnum.class))
                .msgTitle(record.getTitle())
                .msgContent(record.getContent())
                .groups(CollectionUtils.isNotEmpty(messageTemplateGroups) ? BeanUtil.copyToList(messageTemplateGroups, MessageTemplateGroupV3DTO.class) : Lists.newArrayList())
                .buttons(buttons)
                .cardJump(cardJumpV3DTO)
                .msgIcon(record.getIcon())
                .minAppVersion(record.getMinAppVersion())
                .createTimestamp(record.getCreateAt().getTime())
                .updateTimestamp(record.getUpdateAt().getTime())
                .createPersonId(record.getCreatePersonId())
                .createPersonName(record.getCreatePersonName())
                .updatePersonId(record.getUpdatePersonId())
                .updatePersonName(record.getUpdatePersonName())
                .pushData(record.getPushData())
                //推送终端/最低版本
                .appVersionConfigs(record.getAppVersionConfigs())
                //消息通道:IM/待办
                .channels(record.getChannels())
                //卡片样式编码
                .cardStyleCode(record.getCardStyleCode())
                //卡片样式扩展字段
                .cardExtFields(this.buildCardExtFields(record))
                //配置状态戳
                .stateImageConfigs(this.buildStateImageConfigs(record))
                .build();
    }

    /**
     * 构建模版中卡片扩展字段
     * 1 从[卡片样式]表中取全量的[卡片扩展集合]
     * 2 从[消息模版]中取保存的[卡片扩展集合]
     * 3 1/2中交集,则以[消息模版]为准;否则取[卡片样式]，并且enable=false
     * @param record 消息模版
     * @return
     */
    private List<CardElementConfig> buildCardExtFields(MessageTemplateV3 record) {
        List<CardElementConfig> cardElementConfigs = (Objects.nonNull(record) && CollectionUtils.isNotEmpty(record.getCardExtFields())) ? record.getCardExtFields() : Lists.newArrayList();
        Map<String, CardElementConfig> configMap = cardElementConfigs.stream().collect(Collectors.toMap(CardElementConfig::getCode, Function.identity()));
        if (StringUtils.isBlank(record.getCardStyleCode())) {
            return cardElementConfigs;
        }
        CardStyleDetailResponse detail = cardStyleService.detail(record.getCardStyleCode());
        if (Objects.isNull(detail) || CollectionUtils.isEmpty(detail.getElementConfigs())) {
            return cardElementConfigs;
        }
        List<CardElementConfig> configList = detail.getElementConfigs().stream().map(item -> {
            String code = item.getCode();
            CardElementConfig cardElementConfig = configMap.get(code);
            if (Objects.nonNull(cardElementConfig)) {
                configMap.remove(code);
                return cardElementConfig;
            }
            item.setEnabled(false);
            return item;
        }).collect(Collectors.toList());

        configList.addAll(configMap.values());
        return configList;
    }

    /**
     * 构建配置状态戳
     * 1 从[卡片样式]表中取全量的[卡片扩展集合]
     * 2 从[消息模版]中取保存的[卡片扩展集合]
     * 3 1/2中交集,则以[消息模版]为准;否则取[卡片样式]，并且enable=false
     * @param record 消息模版
     * @return
     */
    private List<StateImageConfigDTO> buildStateImageConfigs(MessageTemplateV3 record) {
        List<StateImageConfig> stateImageConfigs = (Objects.nonNull(record) && CollectionUtils.isNotEmpty(record.getStateImageConfigs())) ? record.getStateImageConfigs() : Lists.newArrayList();

        Map<String,StateImageConfigDTO> configDTOMap = stateImageConfigs.stream().map(item -> {
            CardBizState bizState = item.determineBizState();
            return StateImageConfigDTO.builder()
                    .stateCode(item.getStateCode())
                    .description(bizState == null ? null : bizState.getDescription())
                    .enabled(item.isEnabled())
                    .imageUrl(item.getImageUrl())
                    .build();
        }).collect(Collectors.toMap(StateImageConfigDTO::getStateCode, Function.identity()));

        List<CardBizState> cardStates = CardBizState.imageConfigurableStates();

        List<StateImageConfigDTO> configDTOList = cardStates.stream().map(item -> {
            StateImageConfigDTO stateImageConfigDTO = configDTOMap.get(item.name());
            if (Objects.nonNull(stateImageConfigDTO)) {
                configDTOMap.remove(item.name());
                return stateImageConfigDTO;
            }
            return StateImageConfigDTO.builder()
                    .stateCode(item.name())
                    .description(item.getDescription())
                    .enabled(false)
                    .imageUrl(item.getDefaultImageUrl())
                    .build();
        }).collect(Collectors.toList());

         configDTOList.addAll(configDTOMap.values());
         return configDTOList;
    }
}
