package com.infynova.udi.service.helper;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.infynova.udi.entity.MatchData;
import com.infynova.udi.entity.MatchTemp;
import com.infynova.udi.enums.match.MatchStatusEnum;
import com.infynova.udi.enums.match.MatchUpdateStatusEnum;
import com.infynova.udi.mapper.MatchDataMapper;
import com.infynova.udi.mapper.MatchTempMapper;
import com.infynova.udi.mapper.UdiMapper;
import com.infynova.udi.vo.TaskVo;
import com.infynova.udi.vo.UdiMatchRateVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @author liudong
 * 2024/3/5 13:44
 * @version 1.0
 */
@Slf4j
@Component("matchHelper")
public class MatchHelper {

    @Resource
    private MatchDataMapper matchDataMapper;

    @Resource
    private MatchTempMapper matchTempMapper;

    @Resource
    private UdiMapper udiMapper;

    @Resource
    private TaskHelper taskHelper;

    @Resource
    private UdiLock udiLock;

    @Resource
    private MatchESHelper matchESHelper;

    private static final BigDecimal ONE_HUNDRED_RATE = new BigDecimal("100");

    private String getKey(String key){
        return "match:" + key;
    }

    /**
     * 对码匹配
     */
    public void tranMatchRate(){

        Set<Long> taskIdSet = matchDataMapper.queryMatchWaitingTask();

        if(CollectionUtils.isEmpty(taskIdSet)){
            log.info("对码匹配-没有需要对码匹配的数据");
        }

        for (Long taskId : taskIdSet) {
            // 加锁机制
            String key = this.getKey(String.valueOf(taskId));
            Boolean hasKey = udiLock.hasKey(key);
            if(hasKey){
                log.info("对码匹配-taskId:{},对码已在运行中;",key);
                continue;
            }else{
                try {
                    log.info("对码匹配-taskId:{},上锁;",key);
                    udiLock.lock(key,1440L);
                    // 通知任务修改任务状态
                    taskHelper.taskMatchStatusStart(taskId);
                    // 对码核心逻辑
                    this.taskMatch(taskId);
                    // 结束
                    taskHelper.taskMatchStatusFinish(taskId);
                } finally {
                    log.info("对码匹配-taskId:{},解锁;",key);
                    udiLock.del(key);
                }
            }

        }

    }

    /**
     * 对码核心逻辑
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void taskMatch(Long taskId){
        // 每一次转换最大数量100
        int pageNo = 1;
        int oneMaxNum = 100;

        TaskVo taskVo = taskHelper.getTaskVo(taskId);
        log.info("对码匹配-taskNo:{}",taskVo.getTask().getTaskNo());

        while (true){
            // 查询未开始的数据
            LambdaQueryWrapper<MatchData> queryWrapper = new LambdaQueryWrapper<>();
            Page<MatchData> page = new Page<>(pageNo, oneMaxNum);
            queryWrapper.eq(MatchData::getMatchStatus, MatchStatusEnum.WAITING.getCode());
            queryWrapper.eq(MatchData::getTaskId, taskId);
            Page<MatchData> dataPage = matchDataMapper.selectPage(page, queryWrapper);
            if(CollectionUtils.isEmpty(dataPage.getRecords())){
                break;
            }
            for (MatchData matchData : dataPage.getRecords()) {
                this.match(taskId,matchData);
            }
            pageNo++;
        }
    }

    /**
     * 查询匹配且生成匹配数据
     */
    private void match(Long taskId, MatchData matchData){
        // 查询udi
        //Page<UdiVo> voPage = this.queryUdi(udiPageNo,pageSize,matchData);
        List<UdiMatchRateVo> matchRateVoAllList = new ArrayList<>();
        boolean haveHundredFlag = false;
        if(StringUtils.isNotBlank(matchData.getUdiCode())){
            // 去匹配udiCode
            List<UdiMatchRateVo> matchRateVoList = this.queryUdiNew(matchData);
            this.setSource(matchRateVoList,1);
            matchRateVoAllList.addAll(matchRateVoList);
            haveHundredFlag = this.haveHundred(matchRateVoList);
        }
        if(!haveHundredFlag && StringUtils.isNotBlank(matchData.getYiBaoCode())){
            // 去匹配YiBaoCode
            List<UdiMatchRateVo> matchRateVoList = this.queryYiBaoCode(matchData);
            this.setSource(matchRateVoList,2);
            matchRateVoAllList.addAll(matchRateVoList);
            haveHundredFlag = this.haveHundred(matchRateVoList);
        }
        if(!haveHundredFlag && (StringUtils.isNotBlank(matchData.getCompanyName()) || StringUtils.isNotBlank(matchData.getProductFactoryCode())) ){
            // 厂家+产品货号/编号
            List<UdiMatchRateVo> matchRateVoList = matchESHelper.three(matchData.getCompanyName(), matchData.getProductFactoryCode());
            this.setSource(matchRateVoList,3);
            matchRateVoAllList.addAll(matchRateVoList);
            haveHundredFlag = this.haveHundred(matchRateVoList);
        }
        if (!haveHundredFlag && (StringUtils.isNotBlank(matchData.getBrandName())
                || StringUtils.isNotBlank(matchData.getProductName())
                || StringUtils.isNotBlank(matchData.getSpecification())
                || StringUtils.isNotBlank(matchData.getModel())) ) {
            // 品牌+名称+规格+型号;
            List<UdiMatchRateVo> matchRateVoList = matchESHelper.four(matchData.getBrandName(),
                    matchData.getProductName(),
                    matchData.getSpecification(),
                    matchData.getModel());
            this.setSource(matchRateVoList,4);
            matchRateVoAllList.addAll(matchRateVoList);
        }
        // 需要按照匹配率高低排序,留20条
        List<UdiMatchRateVo> matchRateVoSortList = this.getTopNMatchRates(matchRateVoAllList, 20);

        // 计算匹配率
        List<MatchTemp> matchTempList = new ArrayList<>();
        boolean hundredRate = false;
        for (UdiMatchRateVo udiVo : matchRateVoSortList) {
            // 核心匹配率逻辑
            BigDecimal matchRate = udiVo.getMatchRatio();
            if(matchRate.compareTo(ONE_HUNDRED_RATE) == 0){
                hundredRate = true;
            }
            MatchTemp matchTemp = new MatchTemp();
            BeanUtils.copyProperties(udiVo,matchTemp);
            Long matchId = matchData.getId();
            matchTemp.setProductId(udiVo.getId());
            matchTemp.setOriginalRecord(false);
            matchTemp.setMatchId(matchId);
            matchTemp.setTaskId(taskId);
            matchTemp.setMatchRate(matchRate);
            matchTemp.setCreateBy(null);
            matchTemp.setCreateName(null);
            matchTemp.setCreateTime(LocalDateTime.now());
            matchTemp.setUpdateBy(null);
            matchTemp.setUpdateName(null);
            matchTemp.setUpdateTime(LocalDateTime.now());
            matchTempList.add(matchTemp);
        }
        // 存在查询到了数据,但是匹配率全为0的情况
        if(CollectionUtils.isEmpty(matchTempList)){
            // 如果为空
            matchData.setMatchStatus(MatchStatusEnum.NO.getCode());
            matchData.setUpdateStatus(MatchUpdateStatusEnum.NOT_APPLICABLE.getCode());
            matchData.setUpdateTime(LocalDateTime.now());
            matchDataMapper.updateById(matchData);
        }else{
            if(hundredRate){
                // 100%对码
                matchData.setMatchStatus(MatchStatusEnum.HUNDRED.getCode());
            }else{
                // 部分对码
                matchData.setMatchStatus(MatchStatusEnum.SECTION.getCode());
            }
            matchData.setUpdateStatus(MatchUpdateStatusEnum.PENDING.getCode());
            matchData.setUpdateTime(LocalDateTime.now());
            matchDataMapper.updateById(matchData);

            // 进行排序
            List<MatchTemp> sortList = matchTempList.stream().sorted(Comparator.comparing(MatchTemp::getMatchRate)).collect(Collectors.toList());
            matchTempMapper.insertBatchSomeColumn(sortList);
        }
    }

    /**
     * 判断是否有百分之百的数据存在
     * @param list
     * @return
     */
    private boolean haveHundred(List<UdiMatchRateVo> list){
        for (UdiMatchRateVo udiMatchRateVo : list) {
            BigDecimal matchRatio = udiMatchRateVo.getMatchRatio();
            if(matchRatio.compareTo(ONE_HUNDRED_RATE) == 0){
                return true;
            }
        }
        return false;
    }

    private List<UdiMatchRateVo> getTopNMatchRates(List<UdiMatchRateVo> matchRateVoAllList, int n){
        // 按照 matchRatio 字段降序排序,并且保留前 n 条数据
        // todo 是否需要根据id进行去重?
        return matchRateVoAllList.stream()
                .sorted((vo1, vo2) -> vo2.getMatchRatio().compareTo(vo1.getMatchRatio()))
                .limit(n)
                .collect(Collectors.toList());
    }


    /**
     * 查询udi
     */
    private List<UdiMatchRateVo> queryUdiNew(MatchData matchData){
        String udiCode = matchData.getUdiCode();
        // String udiCodeRegex = this.splitWord(udiCode);
        List<String> udiCodeRegexList = new ArrayList<>();
        String lastUdi = udiCode;
        // 规定udi截取的最小长度
        int udiCodeMinLength = 8;
        while (true){
            String subUdi = decrementUDICode(lastUdi,2,udiCodeMinLength);
            if(Objects.equals(subUdi,lastUdi)){
                break;
            }else{
                lastUdi = subUdi;
                udiCodeRegexList.add(subUdi);
            }
        }
        return udiMapper.getUdiCodeRegex(udiCode,udiCodeRegexList);
    }


    // 递减 Code 的函数
    private String decrementUDICode(String code, int decrementAmount, int minLength) {
        int length = code.length();
        if(length<=minLength){
            return code;
        }
        return code.substring(0, length - decrementAmount);
    }


    /**
     * 查询C码
     */
    private List<UdiMatchRateVo> queryYiBaoCode(MatchData matchData){
        String yiBaoCode = matchData.getYiBaoCode();
        List<String> yiBaoCodeRegexList = new ArrayList<>();
        String lastYiBaoCode = yiBaoCode;
        // 规定udi截取的最小长度
        int yiBaoCodeMinLength = 8;
        while (true){
            String subYiBaoCode = decrementUDICode(lastYiBaoCode,2,yiBaoCodeMinLength);
            if(Objects.equals(subYiBaoCode,lastYiBaoCode)){
                break;
            }else{
                lastYiBaoCode = subYiBaoCode;
                yiBaoCodeRegexList.add(subYiBaoCode);
            }
        }

        return udiMapper.getYiBaoCodeRegex(yiBaoCode,yiBaoCodeRegexList);
    }

    private void setSource(List<UdiMatchRateVo> list, Integer source){
        if(CollectionUtils.isNotEmpty(list)){
            list.forEach(i -> i.setSource(source));
        }
    }
}