package cn.axzo.msg.center.inside.notices.config;

import cn.axzo.framework.domain.ServiceException;
import cn.axzo.msg.center.api.enums.MsgTempBizCategoryEnum;
import cn.axzo.msg.center.api.response.PendingMessageTemporarilyTypeRes;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
import cn.axzo.msg.center.service.template.response.MessageDetailStyle;
import com.alibaba.fastjson.JSON;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import static java.util.stream.Collectors.toList;

/**
 * @author cold_blade
 * @version 1.0
 * @description 待办消息的相关业务类配置
 * @date 2023/8/31
 */
@Setter
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "message.pending")
public class PendingMessageBizConfig {

    private Map<MsgTempBizCategoryEnum, List<Long>> category;

    private Map<MsgTempBizCategoryEnum, Integer> categoryOrder;

    private Map<MsgTempBizCategoryEnum, String> categoryIcon;

    private Map<MsgTempBizCategoryEnum, String> categoryJumpUrl;

    @Getter
    private Map<AppTerminalTypeEnum, List<Long>> msgGroupConfig;

    /**
     * 待办样式配置
     */
    private Map<String, DetailStyleInfo> name2DetailStyle;

    /**
     * 分页查询获取待办时, 需要返回分析信息的token
     */
    @Getter
    private String analysisToken = "123331";

    /**
     * 按bizCode获取待办时的, 最大数量
     */
    @Getter
    private int getLatestByBizCodeMaxSize = 500;

    /**
     * 设置待办隐藏的隐藏时间
     */
    @Getter
    private int pendingSetHideSeconds = 60;

    /**
     * 待办最大的接收人数量
     */
    @Getter
    private int pendingMaxReceivers = 500;

    /**
     * 发送待办时, 如果没有传bizCode, 需要自动生成bizCode的模版
     */
    @Getter
    private String genBizCodeTemplates = "";

    /**
     * 清理配置的天数之前的待办日志
     */
    @Getter
    private int expungeTodoLogSinceDaysAgo = 180;

    @Getter
    private boolean logExecSQL;

    @Getter
    private boolean formatExecSQL;

    @Getter
    private boolean logAdvanceTodoStateFail;

    @Getter
    private Set<String> dontConcatRouterParamsTemplateCodes;

    public boolean isConcatRouterParams(String templateCode) {
        return dontConcatRouterParamsTemplateCodes == null || !dontConcatRouterParamsTemplateCodes.contains(templateCode);
    }

    // !! msg-center MQ消息自产自消

    @Getter
    private long msgCenterMqSelfConsumeMaxExecMs = 20000L;

    // !! 老消息缓存

    /**
     * 是否开启老消息走否走缓存
     */
    @Getter
    private boolean oldMsgStatCacheOn = true;

    @Getter
    private int oldMsgStatCacheDataExpireHours = 16;

    @Getter
    private int oldMsgStateDataBackendUpdateBatchSize = 100;

    @Getter
    private boolean oldMsgOffline = true;

    // !! 待办分类统计缓存

    /**
     * 待办分类统计是否走缓存
     */
    @Getter
    private boolean nodeStatCacheOn = true;

    @Getter
    private int nodeStatCacheMaxRequestNodeCodeSize = 10;

    @Getter
    private int nodeStatCacheDataExpireHours = 48;

    public boolean determineOldMsgStatCacheOn() {
        return isOldMsgStatCacheOn();
    }

    public boolean hasMessageDetailStyle(String code) {
        return name2DetailStyle != null && name2DetailStyle.containsKey(code);
    }

    public Optional<MessageDetailStyle> findMessageDetailType(String code) {
        if (name2DetailStyle == null)
            return Optional.empty();
        DetailStyleInfo info = name2DetailStyle.get(code);
        if (info == null)
            return Optional.empty();
        return Optional.of(new MessageDetailStyle(
                code, info.getCategory(), info.getName(), info.getUrl()));
    }

    public Collection<MessageDetailStyle> getMessageDetailStyles() {
        if (name2DetailStyle == null)
            return Collections.emptyList();
        return name2DetailStyle.keySet().stream()
                .map(detailStyleInfo -> findMessageDetailType(detailStyleInfo)
                        .orElseThrow(() -> new ServiceException("should never happen!")))
                .collect(toList());
    }

    public List<MsgTempBizCategoryConfig> tempCategoriesConfig() {
        return Arrays.stream(MsgTempBizCategoryEnum.values())
                .filter(e -> category.containsKey(e))
                .map(this::build)
                .filter(MsgTempBizCategoryConfig::isValid)
                .collect(toList());
    }

    private MsgTempBizCategoryConfig build(MsgTempBizCategoryEnum category) {
        return MsgTempBizCategoryConfig.build(category, this.category.get(category),
                categoryOrder.get(category),
                categoryIcon.get(category),
                categoryJumpUrl.get(category));
    }

    public List<Long> fetchMessageGroupTreeNodeIds(AppTerminalTypeEnum appTerminalType) {
        return fetchMessageGroupTreeNodeIds(Collections.singletonList(appTerminalType));
    }

    public List<Long> fetchMessageGroupTreeNodeIds(List<AppTerminalTypeEnum> terminals) {
        Set<Long> nodeIds = new HashSet<>();
        for (AppTerminalTypeEnum terminal : terminals) {
            List<Long> ids = msgGroupConfig.getOrDefault(terminal, Collections.emptyList());
            nodeIds.addAll(ids);
        }
        return new ArrayList<>(nodeIds);
    }

    public Map<Long, AppTerminalTypeEnum> fetchNodeId2Terminal() {
        if (msgGroupConfig == null || msgGroupConfig.isEmpty())
            return Collections.emptyMap();
        Map<Long, AppTerminalTypeEnum> nodeId2Terminal = new HashMap<>();
        for (Map.Entry<AppTerminalTypeEnum, List<Long>> e : msgGroupConfig.entrySet()) {
            for (Long id : e.getValue()) {
                nodeId2Terminal.put(id, e.getKey());
            }
        }
        return nodeId2Terminal;
    }

    @Setter
    @Getter
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public static class MsgTempBizCategoryConfig implements Comparable<MsgTempBizCategoryConfig> {

        /**
         * 消息临时分类的枚举
         */
        private MsgTempBizCategoryEnum category;
        /**
         * 消息临时分类映射的关联关系id集合
         */
        private Set<Long> configRelationIds;
        /**
         * 消息临时分类页面展示顺序
         */
        private Integer sortOrder;
        /**
         * 图标地址
         */
        private String iconUrl;
        /**
         * 指定的跳转地址
         */
        private String jumpUrl;

        public static MsgTempBizCategoryConfig build(MsgTempBizCategoryEnum category, List<Long> configRelationIds,
                                                     Integer sortOrder, String iconUrl, String jumpUrl) {
            // 去掉首尾的无效空格
            jumpUrl = Optional.ofNullable(jumpUrl).map(String::trim).orElse(null);
            return new MsgTempBizCategoryConfig(category, new HashSet<>(configRelationIds), sortOrder, iconUrl, jumpUrl);
        }

        public PendingMessageTemporarilyTypeRes toTemporarilyType() {
            PendingMessageTemporarilyTypeRes type = new PendingMessageTemporarilyTypeRes();
            type.setCategoryCode(this.category.name());
            type.setCategoryDesc(this.category.getDesc());
            type.setIconUrl(this.iconUrl);
            return type;
        }

        public boolean isValid() {
            return CollectionUtils.isNotEmpty(configRelationIds)
                    && Objects.nonNull(sortOrder)
                    && StringUtils.isNotBlank(iconUrl);
        }

        public boolean match(Long relationId) {
            return isValid() && configRelationIds.contains(relationId);
        }

        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }

        @Override
        public int compareTo(@Nullable MsgTempBizCategoryConfig tgt) {
            if (Objects.isNull(tgt)) {
                // 默认自然排序.null对象做兼容
                return 1;
            }
            return sortOrder.compareTo(tgt.sortOrder);
        }
    }
}
