package com.prolog.eis.strategy.bcr;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.codingapi.tx.annotation.TxTransaction;
import com.prolog.eis.bcr_handle.BcrHandleStrategy;
import com.prolog.eis.constants.base.BaseTaskTypeConstants;
import com.prolog.eis.constants.base.ContainerInfoTypeConstants;
import com.prolog.eis.constants.base.ContainerTaskTypeConstants;
import com.prolog.eis.constants.cfg.LocationCfgConstants;
import com.prolog.eis.core.dto.route.AreaLocationCountDto;
import com.prolog.eis.core.dto.route.AreaTaskReqDto;
import com.prolog.eis.core.dto.route.ItemContainerReqDto;
import com.prolog.eis.core.model.biz.carry.CarryTask;
import com.prolog.eis.core.model.biz.route.ContainerLocation;
import com.prolog.eis.dao.container.ContainerInfoDetailMapper;
import com.prolog.eis.dao.device.DeviceInfoMapper;
import com.prolog.eis.dao.inbound.BizInboundTaskDetailHisMapper;
import com.prolog.eis.dao.inbound.BizInboundTaskHisMapper;
import com.prolog.eis.dto.inbound.BestAreaDto;
import com.prolog.eis.dto.wcs.BcrConfigDto;
import com.prolog.eis.dto.wcs.BcrRequestDto;
import com.prolog.eis.enums.base.BaseStatusEnum;
import com.prolog.eis.enums.mcs.ShapeEnums;
import com.prolog.eis.enums.station.StationTypeEnum;
import com.prolog.eis.model.carry.WmsCarryTask;
import com.prolog.eis.model.container.ContainerInfo;
import com.prolog.eis.model.container.ContainerInfoDetail;
import com.prolog.eis.model.container.LocationCfg;
import com.prolog.eis.model.container.StationCfg;
import com.prolog.eis.model.device.DeviceInfo;
import com.prolog.eis.model.inbound.BizInboundTask;
import com.prolog.eis.model.inbound.BizInboundTaskDetail;
import com.prolog.eis.model.inbound.BizInboundTaskDetailHis;
import com.prolog.eis.model.inbound.BizInboundTaskHis;
import com.prolog.eis.service.carry.WmsCarryTaskService;
import com.prolog.eis.service.container.ContainerInfoService;
import com.prolog.eis.service.container.LocationCfgService;
import com.prolog.eis.service.container.StationCfgService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.inbound.BizInboundTaskDetailService;
import com.prolog.eis.service.inbound.BizInboundTaskService;
import com.prolog.framework.beans.BeanUtils;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Component
//@RequiredArgsConstructor
public class AsyncInboundBcrStrategy extends BcrHandleStrategy {
    @Autowired
    private ContainerInfoService containerInfoService;
    @Autowired
    private BizInboundTaskService inboundTaskService;
    @Autowired
    private BizInboundTaskDetailService bizInboundTaskDetailService;
    @Autowired
    private RouteFeignService plgFeignRouteService;
    @Autowired
    private AsyncInboundBcrStrategy asyncInboundBcrStrategy;
    @Autowired
    private BizInboundTaskHisMapper inboundTaskHisMapper;
    @Autowired
    private BizInboundTaskDetailHisMapper inboundTaskDetailHisMapper;
    @Autowired
    private WmsCarryTaskService wmsCarryTaskService;
    @Autowired
    private LocationCfgService locationCfgService;
    @Autowired
    private StationCfgService stationCfgService;
    @Autowired
    private DeviceInfoMapper deviceInfoMapper;
    @Autowired
    private ContainerInfoDetailMapper containerInfoDetailMapper;

    private final static String matchWeight = "^(0|([1-9]\\d*))(\\.\\d{0,5})?$";

    @Override
    public String doAsyncHandle(BcrRequestDto bcrRequestDto, BcrConfigDto bcrConfigDto) throws Exception {
        log.debug("---------入库策略------------");
        String restMessage = deviceCheck(bcrConfigDto, bcrRequestDto);
        return restMessage;
    }

    private void toInHistory(BizInboundTask inBoundTask, String restMessage) {
        BizInboundTaskHis wmsInTaskHis = new BizInboundTaskHis();
        inBoundTask.setStatus(-1);
        inBoundTask.setErrorMsg(restMessage);
        inBoundTask.setUpdateTime(LocalDateTime.now());
        BeanUtils.copyProperties(inBoundTask, wmsInTaskHis);
        inboundTaskHisMapper.save(wmsInTaskHis);

        List<BizInboundTaskDetail> detailList = bizInboundTaskDetailService.findByMap(MapUtils.put("inboundId", inBoundTask.getId()).getMap());
        ArrayList<BizInboundTaskDetailHis> taskDetailHis = new ArrayList<>();
        for (BizInboundTaskDetail bizInboundTaskDetail : detailList) {
            BizInboundTaskDetailHis bizInboundTaskDetailHis = new BizInboundTaskDetailHis();
            BeanUtils.copyProperties(bizInboundTaskDetail, bizInboundTaskDetailHis);
            taskDetailHis.add(bizInboundTaskDetailHis);
        }
        inboundTaskDetailHisMapper.saveBatch(taskDetailHis);

        //删除入库任务
        inboundTaskService.deleteById(inBoundTask.getId());
        bizInboundTaskDetailService.deleteByMap(MapUtils.put("inboundId", inBoundTask.getId()).getMap());
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public String deviceCheck(BcrConfigDto bcrConfigDto, BcrRequestDto bcrRequestDto) throws Exception {

        if (bcrConfigDto.getBarcodeHandle() != 0) {
            if (bcrRequestDto.getStockId().equalsIgnoreCase("noRead")) {
                return "当前容器条码未识别";
            }
        }
        if (bcrConfigDto.getShapeHandle() != 0) {
            if (bcrRequestDto.getDetection() != ShapeEnums.NORMAL.getType()) {
                return "当前容器尺寸检测结果异常：" + ShapeEnums.getDesc(bcrRequestDto.getDetection());
            }
        }
        if (bcrConfigDto.getWeightHandle() != 0) {
            AssertUtils.isNotTrue(!bcrRequestDto.getWeight().matches(matchWeight), "重量含有字符");
            BigDecimal limitWeight = new BigDecimal(bcrConfigDto.getWeight());
            BigDecimal currentWeight = new BigDecimal(bcrRequestDto.getWeight());
            if (limitWeight.compareTo(currentWeight) < 0) {
                return "当前容器超重，限重：" + bcrConfigDto.getWeight() + ",当前重量：" + bcrRequestDto.getWeight();
            }
        }
        List<ContainerInfo> containerInfoList = containerInfoService.findByMap(MapUtils.put("containerNo", bcrRequestDto.getStockId()).getMap());
        if (CollectionUtil.isNotEmpty(containerInfoList) && Objects.equals(containerInfoList.get(0).getTaskType(), ContainerTaskTypeConstants.SUPPLY_BACK_TYPE)) {
            checkBcrContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId());
            ContainerInfoDetail containerInfoDetail = containerInfoDetailMapper.findByMap(MapUtils.put("containerNo",  bcrRequestDto.getStockId()).getMap(), ContainerInfoDetail.class).stream().findFirst().orElseThrow(() -> new PrologException("为查询到库存"));
            //4 创建点位
            ContainerLocation containerLocation = plgFeignRouteService.seachLocationNo(bcrRequestDto.getStockId());
            if (Objects.isNull(containerLocation)) {
                //创建容器点位信息
                plgFeignRouteService.createItemContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId(), containerInfoList.get(0).getContainerType().toString(), containerInfoDetail.getItemId() + "@@" + containerInfoDetail.getLotId());
            }
            List<DeviceInfo> deviceInfoList = deviceInfoMapper.findByMap(MapUtils.put("isLock", 0).getMap(), DeviceInfo.class);
            List<String> areaNoList = deviceInfoList.stream().map(DeviceInfo::getDeviceNo).collect(Collectors.toList());
            List<AreaLocationCountDto> areaLocationCountDtos = plgFeignRouteService.queryAreaLocationCountDtoList(areaNoList);
            areaLocationCountDtos = areaLocationCountDtos.stream().filter(item -> item.getFreeCount() > 0).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(areaLocationCountDtos)) {
                throw new Exception("没有可用的四向库楼层");
            }
            AreaTaskReqDto ad = new AreaTaskReqDto();
            ad.setAreaNos(areaLocationCountDtos.stream().map(AreaLocationCountDto::getAreaNo).collect(Collectors.toList()));
            Map<String, Integer> sxkTaskByAreaNos = plgFeignRouteService.getSxkTaskByAreaNos(ad);
            areaLocationCountDtos.forEach(item -> item.setLocatorCount(sxkTaskByAreaNos.get(item.getAreaNo())));
            AreaLocationCountDto areaLocationCountDto = areaLocationCountDtos.stream().sorted(Comparator.comparing(AreaLocationCountDto::getLocatorCount).thenComparing(Comparator.comparing(AreaLocationCountDto::getFreeCount, Comparator.reverseOrder()))).findFirst().get();
            plgFeignRouteService.createCarryTask(containerInfoList.get(0).getContainerNo(), null, containerLocation.getCurrentArea(), containerLocation.getCurrentLocation(), areaLocationCountDto.getAreaNo(), null, String.valueOf(BaseTaskTypeConstants.SUPPLER_BACK), 1, "回库", containerInfoDetail.getItemId()+"@@"+containerInfoDetail.getLotId());
            return null;
        }
        Object o = checkStore(bcrRequestDto);
        if (Objects.nonNull(o)) {
            return String.format("当前容器【%s】库存已存在，请确定", bcrRequestDto.getStockId());
        }
        Object wmsTask = null;
        if (bcrConfigDto.getTaskHandle() != 0) {
            wmsTask = getWmsTask(bcrRequestDto);
        }
        if (Objects.isNull(wmsTask)) {
            return "没有找到当前容器的入库任务或借道任务";
        }
        doOtherHandle(o, wmsTask, bcrRequestDto, bcrConfigDto);
        return null;
    }

    @Override
    public Object checkStore(BcrRequestDto bcrRequestDto) throws Exception {
        log.info("----------检查库存------------");
        return containerInfoService.checkStore(bcrRequestDto);
    }

    @Override
    public Object getWmsTask(BcrRequestDto bcrRequestDto) throws Exception {
        log.info("---------------获取入库任务-------------");
        //入库任务
        BizInboundTask inboundTask = inboundTaskService.getWmsTask(bcrRequestDto);
        if (Objects.nonNull(inboundTask)) {
            return inboundTask;
        }

        //借道入库任务
        WmsCarryTask wmsCarryTask = wmsCarryTaskService.findByMap(MapUtils.put("containerNo", bcrRequestDto.getStockId()).getMap()).stream().findFirst().orElse(null);
        return wmsCarryTask;
    }


    @Override
    public void doOtherHandle(Object containerStore, Object wmsTask, BcrRequestDto bcrRequestDto, BcrConfigDto bcrConfigDto) throws Exception {
        asyncInboundBcrStrategy.asyncDoOtherHandle(wmsTask, bcrRequestDto, bcrConfigDto);
    }


    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void asyncDoOtherHandle(Object task, BcrRequestDto bcrRequestDto, BcrConfigDto bcrConfigDto) throws Exception {
        if (task instanceof BizInboundTask) {

            //正常入库
            BizInboundTask inboundTask = (BizInboundTask) task;
            //1 校验并解绑bcr预占托盘
            checkBcrContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId());
            //2 修改入库单状态为已开始
            inboundTaskService.updateInboundTaskStatus(inboundTask);
            //3 创建库存
            containerInfoService.createContainerStore(inboundTask);
            //4 创建点位
            ContainerLocation containerLocation = plgFeignRouteService.seachLocationNo(bcrRequestDto.getStockId());
            if (Objects.isNull(containerLocation)) {
                //创建容器点位信息
                plgFeignRouteService.createItemContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId(), inboundTask.getContainerType().toString(), inboundTask.getItemContainerBusinessProperty());
            }
            //5 创建搬运任务
            //搬运到二次分配点
            BestAreaDto bestAreaDto = BestAreaDto.builder().containerNo(bcrRequestDto.getStockId())
                    .areaNo(bcrConfigDto.getAreaNo()).sourceArea(bcrConfigDto.getAreaNo()).deviceHeight(bcrRequestDto.getHeight())
                    .sourceLocation(bcrConfigDto.getLocationNo()).businessProperty(inboundTask.getItemContainerBusinessProperty()).build();
            String bestInAreaNo = inboundTaskService.findBestInAreaNo(bestAreaDto);
            AssertUtils.isNotNull(bestInAreaNo, "没有找到入库货位");
            if (bcrConfigDto.getAllocation() == 1) {
                LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.RCFP).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("二次分配点位未配置"));
                plgFeignRouteService.createCarryTask(bcrRequestDto.getStockId(), null, bcrConfigDto.getAreaNo(), bcrConfigDto.getLocationNo(), locationCfg.getAreaNo(), null,
                        String.valueOf(BaseTaskTypeConstants.INBOUND_TYPE), 1, "入库到二次分配点",inboundTask.getItemContainerBusinessProperty());
            } else {
                plgFeignRouteService.createCarryTask(bcrRequestDto.getStockId(), null, bcrConfigDto.getAreaNo(), bcrConfigDto.getLocationNo(), bestInAreaNo, null,
                        String.valueOf(BaseTaskTypeConstants.INBOUND_TYPE), 1, "入库",inboundTask.getItemContainerBusinessProperty());
            }

        } else if (task instanceof WmsCarryTask) {

            //借道入库/出库   只有地堆库才会经过bcr
            WmsCarryTask wmsCarryTask = (WmsCarryTask) task;
            //1 修改状态为已开始
            wmsCarryTask.setStatus(BaseStatusEnum.STATUS_STARTED.getCode());
            wmsCarryTask.setUpdateTime(new Date());
            wmsCarryTaskService.update(wmsCarryTask);

            if (wmsCarryTask.getTaskType().equals("1")) {
                //入库
                //1 校验并解绑bcr预占托盘
                checkBcrContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId());
                //2 获取入库目标区域
                LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("wmsAreaNo", wmsCarryTask.getTargetArea()).put("type", LocationCfgConstants.WMSKQ).getMap()).stream().findFirst().orElseThrow(() -> new PrologException(String.format("借道入库目标区域%s未配置对应区域", wmsCarryTask.getTargetArea())));
                //3 创建点位
                ContainerLocation containerLocation = plgFeignRouteService.seachLocationNo(bcrRequestDto.getStockId());
                if (Objects.isNull(containerLocation)) {
                    //创建容器点位信息
                    plgFeignRouteService.createItemContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId(), "0", "0");                }
                //4 创建搬运任务
                plgFeignRouteService.createCarryTask(wmsCarryTask.getContainerNo(), null, bcrConfigDto.getAreaNo(), bcrConfigDto.getLocationNo(), locationCfg.getAreaNo(), null,
                        String.valueOf(BaseTaskTypeConstants.JD_TASK_TYPE), 1, "借道入库", null);
            } else {
                //出库
                //1 获取地堆库出库站台
                StationCfg stationCfg = stationCfgService.findByMap(MapUtils.put("stationType", StationTypeEnum.SXKZT_TYPE.getCode()).put("isLock", 0).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("1F四项库整托出库站台未配置或已锁定"));
                //2 创建点位
                plgFeignRouteService.createItemContainer(bcrRequestDto.getSource(), bcrRequestDto.getStockId(), "0", "0");
                //3 创建搬运任务
                plgFeignRouteService.createCarryTask(wmsCarryTask.getContainerNo(), null, bcrConfigDto.getAreaNo(), bcrConfigDto.getLocationNo(), stationCfg.getAreaNo(), null,
                        String.valueOf(BaseTaskTypeConstants.JD_TASK_TYPE), 1, "借道出库", null);
            }

        } else {
            throw new PrologException("错误的入库类型");
        }

    }
    private void checkBcrContainer(String bcrLocationNo, String containerNo) {
        LocationCfg locationCfg = locationCfgService.findByMap(MapUtils.put("locationNo", bcrLocationNo).put("type", LocationCfgConstants.BCR).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("bcr点位未配置"));
        AssertUtils.isNotNull(locationCfg.getContainerNo(), "当前bcr预占容器为空");
        AssertUtils.isNotTrue(!StrUtil.equals(locationCfg.getContainerNo(), containerNo), "当前容器与bcr预占容器不一致");
        locationCfg.setContainerNo(null);
        locationCfg.setUpdateTime(LocalDateTime.now());
        locationCfgService.update(locationCfg);
    }

}
