package com.prolog.cs.biz.outbound.stage;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.prolog.cs.basic.warehouse.model.vo.StageInfo;
import com.prolog.cs.biz.outbound.core.SpringUtils;
import com.prolog.cs.biz.outbound.dao.WaveBillMapper;
import com.prolog.cs.biz.outbound.dao.WorkStageRuntimeMapper;
import com.prolog.cs.biz.outbound.model.entity.WaveBillDO;
import com.prolog.cs.biz.outbound.model.entity.WorkStageRuntimeDO;
import com.prolog.cs.biz.outbound.resource.WarehouseResource;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.utils.ClassUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.upcloud.base.strategy.domain.core.MatchFactor;
import com.prolog.upcloud.base.strategy.domain.core.SearchFactor;
import com.prolog.upcloud.base.strategy.domain.core.StrategyContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanMap;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class WorkStageContext extends StrategyContext<WorkStage> {

    private static Logger logger = LoggerFactory.getLogger(WorkStageContext.class);
    private Map<String, Object> matchVariable = new ConcurrentHashMap<>();
    private Map<String, Object> defaultSearchConditions = new ConcurrentHashMap<>();

    // 计算结果
    private List<WorkStage> searchResult;

    private boolean completed;
    private boolean isSkipped;

    public WorkStageContext() {
        completed = false;
        isSkipped = false;
    }

    /**
     * 查询数据库，数据映射. 得到集货区当前状态信息
     */
    private static List<WorkStageBillInfo> getWorkStageBillInfo(String stageId) {
        Map<String, Object> map = MapUtils.put("stageId", stageId).put("status", 1).getMap();
        WorkStageRuntimeMapper workStageRuntimeInfoMapper = SpringUtils.getBeansByType(WorkStageRuntimeMapper.class);
        List<WorkStageRuntimeDO> workStageRuntimes = workStageRuntimeInfoMapper.findByMap(map, WorkStageRuntimeDO.class);
        if (CollectionUtil.isEmpty(workStageRuntimes)) {
            return null;
        }
        List<WorkStageBillInfo> workStageBillInfoList = workStageRuntimes.stream().map(info -> {
            String shipperId = info.getShipperId();
            String billGroupId = info.getBillGroupId();
            BigDecimal qty = info.getCaseQty();
            WorkStageBillInfo workStageBillInfo = new WorkStageBillInfo(shipperId, billGroupId, stageId, qty);
            return workStageBillInfo;
        }).collect(Collectors.toList());
        return workStageBillInfoList;
    }

    private static boolean match(WorkStage workStage, List<SearchFactor> searchFactors) {
        StageInfo info = workStage.getInfo();
        return searchFactors.stream().allMatch(searchFactor -> allFiledIsMatched(info, searchFactor));
    }

    private static boolean allFiledIsMatched(StageInfo info, SearchFactor searchFactor) {
        Object v = ClassUtils.getFieldValue(searchFactor.getFieldName(), info);
        if (v == null) {
            return true;
        }
        MatchFactor matchFactor = getMatchFactor(searchFactor);
        return matchFactor.matchField(v);
    }

    private static MatchFactor getMatchFactor(SearchFactor searchFactor) {
        MatchFactor matchFactor = new MatchFactor();
        matchFactor.setFieldName(searchFactor.getFieldName());
        matchFactor.setMathType(searchFactor.getMathType());
        matchFactor.setValue(searchFactor.getValue());
        matchFactor.setValueType(searchFactor.getValueType());
        return matchFactor;
    }

    public boolean isSkipped() {
        return isSkipped;
    }

    public void addMatchVariables(Map<String, Object> matchVariableMap) {
        matchVariableMap.entrySet().stream().filter(entry -> entry.getValue() != null).forEach(entry -> {
            matchVariable.put(entry.getKey(), entry.getValue());
        });
    }

    public void addDefaultSearchConditions(Map<String, Object> conditions) {
        conditions.entrySet().stream().filter(entry -> entry.getValue() != null).forEach(entry -> {
            defaultSearchConditions.put(entry.getKey(), entry.getValue());
        });
    }

    public void addDefaultSearchCondition(String key, Object value) {
        if (value == null || key == null) {
            logger.warn("添加空值过滤条件: {}", key);
            return;
        }
        defaultSearchConditions.put(key, value);
    }

    public List<WorkStage> getSearchResult() {
        return searchResult;
    }

    /**
     * 从warehouse服务中拿到符合条件的所有站存取信息
     * @return List<StageInfo> 集货区集合
     */
    private List<StageInfo> findAllStages() {
        WarehouseResource wareHouseClient = SpringUtils.getBeansByType(WarehouseResource.class);
        RestMessage<List<StageInfo>> stages = wareHouseClient.findAllStages();
        if (stages == null || !stages.isSuccess() || stages.getData() == null || stages.getData().isEmpty()) {
            logger.error("仓库未配置集货区");
            throw new BizException("仓库未配置集货区");
        }
        logger.info("可用集货区大小是：" + stages.getData().size());
        return stages.getData();
    }

    @Override
    public boolean completed() {
        return completed;
    }

    @Override
    public Object getMatchFieldValue(String s) {
        return matchVariable.get(s);
    }

    /**
     * 计算符合集货区集合
     * @param searchFactors 查询条件
     * @return List<WorkStage> 集货区集合
     */
    @Override
    public List<WorkStage> search(List<SearchFactor> searchFactors) {
        logger.info("start search workStageList");

        if (isSkippedRule(searchFactors)) {
            isSkipped = true;
            return null;
        }

        List<StageInfo> stageInfoList = findAllStages();
        logger.info("defaultSearchConditions:"+ JSON.toJSONString(defaultSearchConditions));
        logger.info("stageInfoList:"+ JSON.toJSONString(stageInfoList));
        // 过滤默认条件
        List<StageInfo> filterStages = stageInfoList.stream().filter(stageInfo -> {
            BeanMap beanMap = BeanMap.create(stageInfo);
            return defaultSearchConditions.entrySet().stream().allMatch(entry -> {
                Object value = entry.getValue();
                // 条件值为空时，默认能匹配
                if (null == value) {
                    return true;
                }
                String key = entry.getKey();
                Object o = beanMap.get(key);
                if (null == o) {
                    return true;
                }
                boolean equals = value.equals(o);
                if (!equals) {
                    logger.info("匹配的值为: value {}, o {},key {}", value, o, key);
                }
                return equals;
            });
        }).collect(Collectors.toList());

        if (filterStages.isEmpty()) {
            String message = "查找集货区失败[订单类型:" + defaultSearchConditions.get("stageType") + "、配送方向:" + defaultSearchConditions.get("segment1") + "、提货方式:" + defaultSearchConditions.get("deliveryType") + "]，请配置";
            logger.error(message);
            throw new BizException(message);
        }

        // 构造暂存区实时信息
        List<WorkStage> workStages = filterStages.stream().map(info -> {
            String stageId = info.getStageId();
            List<WorkStageBillInfo> stageBillInfoList = getWorkStageBillInfo(stageId);
            WorkStage workStage = new WorkStage(info, stageBillInfoList);
            // 查询已占用体积
            if (CollectionUtil.isNotEmpty(stageBillInfoList)) {
                WaveBillMapper waveBillMapper = SpringUtils.getBeansByType(WaveBillMapper.class);
                Criteria crt = Criteria.forClass(WaveBillDO.class);
                Restriction r1 = Restrictions.in("billGroupId", stageBillInfoList.stream().map(t->t.getBillGroupId()).distinct().toArray(String[]::new));
                crt.setRestriction(Restrictions.and(r1));
                List<WaveBillDO> bills = waveBillMapper.findByCriteria(crt);
                workStage.setNumOfVol(bills.stream().filter(t->t.getTotalVolume()!=null).map(WaveBillDO::getTotalVolume).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
                logger.info("订单:"+JSON.toJSONString(bills)+"，体积:"+workStage.getNumOfVol());
            }
            return workStage;
        }).collect(Collectors.toList());
        searchResult = workStages;
        completed = true;
        return searchResult;
    }

    private boolean isSkippedRule(List<SearchFactor> searchFactors) {
        if (searchFactors != null && searchFactors.size() == 1) {
            SearchFactor searchFactor = searchFactors.get(0);
            String name = searchFactor.getName();
            if ("skip".equals(name)) {
                logger.info("should skip this rule,do nothing");
                return true;
            }
        }
        return false;
    }

}
