package com.xisc.pm.app.service.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.hzero.core.base.BaseConstants;
import org.hzero.mybatis.domian.Condition;
import org.hzero.mybatis.util.Sqls;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.xisc.boot.common.feign.pojo.MtUserInfo;
import com.xisc.boot.common.sys.UserClient;
import com.xisc.pm.api.dto.RuleParamDTO;
import com.xisc.pm.app.service.PmPdRuleSetService;
import com.xisc.pm.app.service.strategy.RuleExecStrategyFactory;
import com.xisc.pm.app.service.strategy.pdrule.RuleExecStrategy;
import com.xisc.pm.domain.entity.PmPdRuleItem;
import com.xisc.pm.domain.entity.PmPdRuleSet;
import com.xisc.pm.domain.repository.PmPdRuleItemRepository;
import com.xisc.pm.domain.repository.PmPdRuleSetRepository;

import io.choerodon.core.domain.Page;
import io.choerodon.core.exception.CommonException;
import io.choerodon.mybatis.pagehelper.PageHelper;
import io.choerodon.mybatis.pagehelper.domain.PageRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * 组板设计规则集(PmPdRuleSet)应用服务
 *
 * @author 纪文桃 wentao.ji01@hand-china.com
 * @since 2024-07-30 14:15:48
 */
@Slf4j
@Service
public class PmPdRuleSetServiceImpl implements PmPdRuleSetService {

    private PmPdRuleSetRepository pmPdRuleSetRepository;

    private PmPdRuleItemRepository pmPdRuleItemRepository;

    private UserClient userClient;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // 创建缓存，最大容量200，过期时间1分钟
    private final static Cache<String, String> CACHE = Caffeine.newBuilder()
            .maximumSize(200)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();
    
    private final static String RULE_SET_KEY_PREFIX = "pm.rule.set.key.";
    
    public PmPdRuleSetServiceImpl(PmPdRuleSetRepository pmPdRuleSetRepository, PmPdRuleItemRepository pmPdRuleItemRepository, UserClient userClient) {
        this.pmPdRuleSetRepository = pmPdRuleSetRepository;
        this.pmPdRuleItemRepository = pmPdRuleItemRepository;
        this.userClient = userClient;
    }

    @Override
    public Page<PmPdRuleSet> selectList(PageRequest pageRequest, PmPdRuleSet pmPdRuleSet) {
        return PageHelper.doPageAndSort(pageRequest, () -> pmPdRuleSetRepository.selectList(pmPdRuleSet));
    }

    @Override
    public List<PmPdRuleSet> queryList(PmPdRuleSet pmPdRuleSet) {
        List<PmPdRuleSet> resultList = new ArrayList<>();
        // 查询规则集数据
        List<PmPdRuleSet> pdRuleSetList = pmPdRuleSetRepository.selectByCondition(Condition.builder(PmPdRuleSet.class)
                .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_TENANT_ID, pmPdRuleSet.getTenantId()))
                .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_RULE_SET_NO, pmPdRuleSet.getRuleSetNo(), true))
                .build());
        // 查询规则库对应的描述、创建时间、创建人
        if (CollectionUtils.isNotEmpty(pdRuleSetList)) {
            // 获取规则号集合、用于查询规则库数据
            List<Long> ruleNoList = pdRuleSetList.stream().map(PmPdRuleSet::getRuleNo).distinct().collect(Collectors.toList());
            List<PmPdRuleItem> pmPdRuleItemList = pmPdRuleItemRepository.selectByCondition(Condition.builder(PmPdRuleItem.class)
                    .andWhere(Sqls.custom().andIn(PmPdRuleItem.FIELD_RULE_NO, ruleNoList))
                    .andWhere(Sqls.custom().andEqualTo(PmPdRuleItem.FIELD_TENANT_ID, pmPdRuleSet.getTenantId()))
                    .andWhere(Sqls.custom().andEqualTo(PmPdRuleItem.FIELD_RULE_DESC, pmPdRuleSet.getRuleDesc(), true))
                    .build());
            if (CollectionUtils.isNotEmpty(pmPdRuleItemList)) {
                // 转成map,赋值
                Map<Long, PmPdRuleItem> pmPdRuleItemMap = pmPdRuleItemList.stream().collect(Collectors.toMap(PmPdRuleItem::getRuleNo, Function.identity(), (v1, v2) -> v1));
                List<Long> userIdList = pmPdRuleItemList.stream().map(PmPdRuleItem::getCreatedBy).collect(Collectors.toList());
                Map<Long, MtUserInfo> mtUserInfoMap = userClient.userInfoBatchGet(pmPdRuleSet.getTenantId(), userIdList);
                for (PmPdRuleSet pdRuleSet : pdRuleSetList) {
                    if (pmPdRuleItemMap.containsKey(pdRuleSet.getRuleNo())) {
                        PmPdRuleItem pmPdRuleItem = pmPdRuleItemMap.get(pdRuleSet.getRuleNo());
                        // 规则描述
                        pdRuleSet.setRuleDesc(pmPdRuleItem.getRuleDesc());
                        // 规则创建人
                        pdRuleSet.setResponseId(pmPdRuleItem.getCreatedBy());
                        // 获取创建人名称
                        if (!mtUserInfoMap.isEmpty() && mtUserInfoMap.containsKey(pdRuleSet.getCreatedBy())) {
                            MtUserInfo mtUserInfo = mtUserInfoMap.get(pdRuleSet.getCreatedBy());
                            pdRuleSet.setCreatedByName(Strings.concat(mtUserInfo.getLoginName(), mtUserInfo.getRealName()));
                        }
                        // 规则创建时间
                        pdRuleSet.setResponseCreationDate(pmPdRuleItem.getCreationDate());
                        resultList.add(pdRuleSet);
                    }
                }
            }
        }
        return resultList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveData(Long tenantId, List<PmPdRuleSet> pmPdRuleSetList) {
        // 判断集合是否为空，为空则返回
        if (CollectionUtils.isEmpty(pmPdRuleSetList)) {
            return;
        }

        List<PmPdRuleSet> insertList = pmPdRuleSetList.stream().filter(line -> line.getRuleSetId() == null).collect(Collectors.toList());
        List<PmPdRuleSet> updateList = pmPdRuleSetList.stream().filter(line -> line.getRuleSetId() != null).collect(Collectors.toList());

        // 如果是更新
        if (CollectionUtils.isNotEmpty(updateList)) {
            // 获取规则集编号和主键集合，用于数据库查询
            List<Long> ruleSetIdList = updateList.stream().map(PmPdRuleSet::getRuleSetId).collect(Collectors.toList());
            List<String> ruleSetNoList = updateList.stream().map(PmPdRuleSet::getRuleSetNo).collect(Collectors.toList());
            List<PmPdRuleSet> pdRuleSetList = pmPdRuleSetRepository.selectByCondition(Condition.builder(PmPdRuleSet.class)
                    .andWhere(Sqls.custom().andIn(PmPdRuleSet.FIELD_RULE_SET_NO, ruleSetNoList))
                    .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_TENANT_ID, tenantId))
                    .andWhere(Sqls.custom().andNotIn(PmPdRuleSet.FIELD_RULE_SET_ID, ruleSetIdList))
                    .build());
            if (CollectionUtils.isNotEmpty(pdRuleSetList)) {
                Map<String, PmPdRuleSet> pmPdRuleSetMap = pdRuleSetList.stream().collect(Collectors.toMap(pmPdRuleSet -> pmPdRuleSet.getRuleSetNo() + "-" + pmPdRuleSet.getRuleNo(), Function.identity(), (v1, v2) -> v1));
                // 校验规则集+规则序号唯一性
                for (PmPdRuleSet pmPdRuleSet : updateList) {
                    CACHE.invalidate(RULE_SET_KEY_PREFIX + pmPdRuleSet.getRuleSetNo()); 
                    if (pmPdRuleSetMap.containsKey(pmPdRuleSet.getRuleSetNo() + "-" + pmPdRuleSet.getRuleNo())) {
                        throw new CommonException("pm.pd_rule_set", pmPdRuleSet.getRuleSetNo(), pmPdRuleSet.getRuleNo());
                    }
                }
            }
            // 批量更新
            pmPdRuleSetRepository.batchUpdateByPrimaryKeySelective(updateList);
        }

        // 如果是新增，那么查询各个规则集的序号最大值，给序号赋值
        if (CollectionUtils.isNotEmpty(insertList)) {
            // 获取规则集编号集合，用于数据库查询
            List<String> ruleSetNoList = insertList.stream().map(PmPdRuleSet::getRuleSetNo).collect(Collectors.toList());
            List<PmPdRuleSet> pdRuleSetList = pmPdRuleSetRepository.selectByCondition(Condition.builder(PmPdRuleSet.class)
                    .andWhere(Sqls.custom().andIn(PmPdRuleSet.FIELD_RULE_SET_NO, ruleSetNoList))
                    .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_TENANT_ID, tenantId))
                    .build());
            // 用于分组获取最大值序号
            Map<String, List<PmPdRuleSet>> pdRuleSetMap = new HashMap<>(pdRuleSetList.size());
            if (CollectionUtils.isNotEmpty(pdRuleSetList)) {
                pdRuleSetMap = pdRuleSetList.stream().collect(Collectors.groupingBy(PmPdRuleSet::getRuleSetNo));
                Map<String, PmPdRuleSet> pmPdRuleSetMap = pdRuleSetList.stream().collect(Collectors.toMap(pmPdRuleSet -> pmPdRuleSet.getRuleSetNo() + "-" + pmPdRuleSet.getRuleNo(), Function.identity(), (v1, v2) -> v1));
                // 校验规则集+规则序号唯一性
                for (PmPdRuleSet pmPdRuleSet : insertList) {
                    if (pmPdRuleSetMap.containsKey(pmPdRuleSet.getRuleSetNo() + "-" + pmPdRuleSet.getRuleNo())) {
                        throw new CommonException("pm.pd_rule_set", pmPdRuleSet.getRuleSetNo(), pmPdRuleSet.getRuleNo());
                    }
                }
            }
            // 给序号赋值
            for (PmPdRuleSet pmPdRuleSet : pmPdRuleSetList) {
                if (!pdRuleSetMap.isEmpty() && pdRuleSetMap.containsKey(pmPdRuleSet.getRuleSetNo())) {
                    pmPdRuleSet.setRuleSeqNo(pdRuleSetMap.get(pmPdRuleSet.getRuleSetNo()).size() + 1L);
                } else {
                    pmPdRuleSet.setRuleSeqNo(1L);
                }
            }
            // 批量新增
            pmPdRuleSetRepository.batchInsertSelective(insertList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteForUi(List<PmPdRuleSet> pmPdRuleSetList) {
        if (CollectionUtils.isEmpty(pmPdRuleSetList)) {
            return;
        }
        // 批量删除
        pmPdRuleSetRepository.batchDeleteByPrimaryKey(pmPdRuleSetList);

        // 重新排序规则集内的序号
        List<PmPdRuleSet> updatePmPdRuleSetList = new ArrayList<>();
        // 获取规则集编号集合
        List<String> ruleSetNoList = pmPdRuleSetList.stream().map(PmPdRuleSet::getRuleSetNo).collect(Collectors.toList());
        // 获取数据库该规则集内的数据
        List<PmPdRuleSet> ruleSetList = pmPdRuleSetRepository.selectByCondition(Condition.builder(PmPdRuleSet.class)
                .andWhere(Sqls.custom().andIn(PmPdRuleSet.FIELD_RULE_SET_NO, ruleSetNoList))
                .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_TENANT_ID, pmPdRuleSetList.get(0).getTenantId()))
                .build());
        if (CollectionUtils.isNotEmpty(ruleSetList)) {
            Map<String, List<PmPdRuleSet>> ruleSetMap = ruleSetList.stream().collect(Collectors.groupingBy(PmPdRuleSet::getRuleSetNo));
            for (Map.Entry<String, List<PmPdRuleSet>> entry : ruleSetMap.entrySet()) {
                CACHE.invalidate(RULE_SET_KEY_PREFIX + entry.getKey()); 
                List<PmPdRuleSet> sortValueList = entry.getValue().stream().sorted(Comparator.comparing(PmPdRuleSet::getRuleSeqNo)).collect(Collectors.toList());
                // 给序号重新排序
                for (int i = 0; i < sortValueList.size(); i++) {
                    PmPdRuleSet pmPdRuleSet = sortValueList.get(i);
                    pmPdRuleSet.setRuleSeqNo(i + 1L);
                    updatePmPdRuleSetList.add(pmPdRuleSet);
                }
            }
            // 批量更新序号
            pmPdRuleSetRepository.batchUpdateByPrimaryKeySelective(updatePmPdRuleSetList);
        }
    }

    @Override
    public boolean runRuleSet(RuleParamDTO ruleParam) {
        if (ruleParam == null || StringUtils.isBlank(ruleParam.getRuleSetNo())) {
            throw new CommonException("pm.pd_rule_set_001");
        }
        // 获取数据库该规则集内的数据
        List<PmPdRuleSet> ruleSetList = null;
        String vuale = CACHE.getIfPresent(RULE_SET_KEY_PREFIX + ruleParam.getRuleSetNo());
        if(StringUtils.isBlank(vuale)) {
            // 保证循环调用同一个规则集时，只从数据库查询一次，仅对当前ruleParam对象生效
            ruleSetList = pmPdRuleSetRepository.selectByCondition(Condition.builder(PmPdRuleSet.class)
                    .andWhere(Sqls.custom().andEqualTo(PmPdRuleSet.FIELD_RULE_SET_NO, ruleParam.getRuleSetNo())
                            .andEqualTo(PmPdRuleSet.FIELD_VALID_FLAG, BaseConstants.Flag.YES)
                            .andEqualTo(PmPdRuleSet.FIELD_RULE_NO, ruleParam.getRuleNo(), true)
                            .andEqualTo(PmPdRuleSet.FIELD_TENANT_ID, ruleParam.getTenantId())).orderByDesc(PmPdRuleSet.FIELD_RULE_SEQ_NO)
                    .build());
            try {
                CACHE.put(RULE_SET_KEY_PREFIX + ruleParam.getRuleSetNo(), objectMapper.writeValueAsString(ruleSetList));
            } catch (JsonProcessingException e) {
                log.error(e.getMessage() , e);
            }
        } else {
            try {
                CollectionType collectionType = objectMapper.getTypeFactory().constructCollectionType(List.class, PmPdRuleSet.class);
                ruleSetList = objectMapper.readValue(vuale, collectionType);
            } catch (JsonProcessingException e) {
                log.error(e.getMessage());
                throw new CommonException("pm.pd_rule_set_001");
            }
        }
        if (ruleSetList == null || ruleSetList.isEmpty()) {
            return true;
        }
        for (PmPdRuleSet pmPdRuleSet : ruleSetList) {
            if (ruleParam.getExcludeRuleNo().contains(pmPdRuleSet.getRuleNo())) {
                continue;
            }
            RuleExecStrategy ruleExecStrategy = RuleExecStrategyFactory.matchPdRuleStrategy(pmPdRuleSet.getRuleNo());
            if (ruleExecStrategy == null) {
                throw new CommonException("pm.pd_rule_set_002", pmPdRuleSet.getRuleNo());
            }
            try {
                boolean flag = ruleExecStrategy.execRule(ruleParam);
                if (!flag) {
                    return false;
                }
            } catch (CommonException e) {
                log.error(String.format("规则集执行不通过，%s, %s", pmPdRuleSet.getRuleNo() , e.getMessage()));
                throw e ;
            }
        }
        return true;
    }
}

