package com.prolog.eis.service.carry.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.codingapi.tx.annotation.TxTransaction;
import com.prolog.eis.common.util.PrologStringUtils;
import com.prolog.eis.common.util.location.LocationConstants;
import com.prolog.eis.constants.base.BasePropertyConstants;
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.model.biz.carry.CarryTask;
import com.prolog.eis.dto.inbound.BestAreaDto;
import com.prolog.eis.dto.wcs.StackerTaskDto;
import com.prolog.eis.enums.base.LayerEnum;
import com.prolog.eis.enums.wms.WmsCarryTaskInCode;
import com.prolog.eis.model.container.ContainerInfo;
import com.prolog.eis.model.container.EmptyContainerTask;
import com.prolog.eis.model.container.LocationCfg;
import com.prolog.eis.router.vo.ContainerLocationVo;
import com.prolog.eis.service.carry.StackerService;
import com.prolog.eis.service.container.ContainerInfoService;
import com.prolog.eis.service.container.EmptyContainerTaskService;
import com.prolog.eis.service.container.LocationCfgService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.inbound.BizInboundTaskService;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @desc:
 * @author: zhangJie
 * @date: 2023/8/28 18:17
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class  StackerServiceImpl implements StackerService {

    private final RouteFeignService routeFeignService;
    private final LocationCfgService locationCfgService;
    private final BizInboundTaskService inboundTaskService;
    private final ContainerInfoService containerInfoService;
    private final EmptyContainerTaskService emptyContainerTaskService;

    //空托发往叠盘机
    @Override
    public synchronized void sendStackerTask(String containerNo, LayerEnum layerEnum, String wmsLocationCode) throws Exception {
        //1 查询当前容器点位区域信息
        ContainerLocationVo containerLocationVo = routeFeignService.findLocationByContainerList(Arrays.asList(containerNo)).stream().findFirst().orElseThrow(() -> new PrologException(String.format("容器%s点位信息未查到", containerNo)));
        //2 查询对应碟盘机区域信息
        LocationCfg dpjLocation = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.getStackerType(layerEnum)).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("叠盘机区域信息未配置"));
        //判断叠盘机是否故障
        boolean isFault = dpjLocation.getStatus() == 0;
        if (isFault) {
            //叠盘机故障
            Map<String, Object> map = MapUtils.put("wmsAreaNo", wmsLocationCode).put("type", 5).getMap();
            if (StrUtil.equals(WmsCarryTaskInCode.AGVL.getCode(), wmsLocationCode)) {
                map.put("type", 4);
            }
            LocationCfg locationCfg = locationCfgService.findByMap(map).stream().findFirst().orElseThrow(() -> new PrologException(String.format("wms区域%s信息未配置", wmsLocationCode)));
            CarryTask carryTask = new CarryTask();
            carryTask.setTaskType(LocationConstants.PATH_TASK_TYPE_CARRY);
            carryTask.setId(PrologStringUtils.newGUID());
            carryTask.setContainerNo(containerNo);
            carryTask.setPalletNo(containerNo);
            carryTask.setStartRegion(containerLocationVo.getCurrentArea());
            carryTask.setStartLocation(containerLocationVo.getCurrentLocation());
            carryTask.setEndRegion(locationCfg.getAreaNo());
            carryTask.setHeight(1);
            carryTask.setBusinessProperty("EMPTY");
            carryTask.setBusinessType(String.valueOf(BaseTaskTypeConstants.EMPTY_TYPE));
            carryTask.setBusinessDescribe("空托搬往库内");
            carryTask.setPriority(99);
            carryTask.setExtend1(wmsLocationCode);
            routeFeignService.createBatchCarry(Arrays.asList(carryTask));

        } else {
            //3 判断叠盘机当前是否可入
            AssertUtils.isNotTrue(dpjLocation.getMaxCachePosition() <= dpjLocation.getCurNum(), "叠盘机已满");
            //4 搬运
            startCarryTask(containerNo, containerLocationVo, dpjLocation);
        }

    }

    /**
     * @param containerNo      搬运的容器
     * @param containerLocationVo 容器所在的位置信息
     * @param stackerLocation  叠盘机所在的点位信息
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void startCarryTask(String containerNo, ContainerLocationVo containerLocationVo, LocationCfg stackerLocation) throws Exception {
        //1 叠盘机缓存数量+1
        stackerLocation.setCurNum(stackerLocation.getCurNum() + 1);
        stackerLocation.setUpdateTime(LocalDateTime.now());
        locationCfgService.update(stackerLocation);
        //2 开始搬运
        routeFeignService.createCarryTask(containerNo, LocationConstants.PATH_TASK_TYPE_OUTBOUND, containerLocationVo.getCurrentArea(), containerLocationVo.getCurrentLocation(), stackerLocation.getAreaNo(), null, String.valueOf(BaseTaskTypeConstants.EMPTY_OUT_TYPE), 1, "搬往叠盘机", null);
    }

    //叠盘机叠满发往库内
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TxTransaction(rollbackFor = Exception.class)
    public void sendStore(StackerTaskDto taskDto)throws Exception {
        String targetArea = null, warehouseArea = null, containerNo =findContainer();
        //1 查询叠盘机位置信息
        LocationCfg dpjLocationCfg = locationCfgService.findByMap(MapUtils.put("locationNo", taskDto.getCoord()).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("叠盘机位置未配置"));
        //3 1楼叠盘机或2楼空托垛缓存位满 找四项库货位
        if (StrUtil.isBlank(targetArea)) {
            BestAreaDto bestAreaDto = BestAreaDto.builder().containerNo(containerNo)
                    .areaNo(dpjLocationCfg.getAreaNo()).sourceArea(dpjLocationCfg.getAreaNo()).deviceHeight(1)
                    .sourceLocation(dpjLocationCfg.getLocationNo()).businessProperty("EMPTY").build();
            targetArea = inboundTaskService.findBestInAreaNo(bestAreaDto);
            warehouseArea = WmsCarryTaskInCode.MJZ.getCode();
        }
        AssertUtils.isNotTrue(!routeFeignService.checkAreaIn(targetArea), "位置已满");
        //4 叠盘机对应的空托数量重置为0
        dpjLocationCfg.setCurNum(0);
        dpjLocationCfg.setUpdateTime(LocalDateTime.now());
        locationCfgService.update(dpjLocationCfg);
        //5 创建库存
        containerInfoService.createEmptyStore(containerNo, warehouseArea);
        //6 创建点位
        routeFeignService.createItemContainer(taskDto.getCoord(), containerNo, String.valueOf(ContainerInfoTypeConstants.EMPTY_PALLET_PILE), String.valueOf(BasePropertyConstants.EMPTY_PALLET_PILE));
        //7 搬运
        if (StrUtil.equals(warehouseArea, WmsCarryTaskInCode.MJZ.getCode())) {
            CarryTask carryTask = routeFeignService.setCarryTask(containerNo, null, dpjLocationCfg.getAreaNo(), dpjLocationCfg.getLocationNo(), targetArea, null, String.valueOf(BaseTaskTypeConstants.IN_TYPE), 1, "空托垛入库", null, 10);
            routeFeignService.createCarryTask(carryTask);
        } else {
            routeFeignService.createCarryTask(containerNo, null, dpjLocationCfg.getAreaNo(), dpjLocationCfg.getLocationNo(), targetArea, null, String.valueOf(BaseTaskTypeConstants.IN_TYPE), 1, "空托垛入库", null);
        }
    }
    private String findContainer() {
        String containerNo = getContainerNo();
        List<ContainerInfo> containerInfos = containerInfoService.findByMap(MapUtils.put("containerNo", containerNo).getMap());
        if (CollectionUtils.isNotEmpty(containerInfos)) {
            findContainer();
        }
        return containerNo;
    }

    private String getContainerNo() {
        int num = (int) (Math.random() * 10000000);
        String numStr = String.format("%06d", num);
        return "9" + numStr;
    }
    //空托盘发往1楼叠盘机
    @Override
    public void sendEmptyToStacker() throws Exception{
        List<ContainerInfo> emptyContainerInfoList = containerInfoService.getEmptyContainerInfo(WmsCarryTaskInCode.MJZ.getCode());
        if (CollectionUtil.isEmpty(emptyContainerInfoList)) {
            return;
        }
        LocationCfg dpjLocationCfg = locationCfgService.findByMap(MapUtils.put("type", LocationCfgConstants.DPJ).getMap()).stream().findFirst().orElseThrow(() -> new PrologException("1楼叠盘机位置信息未配置"));
        //校验叠盘机是否故障
        if (dpjLocationCfg.getStatus() == 0) {
            log.warn("1F叠盘机不可用");
            return;
        }
        int toNum = dpjLocationCfg.getMaxCachePosition() - dpjLocationCfg.getCurNum();
        if (toNum <= 0) {
            return;
        }
        for (int i = 0; i < toNum; i++) {
            ContainerInfo containerInfo = emptyContainerInfoList.get(i);
            emptyContainerTaskService.save(containerInfo.getContainerNo(), LayerEnum.ONE,WmsCarryTaskInCode.MJZ);
            containerInfoService.updateTaskType(containerInfo.getId(), ContainerTaskTypeConstants.OUTBOUND);
        }
    }

    @Override
    public void sendStaionEmptyToStacker() {
        Criteria criteria = new Criteria(EmptyContainerTask.class);
        criteria.setOrder(Order.newInstance().asc(EmptyContainerTask::getCreateTime));
        List<EmptyContainerTask> emptyContainerTasks = emptyContainerTaskService.findByCriteria(criteria);
        if (CollectionUtil.isEmpty(emptyContainerTasks)) {
            return;
        }
        emptyContainerTasks.forEach(item->{
            try {
                sendEmptyToStacker(item);
            } catch (Exception e) {
                log.warn("空托发往叠盘机失败：" + e.getMessage());
            }
        });
    }

    @Transactional
    public void sendEmptyToStacker(EmptyContainerTask item) throws Exception {
        this.sendStackerTask(item.getContainerNo(), LayerEnum.of(item.getLayer()),item.getWmsLocationCode());
        emptyContainerTaskService.deleteById(item.getId());
    }
}
