package cn.axzo.msg.center.inside.notices.service.impl;

import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.msg.center.api.enums.MsgStateEnum;
import cn.axzo.msg.center.api.enums.MsgTypeEnum;
import cn.axzo.msg.center.api.response.MessageDetailRes;
import cn.axzo.msg.center.common.utils.BeanConvertUtils;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import cn.axzo.msg.center.dal.MessageRecordDao;
import cn.axzo.msg.center.domain.dto.MessagePageQueryDTO;
import cn.axzo.msg.center.domain.dto.MessageRouterDTO;
import cn.axzo.msg.center.domain.dto.MsgModuleDTO;
import cn.axzo.msg.center.domain.entity.MessageRecord;
import cn.axzo.msg.center.domain.entity.MessageRouter;
import cn.axzo.msg.center.domain.enums.MsgRouteTypeEnum;
import cn.axzo.msg.center.domain.enums.NativeTypeEnum;
import cn.axzo.msg.center.domain.enums.YesNoEnum;
import cn.axzo.msg.center.inside.notices.config.MessageSystemConfig;
import cn.axzo.msg.center.inside.notices.service.MessageModuleService;
import cn.axzo.msg.center.inside.notices.service.MessageRouterService;
import cn.axzo.msg.center.inside.notices.service.RawMessageRecordService;
import cn.axzo.msg.center.inside.notices.utils.MessageRecordHelper;
import cn.azxo.framework.common.model.Page;
import com.alibaba.fastjson.JSON;
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.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
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;

/**
 * @author cold_blade
 * @date 2023/9/13
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RawMessageRecordServiceImpl implements RawMessageRecordService {

    private final MessageRecordDao messageRecordDao;
    private final MessageModuleService messageModuleService;
    private final MessageRouterService messageRouterService;
    private final MessageSystemConfig messageSystemConfig;

    @Override
    public void updatePersonMessageState(Long personId, Collection<MsgStateEnum> srcStates, MsgStateEnum tgtState,
                                         Long msgId) {
        AssertUtil.notNull(personId, "personId is required");
        AssertUtil.notEmpty(srcStates, "srcStates is required");
        AssertUtil.notNull(tgtState, "tgtState is required");
        AssertUtil.notNull(msgId, "msgId is required");

        messageRecordDao.lambdaUpdate()
                .set(MessageRecord::getState, tgtState)
                .in(MessageRecord::getState, srcStates)
                .eq(MessageRecord::getId, msgId)
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .update();
    }

    @Override
    public void updatePersonMessageState(Long personId, Collection<MsgStateEnum> srcStates, MsgStateEnum tgtState,
                                         MsgTypeEnum msgType, @Nullable Long identifyId,
                                         @Nullable Collection<Long> moduleIds) {
        AssertUtil.notNull(personId, "personId is required");
        AssertUtil.notEmpty(srcStates, "srcStates is required");
        AssertUtil.notNull(tgtState, "tgtState is required");

        messageRecordDao.lambdaUpdate()
                .set(MessageRecord::getState, tgtState)
                .eq(MessageRecord::getPersonId, personId)
                .in(MessageRecord::getState, srcStates)
                .eq(Objects.nonNull(identifyId), MessageRecord::getToId, identifyId)
                .in(CollectionUtils.isNotEmpty(moduleIds), MessageRecord::getModuleId, moduleIds)
                .eq(Objects.nonNull(msgType), MessageRecord::getType, msgType)
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .ge(MessageRecord::getCreateAt, getQueryFromTime())
                .update();
    }

    @Override
    public int countPersonMessage(Long personId, MsgTypeEnum msgType, @Nullable Long identifyId,
                                  @Nullable Collection<Long> moduleIds, @Nullable Collection<MsgStateEnum> states) {
        AssertUtil.notNull(personId, "personId is required");
        AssertUtil.notNull(msgType, "msgType is required");


        return messageRecordDao.lambdaQuery()
                .eq(MessageRecord::getPersonId, personId)
                .eq(MessageRecord::getType, msgType)
                .eq(Objects.nonNull(identifyId), MessageRecord::getToId, identifyId)
                .in(CollectionUtils.isNotEmpty(moduleIds), MessageRecord::getModuleId, moduleIds)
                .in(CollectionUtils.isNotEmpty(states), MessageRecord::getState, states)
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .ge(MessageRecord::getCreateAt, getQueryFromTime())
                .count();
    }

    @Override
    public Optional<MessageDetailRes> selectLatestOnePersonMessage(Long personId, MsgTypeEnum msgType, String routerType,
                                                                   @Nullable Long identifyId,
                                                                   @Nullable Collection<Long> moduleIds) {
        MessageRecord record = messageRecordDao.lambdaQuery()
                .eq(MessageRecord::getPersonId, personId)
                .eq(MessageRecord::getType, msgType)
                .eq(Objects.nonNull(identifyId), MessageRecord::getToId, identifyId)
                .in(CollectionUtils.isNotEmpty(moduleIds), MessageRecord::getModuleId, moduleIds)
                .in(MessageRecord::getState, MsgStateEnum.successfullyStats())
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .ge(MessageRecord::getCreateAt, getQueryFromTime())
                .orderByAsc(MessageRecord::getState)
                .orderByDesc(MessageRecord::getCreateAt)
                .last("LIMIT 1")
                .one();
        return Optional.ofNullable(record)
                .map(v -> {
                    Map<Long, MsgModuleDTO> moduleMap = listMessageModules(Lists.newArrayList(v));
                    if (!moduleMap.containsKey(v.getId())) {
                        return null;
                    }
                    MessageRouterDTO router = listRouters(Lists.newArrayList(v), routerType).get(v.getId());
                    return convert(v, router, moduleMap.get(v.getId()));
                });
    }

    @Override
    public Page<MessageDetailRes> pagePersonMessages(MessagePageQueryDTO request) {
        AssertUtil.notNull(request.getPersonId(), "personId is required");
        AssertUtil.notNull(request.getMsgType(), "msgType is required");

        List<MsgStateEnum> msgStates = CollectionUtils.isEmpty(request.getStates()) ? MsgStateEnum.successfullyStats()
                : request.getStates();
        IPage<MessageRecord> page = request.toPage();
        page = messageRecordDao.lambdaQuery()
                .eq(Objects.nonNull(request.getPersonId()), MessageRecord::getPersonId, request.getPersonId())
                .eq(MessageRecord::getType, request.getMsgType())
                .in(CollectionUtils.isNotEmpty(request.getModuleIds()), MessageRecord::getModuleId,
                        request.getModuleIds())
                .in(MessageRecord::getState, msgStates)
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .ge(MessageRecord::getCreateAt, getQueryFromTime())
                // 去掉将未读的消息排到前面的逻辑
//                .orderByAsc(MessageRecord::getState)
                .orderByDesc(MessageRecord::getCreateAt)
                .page(page);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            // 列表为空的场景
            return Page.toPage(page.getCurrent(), page.getSize(), page.getTotal(), Collections.emptyList());
        }
        List<MessageRecord> records = page.getRecords();
        // 查询消息对应的路由集
        Map<Long, MessageRouterDTO> routerMap = listRouters(records, request.getRouteType());
        Map<Long, MsgModuleDTO> moduleMap = listMessageModules(records);
        // 原始的数据库模型数据转化为页面展示的数据模型
        List<MessageDetailRes> msgRecordBriefs = page.getRecords().stream()
                .filter(e -> moduleMap.containsKey(e.getId()))
                .map(e -> convert(e, routerMap.get(e.getId()), moduleMap.get(e.getId())))
                .collect(Collectors.toList());
        return Page.toPage(page.getCurrent(), page.getSize(), page.getTotal(), msgRecordBriefs);

    }

    private Map<Long, MsgModuleDTO> listMessageModules(List<MessageRecord> msgRecords) {
        //获取relationId集合
        Set<Long> relationIds = msgRecords.stream().map(MessageRecord::getRelationId).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(relationIds)) {
            relationIds.remove(null);
            relationIds.remove(0L);
        }
        //获取oldTypeId集合
        Set<Integer> oldTypeIds = msgRecords.stream().map(MessageRecord::getOldTypeId).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(oldTypeIds)) {
            oldTypeIds.remove(null);
            oldTypeIds.remove(0);
        }
        if (CollectionUtils.isEmpty(relationIds) && CollectionUtils.isEmpty(oldTypeIds)) {
            return Collections.emptyMap();
        }
        //获取模板列表
        List<MsgModuleDTO> modules = messageModuleService.listModuleByRelationIdsOrTypeIds(oldTypeIds, relationIds);
        //新关联键分组
        Map<Long, List<MsgModuleDTO>> relationModuleMap = modules.stream()
                .filter(e -> Objects.nonNull(e.getRelationId()) && e.getRelationId() != 0)
                .collect(Collectors.groupingBy(MsgModuleDTO::getRelationId));
        //老关联键分组
        Map<Integer, List<MsgModuleDTO>> typeIdModuleMap = modules.stream()
                .filter(e -> Objects.nonNull(e.getTypeId()) && e.getTypeId() != 0)
                .collect(Collectors.groupingBy(MsgModuleDTO::getTypeId));
        return msgRecords.stream()
                .map(e -> selectModule(e, relationModuleMap, typeIdModuleMap))
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(MsgModuleDTO::getMsgId, Function.identity(), (pre, cur) -> cur));
    }

    private MsgModuleDTO selectModule(MessageRecord record, Map<Long, List<MsgModuleDTO>> relationModuleMap,
                                      Map<Integer, List<MsgModuleDTO>> typeIdModuleMap) {
        MsgModuleDTO module = null;
        // 优先使用relation关联的模板
        if (MessageRecordHelper.isRelationIdValid(record.getRelationId())) {
            module = relationModuleMap.get(record.getRelationId()).stream()
                    .findFirst().orElse(null);
        }
        if (Objects.isNull(module) && MessageRecordHelper.isOldTypeIdValid(record.getOldTypeId())) {
            module = typeIdModuleMap.get(record.getOldTypeId()).stream()
                    .findFirst().orElse(null);
        }
        Optional.ofNullable(module)
                .ifPresent(v -> v.setMsgId(record.getId()));
        return module;
    }

    private LocalDateTime getQueryFromTime() {
        return Optional.ofNullable(messageSystemConfig.getDataDivideDays())
                .filter(e -> e > 0)
                .map(e -> LocalDate.now().minusDays(e).atStartOfDay())
                .orElseGet(() -> LocalDate.now().minusYears(10).atStartOfDay());
    }

    private MessageDetailRes convert(MessageRecord msgRecord, MessageRouterDTO router, MsgModuleDTO module) {
        MessageDetailRes res = BeanConvertUtils.copyBean(msgRecord, MessageDetailRes.class);
        res.setMsgId(msgRecord.getId());
        res.setMsgTitle(msgRecord.getTitle());
        res.setMsgTimestamp(msgRecord.getCreateAt().getTime());
        if (Objects.nonNull(router)) {
            res.setRouteUrl(router.getUrl());
            res.setRouteType(router.getType());
            res.setRouterParam(msgRecord.getRouterParams());
        }
        res.setModuleName(module.getMsgModuleName());
        res.setModuleIcon(module.getMsgModuleIcon());
        return res;
    }

    private Map<Long, MessageRouterDTO> listRouters(Collection<MessageRecord> msgRecords, String routerType) {
        final Set<Long> relationIds = msgRecords.stream().map(MessageRecord::getRelationId).collect(Collectors.toSet());
        Map<Long, List<MessageRouter>> rawRouterMap = messageRouterService.listRoutersByRelationIds(relationIds).stream()
                .collect(Collectors.groupingBy(MessageRouter::getRelationId));
        final Map<Long, MessageRouterDTO> resRouterMap = new HashMap<>();
        msgRecords.forEach(e -> {
            List<MessageRouter> routers = rawRouterMap.get(e.getRelationId());
            MessageRouterDTO router = pickAndParseMessageRouter(routers, routerType, e.getRouterParams());
            resRouterMap.put(e.getId(), router);
        });
        return resRouterMap;
    }

    private MessageRouterDTO pickAndParseMessageRouter(List<MessageRouter> rawRouters, String systemType, String routerParam) {
        if (CollectionUtils.isEmpty(rawRouters)) {
            return null;
        }
        MsgRouteTypeEnum routeType = getSystemType(systemType);
        return pickAndParseMessageRouter(rawRouters, routeType, routerParam);
    }

    private MessageRouterDTO pickAndParseMessageRouter(List<MessageRouter> messageRouters,
                                                       MsgRouteTypeEnum msgRouteTypeEnum, String routerParam) {
        String rawRouterUrl = null;
        Integer routerType = null;
        if (messageRouters.size() == 1) {
            NativeTypeEnum nativeTypeEnum = NativeTypeEnum.getByCode(messageRouters.get(0).getRouterType().getCode());
            if (Objects.nonNull(nativeTypeEnum)) {
                rawRouterUrl = messageRouters.get(0).getRouterUrl();
                routerType = nativeTypeEnum.getMessage();
            }
        } else {
            //不止一条 获取当前系统对应路由
            List<MessageRouter> routers = messageRouters.stream()
                    .filter(k -> k.getRouterType().equals(msgRouteTypeEnum))
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(routers)) {
                NativeTypeEnum nativeTypeEnum = NativeTypeEnum.getByCode(routers.get(0).getRouterType().getCode());
                if (nativeTypeEnum != null) {
                    rawRouterUrl = routers.get(0).getRouterUrl();
                    routerType = nativeTypeEnum.getMessage();
                }
            } else {
                //优先取H5
                List<MessageRouter> routerH5 = messageRouters.stream()
                        .filter(k -> k.getRouterType().equals(MsgRouteTypeEnum.WEBVIEW))
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(routerH5)) {
                    NativeTypeEnum nativeTypeEnum = NativeTypeEnum.getByCode(routerH5.get(0).getRouterType().getCode());
                    if (nativeTypeEnum != null) {
                        rawRouterUrl = routerH5.get(0).getRouterUrl();
                        routerType = nativeTypeEnum.getMessage();
                    }
                }

                //再取小程序
                List<MessageRouter> routerUni = messageRouters.stream()
                        .filter(k -> k.getRouterType().equals(MsgRouteTypeEnum.MINI_PROGRAM)).collect(
                                Collectors.toList());
                if (CollectionUtils.isNotEmpty(routerUni)) {
                    NativeTypeEnum nativeTypeEnum = NativeTypeEnum.getByCode(routerUni.get(0).getRouterType().getCode());
                    if (nativeTypeEnum != null) {
                        rawRouterUrl = routerUni.get(0).getRouterUrl();
                        routerType = nativeTypeEnum.getMessage();
                    }
                }
            }
        }
        PlaceholderResolver resolver = PlaceholderResolver.getDefaultResolver();
        if (Objects.nonNull(routerType)) {
            rawRouterUrl = resolver.resolveByMap(rawRouterUrl,
                    isRouterParamValid(routerParam) ? JSON.parseObject(routerParam) : Collections.emptyMap());
        }
        return new MessageRouterDTO(rawRouterUrl, routerType);
    }

    private boolean isRouterParamValid(String routerParam) {
        return StringUtils.isNotEmpty(routerParam)
                && !"null".equals(routerParam);
    }

    private MsgRouteTypeEnum getSystemType(String systemType) {
        /*String systemType = ContextInfoHolder.get().getSystemAndDeviceInfo().getSystemType();*/
        //判断是否为手机端调用
        if (MsgRouteTypeEnum.IOS.getMessage().equals(systemType)) {
            return MsgRouteTypeEnum.IOS;
        }
        if (MsgRouteTypeEnum.ANDROID.getMessage().equals(systemType)) {
            return MsgRouteTypeEnum.ANDROID;
        }
        if (MsgRouteTypeEnum.WECHAT_MINI_PROGRAM.getMessage().equals(systemType)) {
            return MsgRouteTypeEnum.WECHAT_MINI_PROGRAM;
        }
        //都不是表明确定web端调用
        return MsgRouteTypeEnum.WEB;
    }
}
