package com.prolog.eis.api;

import com.prolog.eis.bcr_handle.BcrHandleStrategy;
import com.prolog.eis.dto.strategy.AreaInfoDto;
import com.prolog.eis.dto.strategy.ContainerInfoDto;
import com.prolog.eis.dto.wcs.BcrConfigDto;
import com.prolog.eis.dto.wcs.BcrRequestDto;
import com.prolog.eis.dto.wcs.McsResponseDto;
import com.prolog.eis.outbound_dispatch.DispatchStrategy;
import com.prolog.eis.pallet_positioning.PositioningStrategy;
import com.prolog.eis.roadway_allocation.AllocationStrategy;
import com.prolog.eis.store_init.StoreInitStrategy;
import com.prolog.framework.core.exception.PrologException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

@Slf4j
public class StrategyApi {

    /**
     * 调度出库
     *
     * @param dispatchStrategy 出库策略
     * @param billType
     * @param warehouseArea
     * @return 试算结果
     * @throws Exception 异常
     */
    public static void doSwitchDispatch(DispatchStrategy dispatchStrategy, String billType, String warehouseArea, StoreInitStrategy storeInitStrategy) throws Exception {
        dispatchStrategy.doDispatch(billType, warehouseArea, storeInitStrategy);
    }
    /**
     * 获取最佳托盘
     *
     * @param strategyMap          排序策略
     * @param containerInfoDtoList 库存集合
     * @param qty                  所需数量
     * @return 排序后的托盘
     * @throws Exception 异常
     */
    public static List<ContainerInfoDto> getBestContainer(Map<Integer, String> strategyMap, List<ContainerInfoDto> containerInfoDtoList, BigDecimal qty) throws Exception {
        List<PositioningStrategy<ContainerInfoDto>> strategies = getPositioningStrategyList(strategyMap);
        return strategies.get(0).sort(strategies.get(0).order, containerInfoDtoList, qty, strategies);
    }

    /**
     * 获取最佳区域
     *
     * @param strategyMap     路径分配策略
     * @param areaInfoDtoList 区域集合
     * @return 排序后的区域
     * @throws Exception 异常
     */
    public static List<AreaInfoDto> getBestArea(Map<Integer, String> strategyMap, List<AreaInfoDto> areaInfoDtoList) throws Exception {
        List<AllocationStrategy<AreaInfoDto>> strategies = getAllocationStrategyList(strategyMap);
        return strategies.get(0).sort(strategies.get(0).order, areaInfoDtoList, strategies);
    }

    /**
     * bcr异步处理
     *
     * @param map              bcr处理策略集合
     * @param bcrRequestDto     hcr请求
     * @param bcrConfigDtoList bcr配置集合
     * @return 返回错误信息
     */
    public static String doBcrAsyncHandle(Map<String, BcrHandleStrategy> map, BcrRequestDto bcrRequestDto, List<BcrConfigDto> bcrConfigDtoList)throws Exception {
        BcrConfigDto bcrConfig =
                bcrConfigDtoList.stream().filter(bcrConfigDto -> bcrConfigDto.getLocationNo().equals(bcrRequestDto.getSource())).findFirst()
                        .orElseThrow(() -> new PrologException("未找到对应托盘【" + bcrRequestDto.getStockId() + "】点位【" + bcrRequestDto.getSource() + "】的bcr配置"));

        BcrHandleStrategy bcrHandleStrategy = getBcrHandleStrategy(map, bcrConfig);
        return bcrHandleStrategy.doAsyncHandle(bcrRequestDto, bcrConfig);
    }

    /**
     * bcr同步处理
     *
     * @param bcrRequestDto    bcr请求实体
     * @param bcrConfigDtoList bcr配置集合
     * @return 返回信息
     */
    public static McsResponseDto doBcrSyncHandle(Map<String, BcrHandleStrategy> map, BcrRequestDto bcrRequestDto, List<BcrConfigDto> bcrConfigDtoList) {
        McsResponseDto mcsResponseDto = new McsResponseDto();
        mcsResponseDto.setTaskId(bcrRequestDto.getTaskId());
        try {
            BcrConfigDto bcrConfig =
                    bcrConfigDtoList.stream().filter(bcrConfigDto -> bcrConfigDto.getLocationNo().equals(bcrRequestDto.getSource())).findFirst().orElseThrow(() -> new RuntimeException("未匹配到对应bcr请求点位:" + bcrRequestDto.getSource()));
            BcrHandleStrategy bcrHandleStrategy = getBcrHandleStrategy(map, bcrConfig);
            Integer integer = bcrHandleStrategy.doSyncHandle(bcrRequestDto, bcrConfig);
            mcsResponseDto.setTarget(integer);
        } catch (Exception e) {
            mcsResponseDto.setErrorMsg(e.getMessage());
        }
        return mcsResponseDto;
    }

    private static BcrHandleStrategy getBcrHandleStrategy(Map<String, BcrHandleStrategy> map, BcrConfigDto bcrConfigDto) throws Exception {
        String strategyName = bcrConfigDto.getStrategyName();
        String[] split = strategyName.split("\\.");
        strategyName = split[split.length - 1];
        strategyName = strategyName.substring(0, 1).toLowerCase() + strategyName.substring(1);
        BcrHandleStrategy bcrHandleStrategy = map.get(strategyName);
        return bcrHandleStrategy;
    }

    private static DispatchStrategy getDispatchStrategy(String strategyName) throws Exception {
        Class<?> aClass = Class.forName(strategyName);
        Constructor<?> constructor = aClass.getConstructor();
        return (DispatchStrategy) constructor.newInstance();
    }

    @SuppressWarnings("unchecked")
    private static List<PositioningStrategy<ContainerInfoDto>> getPositioningStrategyList(Map<Integer, String> strategyMap) throws Exception {
        List<PositioningStrategy<ContainerInfoDto>> strategies = new ArrayList<>();
        for (Map.Entry<Integer, String> integerStringEntry : strategyMap.entrySet()) {
            Class<?> aClass = Class.forName(integerStringEntry.getValue());
            Constructor<?> constructor = aClass.getConstructor(int.class);
            PositioningStrategy<ContainerInfoDto> o = (PositioningStrategy<ContainerInfoDto>) constructor.newInstance(integerStringEntry.getKey());
            strategies.add(o);
        }
        strategies.sort(Comparator.comparing(PositioningStrategy::getOrder));
        return strategies;
    }

    @SuppressWarnings("unchecked")
    private static List<AllocationStrategy<AreaInfoDto>> getAllocationStrategyList(Map<Integer, String> strategyMap) throws Exception {
        List<AllocationStrategy<AreaInfoDto>> strategies = new ArrayList<>();
        for (Map.Entry<Integer, String> integerStringEntry : strategyMap.entrySet()) {
            Class<?> aClass = Class.forName(integerStringEntry.getValue());
            Constructor<?> constructor = aClass.getConstructor(int.class);
            AllocationStrategy<AreaInfoDto> o = (AllocationStrategy<AreaInfoDto>) constructor.newInstance(integerStringEntry.getKey());
            strategies.add(o);
        }
        strategies.sort(Comparator.comparing(AllocationStrategy::getOrder));
        return strategies;
    }
}
