package cn.axzo.msg.center.message.domain.dto;

import cn.axzo.basics.common.util.AssertUtil;
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.service.dto.MessageButtonRouteStrategyDTO;
import cn.axzo.msg.center.service.dto.MessageDetailRouteStrategyDTO;
import cn.axzo.msg.center.service.dto.MessageRouterTerminalDTO;
import cn.axzo.msg.center.service.enums.BizDetailJumpStrategyEnum;
import cn.axzo.msg.center.service.enums.BizDetailShowStrategyEnum;
import cn.axzo.msg.center.service.enums.PresetButtonType;
import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum;
import cn.axzo.msg.center.service.enums.RouterCategoryEnum;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.utils.MessageRouterUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author cold_blade
 * @date 2023/11/8
 * @version 1.0
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MessageTemplateRouterDTO implements Serializable {

    private static final long serialVersionUID = -4230859886203498149L;

    /**
     * 业务详情路由
     */
    private MessageRouteDetailDTO routeDetail;
    /**
     * 按钮路由列表
     */
    private List<MessageRouteButtonDTO> routeButtons;

    public boolean isValid() {
        return Objects.nonNull(routeDetail)
                || CollectionUtils.isNotEmpty(routeButtons);
    }

    public boolean isInValid() {
        return !isValid();
    }

    public Optional<String> fetchTemplateCode() {
        if (Objects.nonNull(routeDetail)) {
            return Optional.ofNullable(routeDetail.getTemplateCode());
        }
        if (CollectionUtils.isNotEmpty(routeButtons)) {
            return Optional.ofNullable(routeButtons.get(0).getTemplateCode());
        }
        return Optional.empty();
    }

    public MessageTemplateRouterDTO deepClone() {
        List<MessageRouteButtonDTO> buttons = CollectionUtils.isEmpty(this.routeButtons) ? Collections.emptyList()
                : this.routeButtons.stream().map(MessageRouteButtonDTO::deepClone).collect(Collectors.toList());
        return MessageTemplateRouterDTO.builder()
                .routeDetail(Optional.ofNullable(this.routeDetail).map(MessageRouteDetailDTO::deepClone).orElse(null))
                .routeButtons(buttons)
                .build();
    }

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

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class MessageRouteDetailDTO implements Serializable {

        private static final long serialVersionUID = -6332526731908737802L;

        /**
         * 显示名称
         */
        private String name;
        /**
         * 模板code
         */
        private String templateCode;
        /**
         * 业务详情展示策略
         */
        private BizDetailShowStrategyEnum showStrategy;
        /**
         * 业务详情跳转策略
         */
        private BizDetailJumpStrategyEnum jumpStrategy;
        /**
         * 跳转路由的配置列表
         */
        private List<MessageRouterConfigDTO> routerConfigs;

        public static MessageRouteDetailDTO from(MessageRouteDetail msgRouteDetail,
                                                 List<MessageRouterConfig> routerConfigs) {
            AssertUtil.notNull(msgRouteDetail, "msgRouteDetail can not be null");
            AssertUtil.notEmpty(routerConfigs, "routerConfigs can not be empty");

            return MessageRouteDetailDTO.builder()
                    .name(msgRouteDetail.getName())
                    .templateCode(msgRouteDetail.getTemplateCode())
                    .showStrategy(msgRouteDetail.getShowStrategy())
                    .jumpStrategy(msgRouteDetail.getJumpStrategy())
                    .routerConfigs(routerConfigs.stream().map(MessageRouterConfigDTO::from).collect(Collectors.toList()))
                    .build();
        }

        public static MessageRouteDetailDTO from(MessageDetailRouteStrategyDTO bizDetailShowStrategy, String templateCode) {
            AssertUtil.notNull(bizDetailShowStrategy, "btnRouter can not be null");
            AssertUtil.notEmpty(templateCode, "templateCode can not be empty");

            List<MessageRouterConfigDTO> configs = bizDetailShowStrategy.getTerminals().stream()
                    .map(e -> MessageRouterConfigDTO.builder().url(e.getUrl()).terminalType(e.getTerminalType()).build())
                    .collect(Collectors.toList());
            return MessageRouteDetailDTO.builder()
                    .name(MessageRouterUtil.DETAIL_ROUTER_DESC)
                    .templateCode(templateCode)
                    .showStrategy(bizDetailShowStrategy.getShowStrategy())
                    .jumpStrategy(bizDetailShowStrategy.getJumpStrategy())
                    .routerConfigs(configs)
                    .build();
        }

        public MessageRouteDetail fetchMessageRouteDetail() {
            return MessageRouteDetail.builder()
                    .name(this.name)
                    .showStrategy(this.showStrategy)
                    .jumpStrategy(this.jumpStrategy)
                    .templateCode(this.templateCode)
                    .build();
        }

        public List<MessageRouterConfig> fetchMessageRouterConfigs() {
            return CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList() :
                    this.routerConfigs.stream()
                            .map(e -> e.fetchMessageRouterConfig(this.templateCode))
                            .collect(Collectors.toList());
        }

        public MessageDetailRouteStrategyDTO toBizDetailShowStrategyDTO() {
            List<MessageRouterTerminalDTO> terminals = this.routerConfigs.stream()
                    .map(e -> MessageRouterTerminalDTO.builder().terminalType(e.terminalType).url(e.url).build())
                    .collect(Collectors.toList());
            return MessageDetailRouteStrategyDTO.builder().showStrategy(this.showStrategy).jumpStrategy(this.jumpStrategy).terminals(terminals).build();
        }

        public MessageRouteDetailDTO deepClone() {
            List<MessageRouterConfigDTO> configs = CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList()
                    : this.routerConfigs.stream().map(MessageRouterConfigDTO::deepClone).collect(Collectors.toList());
            return MessageRouteDetailDTO.builder()
                    .name(this.name)
                    .templateCode(this.templateCode)
                    .showStrategy(this.showStrategy)
                    .jumpStrategy(this.jumpStrategy)
                    .routerConfigs(configs)
                    .build();
        }

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

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class MessageRouteButtonDTO implements Serializable {

        private static final long serialVersionUID = -7544674409859277745L;

        /**
         * 按钮名称
         */
        private String name;
        /**
         * 按钮唯一标识
         */
        private String btnCode;
        /**
         * 预设按钮类型
         *  AGREE: 同意
         *  REJECT: 驳回
         */
        private PresetButtonType presetButtonType;
        /**
         * 模板code
         */
        private String templateCode;
        /**
         * 按钮来源
         */
        private RouterButtonSourceEnum source;
        /**
         * 路由类型
         */
        private RouterCategoryEnum category;
        /**
         * API地址,仅当按钮是接口调用类型时有值
         */
        private String apiUrl;
        /**
         * 跳转路由的配置列表
         */
        private List<MessageRouterConfigDTO> routerConfigs;
        /**
         * 按钮style配置
         */
        private JSONArray style;
        /**
         * 执行人可见：true 不可见：false
         */
        private Boolean executorShow;
        /**
         * 待办状态可见可见：true 不可见：false
         */
        private Boolean pendingShow;
        /**
         * 按钮优先级，数值越大优先级越低
         */
        private Integer priority;

        public static MessageRouteButtonDTO from(MessageRouteButton routeButton,
                                                 List<MessageRouterConfig> routerConfigs) {
            AssertUtil.notNull(routeButton, "msgRouteDetail can not be null");

            List<MessageRouterConfigDTO> configs = CollectionUtils.isEmpty(routerConfigs) ? Collections.emptyList() :
                    routerConfigs.stream().map(MessageRouterConfigDTO::from).collect(Collectors.toList());
            return MessageRouteButtonDTO.builder()
                    .name(routeButton.getName())
                    .btnCode(routeButton.getBtnCode())
                    .presetButtonType(routeButton.getPresetBtnCode())
                    .templateCode(routeButton.getTemplateCode())
                    .source(routeButton.getSource())
                    .category(routeButton.getCategory())
                    .apiUrl(routeButton.getApiUrl())
                    .style(routeButton.getStyle())
                    .executorShow(routeButton.getExecutorShow())
                    .pendingShow(routeButton.getPendingShow())
                    .priority(routeButton.getPriority())
                    .routerConfigs(configs)
                    .build();
        }

        public static MessageRouteButtonDTO from(MessageButtonRouteStrategyDTO btnRouter, String templateCode) {
            AssertUtil.notNull(btnRouter, "btnRouter can not be null");
            AssertUtil.notEmpty(templateCode, "templateCode can not be empty");

            JSONArray style = CollectionUtils.isEmpty(btnRouter.getStyle()) ? null
                    : JSON.parseArray(JSON.toJSONString(btnRouter.getStyle()));
            List<MessageRouterConfigDTO> routerConfigs = CollectionUtils.isEmpty(btnRouter.getTerminals()) ? null :
                    btnRouter.getTerminals().stream()
                            .map(e -> MessageRouterConfigDTO.builder().url(e.getUrl()).terminalType(e.getTerminalType()).build())
                            .collect(Collectors.toList());
            return MessageRouteButtonDTO.builder()
                    .name(btnRouter.getDesc())
                    .btnCode(btnRouter.getIdentityCode())
                    .presetButtonType(btnRouter.getPresetButtonType())
                    .templateCode(templateCode)
                    .source(btnRouter.getSource())
                    .category(btnRouter.getCategory())
                    .apiUrl(btnRouter.getApiUrl())
                    .routerConfigs(routerConfigs)
                    .style(style)
                    .executorShow(btnRouter.getExecutorShow())
                    .pendingShow(btnRouter.getPendingShow())
                    .build();
        }

        public MessageRouteButton fetchMessageRouteButton() {
            return MessageRouteButton.builder()
                    .name(this.name)
                    .btnCode(this.btnCode)
                    .presetBtnCode(this.presetButtonType)
                    .templateCode(this.templateCode)
                    .source(this.source)
                    .category(this.category)
                    .apiUrl(this.apiUrl)
                    .style(this.style)
                    .executorShow(this.executorShow)
                    .pendingShow(this.pendingShow)
                    .build();
        }

        public List<MessageRouterConfig> fetchMessageRouterConfigs(String routerCode) {
            return CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList() :
                    this.routerConfigs.stream()
                            .map(e -> e.fetchMessageRouterConfig(routerCode))
                            .collect(Collectors.toList());
        }

        /**
         * @see {@link MessageButtonRouteStrategyDTO#isValid()}
         * <p>要改2处都要记得改
         */
        public boolean isValid() {
            if (RouterCategoryEnum.ACTION.equals(this.category)) {
                return StringUtils.isNotBlank(apiUrl);
            }
            if (RouterCategoryEnum.PRESET_BUTTON.equals(category)) {
                return presetButtonType != null;
            }
            return CollectionUtils.isNotEmpty(routerConfigs);
        }

        public MessageButtonRouteStrategyDTO toMessageRouterButton() {
            List<MessageRouterTerminalDTO> terminals = CollectionUtils.isEmpty(this.routerConfigs) ?
                    Collections.emptyList() : this.routerConfigs.stream()
                    .map(e -> MessageRouterTerminalDTO.builder().terminalType(e.terminalType).url(e.url).build())
                    .collect(Collectors.toList());
            return MessageButtonRouteStrategyDTO.builder()
                    .desc(this.name)
                    .category(this.category)
                    .identityCode(this.btnCode)
                    .presetButtonType(this.presetButtonType)
                    .source(this.source)
                    .style(MessageRouterUtil.parseButtonStyle(this.style))
                    .executorShow(this.executorShow)
                    .pendingShow(this.pendingShow)
                    .apiUrl(this.apiUrl)
                    .terminals(terminals)
                    .build();
        }

        public MessageRouteButtonDTO deepClone() {
            List<MessageRouterConfigDTO> configs = CollectionUtils.isEmpty(this.routerConfigs) ? Collections.emptyList()
                    : this.routerConfigs.stream().map(MessageRouterConfigDTO::deepClone).collect(Collectors.toList());
            return MessageRouteButtonDTO.builder()
                    .name(this.name)
                    .btnCode(this.btnCode)
                    .presetButtonType(this.presetButtonType)
                    .templateCode(this.templateCode)
                    .source(this.source)
                    .category(this.category)
                    .apiUrl(this.apiUrl)
                    .style(Optional.ofNullable(this.style).map(JSONArray::new).orElse(null))
                    .executorShow(this.executorShow)
                    .pendingShow(this.pendingShow)
                    .priority(this.priority)
                    .routerConfigs(configs)
                    .build();
        }

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

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class MessageRouterConfigDTO implements Serializable {

        private static final long serialVersionUID = -303015602477458085L;

        /**
         * 路由编码
         */
        private String routerCode;
        /**
         * 路由的系统类型
         */
        private TerminalTypeEnum terminalType;
        /**
         * 路由地址
         */
        private String url;

        static MessageRouterConfigDTO from(MessageRouterConfig config) {
            return MessageRouterConfigDTO.builder()
                    .routerCode(config.getRouterCode())
                    .terminalType(config.getTerminalType())
                    .url(config.getUrl())
                    .build();
        }

        MessageRouterConfig fetchMessageRouterConfig(String routerCode) {
            return MessageRouterConfig.builder()
                    .routerCode(routerCode)
                    .terminalType(this.terminalType)
                    .url(this.url)
                    .build();
        }

        public MessageRouterConfigDTO deepClone() {
            return MessageRouterConfigDTO.builder()
                    .routerCode(this.routerCode)
                    .terminalType(this.terminalType)
                    .url(this.url)
                    .build();
        }

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