/*
 *  Copyright 2019-2020  Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.youchain.basicdata.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.youchain.RequestData.TransTask;
import com.youchain.appupdate.ReturnJson.ReturnTaskVo;
import com.youchain.basicdata.domain.Area;
import com.youchain.basicdata.domain.Item;
import com.youchain.basicdata.domain.Point;
import com.youchain.basicdata.domain.Stock;
import com.youchain.basicdata.repository.AreaRepository;
import com.youchain.basicdata.repository.PointRepository;
import com.youchain.basicdata.service.ItemService;
import com.youchain.basicdata.vo.BarCodeVo;
import com.youchain.businessdata.domain.*;
import com.youchain.businessdata.repository.*;
import com.youchain.businessdata.service.*;
import com.youchain.businessdata.service.dto.TaskDto;
import com.youchain.exception.BadRequestException;
import com.youchain.modules.mnt.websocket.MsgType;
import com.youchain.modules.mnt.websocket.SocketMsg;
import com.youchain.modules.mnt.websocket.WebSocketServer;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.basicdata.repository.StockRepository;
import com.youchain.basicdata.service.StockService;
import com.youchain.basicdata.service.dto.StockDto;
import com.youchain.basicdata.service.dto.StockQueryCriteria;
import com.youchain.basicdata.service.mapstruct.StockMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;


/**
 * @author liuxue
 * @website <a href="https://eladmin.vip">...</a>
 * @description 服务实现
 * @date 2023-07-28
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class StockServiceImpl implements StockService {

    private final StockRepository stockRepository;
    private final PointRepository pointRepository;
    private final PickRepository pickRepository;
    private final PickDetailRepository pickDetailRepository;
    private final TaskRepository taskRepository;
    private final AgvTaskRepository agvTaskRepository;
    private final InventoryRepository inventoryRepository;
    private final AreaRepository areaRepository;
    private final AgvTaskService agvTaskService;
    private final ItemService itemService;
    private final ItemKeyService itemKeyService;
    private final InventoryLogService inventoryLogService;
    private final AsnDetailService asnDetailService;
    private final TaskService taskService;
    private final StockTypeToAreaMap stockTypeToAreaMap;
    private final BatchCreateOrUpdate batchCreateOrUpdate;
    private final StockMapper stockMapper;


    @Override
    public Map<String, Object> queryAll(StockQueryCriteria criteria, Pageable pageable) {
        Page<Stock> page = stockRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(stockMapper::toDto));
    }

    @Override
    public List<StockDto> queryAll(StockQueryCriteria criteria) {
        return stockMapper.toDto(stockRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    public List<Stock> findByKyStockList(String code) {
        return stockRepository.findByKyStockList(code);
    }

    @Override
    @Transactional
    public StockDto findById(Long id) {
        Stock stock = stockRepository.findById(id).orElseGet(Stock::new);
        ValidationUtil.isNull(stock.getId(), "Stock", "id", id);
        return stockMapper.toDto(stock);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StockDto create(Stock resources) {
        if (resources.getPoint() != null) {
            Point point = resources.getPoint();
            point.setStatus(BaseStatus.USED);
            pointRepository.save(point);
        }
        return stockMapper.toDto(stockRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Stock resources) {
        Stock stock = stockRepository.findById(resources.getId()).orElseGet(Stock::new);
        if (stock.getPoint() != null) {
            Point point = stock.getPoint();
            point.setStatus(BaseStatus.FREE);
            pointRepository.save(point);
        }
        if (resources.getPoint() != null) {
            Point point = resources.getPoint();
            point.setStatus(BaseStatus.USED);
            pointRepository.save(point);
        } else {
            resources.setPoint(null);
            stockRepository.save(resources);
        }
        ValidationUtil.isNull(stock.getId(), "Stock", "id", resources.getId());
        stock.copy(resources);
        stockRepository.save(stock);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            stockRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<StockDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (StockDto stock : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("代码", stock.getCode());
            map.put("类型", stock.getStockType());
            map.put("状态", stock.getStatus());
            map.put("启用", stock.getEnabled());
            map.put("地标", stock.getPoint()==null?null:stock.getPoint().getCode());
            list.add(map);
        }

        FileUtil.downloadExcel(list, response);
    }

    @Override
    public Stock toEntity(StockDto stockDto) {
        return stockMapper.toEntity(stockDto);
    }

    @Override
    public Stock findByCode(String code, String status) {
        return stockRepository.findByCode(code, status);
    }

    @Override
    public String getItemCode(String stockCode) {
        List<Inventory> inventoryList = stockRepository.findByinventoryList(stockCode);
        if (!inventoryList.isEmpty()) {
            return inventoryList.get(0).getItemKey().getItem().getCode();
        }
        return "";
    }

    @Override
    public synchronized String containerInJson(String containerCode, String position) {
        JSONObject jsonObject = new JSONObject(new LinkedHashMap<>());
        jsonObject.put("requestId", String.valueOf(new Date().getTime()));//请求 id
        jsonObject.put("containerType", "");//容器类型
        jsonObject.put("containerCode", containerCode);//容器编号
        jsonObject.put("position", position);//容器当前位置
        jsonObject.put("enterOrientation", "");//角度
        return jsonObject.toString();
    }

    @Override
    public synchronized String containerOutJson(String containerCode) {
        JSONObject jsonObject = new JSONObject(new LinkedHashMap<>());
        jsonObject.put("requestId", String.valueOf(new Date().getTime()));//请求 id
        jsonObject.put("containerType", "");//容器类型
        jsonObject.put("containerCode", containerCode);//容器编号
        jsonObject.put("position", "");//容器当前位置
        return jsonObject.toJSONString();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void containerIn(String containerCode, String position) {
        Stock stock = validateStock(containerCode);//验证容器
        Point point = validateSrcPoint(position);//验证点位
        if (!isRestrictedArea(point.getType())) {
            throw new BadRequestException(point.getCode() + "点位不能入场!");
        }

        validateAgvContainerIn(UrlApi.containerIn(), containerInJson(containerCode, position));//验证AGV返回信息


        //入空车
        handleEmptyContainer(stock, point);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void containerOut(String containerCode) {
        Stock stock = validateStock(containerCode);//验证容器
        validateAgvContainerOut(UrlApi.containerOut(), containerOutJson(stock.getCode()));//验证AGV返回信息
        clearInventory(stock);//清空库存
        Point point = stock.getPoint();
        if (point != null) {
            point.setStatus(BaseStatus.FREE);
            pointRepository.save(point);
        }
        stock.setStatus(BaseStatus.FREE);
        stock.setPoint(null);
        update(stock);
    }

    @Override
    public void operationFeedback(String code) {

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void materialIn(String stockCode, List<String> boxNumbers, String pointCode) {
        /**验证容器*/
        Stock stock = validateStock(stockCode);

        /**验证容器状态*/
        validateStockStatus(stock);

        /**验证箱条码集合*/
        validateBoxNumbers(boxNumbers);

        /**解析箱条码集合*/
        List<BarCodeVo> barCodeVos = parseBoxNumbers(boxNumbers);

        /**验证箱条码集合是否为同一物料,箱号是否重复*/
        validateItemCodeAndOrderNumber(barCodeVos);

        /**验证物料*/
        String firstItemCode = barCodeVos.iterator().next().getItemCode();
        Item item = validateItem(firstItemCode);

        /*容器类型：托盘*/
        validateStockType(stock.getStockType());

        /**验证起点点位*/
        Point srcPoint = validateSrcPoint(pointCode);

        /**验证起点点位状态*/
        checkPointStatus(srcPoint);

        /**验证起点点位是否在可入库区域*/
        validateSrcPointArea(srcPoint);


        /**验证终点点位*/
        String areaCode = stockTypeToAreaMap.getValueByKey(item.getGoodType());
        Point endPoint = validateEndPoint(areaCode);

        /**创建下发任务*/
        AgvTask agvTask = createAndSendAgvTask(stock, srcPoint, endPoint);

        /**创建ASN明细和ItemKey和Task任务*/
        createAsnDetailsAndItemKeysAndTasks(barCodeVos, item, stock, srcPoint, endPoint, agvTask);

        /**更新库存和点位状态*/
        updateStockAndPoints(stock, srcPoint, endPoint);

        agvTaskService.sendAgvTaskCCImpl(agvTask);//发送任务

    }

    private void validateBoxNumbers(List<String> boxNumbers) {
        if (boxNumbers.isEmpty()) {
            throw new BadRequestException("请扫描箱号!");
        }
    }

    private List<BarCodeVo> parseBoxNumbers(List<String> boxNumbers) {
        List<BarCodeVo> barCodeVos = new ArrayList<>();
        for (String mo : boxNumbers) {
            Map<String, String> map = parseString(mo);
            BarCodeVo barCodeVo = new BarCodeVo();
            try {
                barCodeVo.setMo(mo);
                barCodeVo.setItemCode(getStringCode(map.get("M")));
                barCodeVo.setPropC1(map.get("Lot"));
                barCodeVo.setBoxNumber(map.get("S"));
                barCodeVo.setPropC3(map.get("P"));
                barCodeVo.setPropD1(map.get("D"));
                barCodeVo.setOrderQty(parseQuantityString(map.get("Q")));
                barCodeVos.add(barCodeVo);
            } catch (Exception e) {
                throw new BadRequestException("箱号格式错误!");
            }
        }
        if (barCodeVos.isEmpty()) {
            throw new BadRequestException("请扫描箱号!");
        }
        return barCodeVos;
    }

    private void validateItemCodeAndOrderNumber(List<BarCodeVo> barCodeVos) {
        String firstItemCode = barCodeVos.iterator().next().getItemCode();
        boolean allSame = barCodeVos.stream().allMatch(vo -> vo.getItemCode().equals(firstItemCode));
        if (!allSame) {
            throw new BadRequestException("一个托盘只能放同一种物料!");
        }

        Set<String> barNumbers = barCodeVos.stream().map(BarCodeVo::getBoxNumber).collect(Collectors.toSet());
        List<AsnDetail> asnDetails = asnDetailService.existsByboxNumber(barNumbers);
        if (!asnDetails.isEmpty()) {
            Set<String> newBarNumbers = asnDetails.stream().map(AsnDetail::getOrderNumber).collect(Collectors.toSet());
            throw new BadRequestException("箱号已入库,请勿重复入库:" + newBarNumbers);
        }

    }

    private void validateStockType(String stockType) {
        if (!"托盘".equals(stockType)) {
            throw new BadRequestException(stockType + "容器类型错误!");
        }
    }

    private void validateSrcPointArea(Point srcPoint) {
        String srcPointAreaName = srcPoint.getArea().getCode();
        if (!isValidMCArea(srcPointAreaName)) {
            throw new BadRequestException(srcPoint.getCode() + "为" + srcPointAreaName + "不能入库!");
        }
    }

    private AgvTask createAndSendAgvTask(Stock stock, Point srcPoint, Point endPoint) {
        AgvTask agvTask = new AgvTask(BizStatus.ASN, stock.getCode(), srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "FORKLIFT_MOVE");
        agvTaskRepository.save(agvTask);
        return agvTask;
    }

    public void createAsnDetailsAndItemKeysAndTasks(List<BarCodeVo> barCodeVos, Item item, Stock stock, Point srcPoint, Point endPoint, AgvTask agvTask) {
        List<AsnDetail> createAsnDetails = new ArrayList<>();
        List<Task> createAsnTasks = new ArrayList<>();
        for (BarCodeVo barCodeVo : barCodeVos) {
            AsnDetail asnDetail = createAsnDetail(item, stock, srcPoint, barCodeVo.getPropC1(), barCodeVo.getBoxNumber(), barCodeVo.getPropC3(), Timestamp.valueOf(DateUtil.formatDateTime(DateUtil.parse(barCodeVo.getPropD1()))), barCodeVo.getOrderQty(), barCodeVo.getMo());
            createAsnDetails.add(asnDetail);
            ItemKey itemKey = itemKeyService.getItemKey(item, barCodeVo.getPropC1(), barCodeVo.getBoxNumber());
            Task task = createAsnTask(item, itemKey, asnDetail, stock, srcPoint, endPoint, agvTask);
            createAsnTasks.add(task);
        }

        if (!createAsnDetails.isEmpty()) {
            batchCreateOrUpdate.batchInsert(createAsnDetails);
        }

        if (!createAsnTasks.isEmpty()) {
            batchCreateOrUpdate.batchInsert(createAsnTasks);
        }


    }

    private AsnDetail createAsnDetail(Item item, Stock stock, Point srcPoint, String propC1, String boxNumber, String propC3, Timestamp propD1, double orderQty, String mo) {
        AsnDetail asnDetail = new AsnDetail();
        asnDetail.setItem(item);
        asnDetail.setLineNo(1L);
        asnDetail.setStatus(BizStatus.OPEN);
        asnDetail.setOrderQty(orderQty);
        asnDetail.setDept(item.getDept());
        asnDetail.setStock(stock);
        asnDetail.setPoint(srcPoint);
        asnDetail.setPropC1(propC1);
        asnDetail.setOrderNumber(boxNumber);
        asnDetail.setPropC3(propC3);
        asnDetail.setPropD1(propD1);
        asnDetail.setPo(mo);
        return asnDetail;
    }

    private Task createAsnTask(Item item, ItemKey itemKey, AsnDetail asnDetail, Stock stock, Point srcPoint, Point endPoint, AgvTask agvTask) {

        Task task = new Task();
        task.setItem(item);
        task.setItemKey(itemKey);
        task.getItemKey().setOrderNumber(asnDetail.getOrderNumber());
        task.setTaskType(BizStatus.ASN);
        task.setAsnDetail(asnDetail);
        task.setSrcStock(stock);
        task.setSrcPoint(srcPoint);
        task.setSrcStockCode(stock.getCode());
        task.setSrcPoint(srcPoint);
        task.setSrcPointCode(srcPoint == null ? null : srcPoint.getCode());
        task.setDstPoint(endPoint);
        task.setDstPointCode(endPoint == null ? null : endPoint.getCode());
        task.setTaskStatus(BizStatus.OPEN);
        task.setPlanQty(asnDetail.getOrderQty());
        task.setDept(item.getDept());
        task.setAgvTask(agvTask);
        return task;
    }

    private Map<String, String> parseString(String QRCode) {
        //P:工厂;V:客户编号;M:物料编号/PCS;B:BYD批次;Lot:供应商批次号;S:工单编号;PO:采购订单/箱号;Q:数量;D:生产日期;YX:有效天数;
        // 使用分号分隔字符串
        String[] pairs = QRCode.split(";");
        Map<String, String> resultMap = new HashMap<>();
        for (String pair : pairs) {
            // 使用冒号分隔键和值
            String[] keyValue = pair.split(":");
            if (keyValue.length == 2) {
                String key = keyValue[0].trim();
                String value = keyValue[1].trim();
                resultMap.put(key, value);
            }
        }
        return resultMap;
    }

    private String getStringCode(String str) {
        int index = str.indexOf('/');
        return str.substring(0, index);
    }

    public static int parseQuantityString(String str) {
        // 定义一个正则表达式来匹配第一个数字
        Pattern pattern = Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(str);

        // 查找第一个匹配的数字
        if (matcher.find()) {
            return Integer.parseInt(matcher.group());
        } else {
            throw new BadRequestException("数量格式错误!");
        }
    }

    private boolean isValidMCArea(String areaName) {
        return AreaNameDic.DXJRKQ.equals(areaName);
    }

    @Override
    public Map<String, Stock> findByCodes(Set stockCodes) {
        List<Stock> stockList = stockRepository.findByCodes(stockCodes);
        Map<String, Stock> stockMap = new HashMap<>();
        for (Stock stock : stockList) {
            stockMap.put(stock.getCode(), stock);
        }
        return stockMap;
    }

    @Override
    public ReturnTaskVo scanStock(String stockCode) {
        Task task = taskRepository.findBySrcStockCode(stockCode);
        if (task == null) {
            throw new BadRequestException("未找到" + stockCode + "托盘的翻包拣货任务!");
        }
        ReturnTaskVo returnTaskVo = new ReturnTaskVo();
        returnTaskVo.setTaskId(task.getId());
        returnTaskVo.setSrcOrderNumber(task.getItemKey().getOrderNumber());
        returnTaskVo.setItemCode(task.getItem().getCode());
        returnTaskVo.setItemName(task.getItem().getName());
        returnTaskVo.setSrcPointCode(task.getSrcPointCode());
        returnTaskVo.setDstPointCode(task.getDstPointCode());
        returnTaskVo.setPlanQty(task.getPlanQty());
        return returnTaskVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void fbPicking(Long taskId, String orderNumber, String dstStockCode) {
        /**验证任务*/
        Task task = validateTask(taskId, orderNumber);

        /**工单*/
        Pick pick = task.getPickDetail().getPick();

        /**验证目标容器*/
        Stock dstStock = validatedstStock(pick, dstStockCode);


        /**待拣货数量*/
        double moveQty = task.getPlanQty();

        /**库存移位*/
        moveInventory(task, dstStock, moveQty);


        /**刷新拣货状态*/
        refreshPickStatus(task, moveQty);

        /**原托盘回库内*/
        returnFBAgvTask(task.getDstPoint(), task.getSrcStock(), task.getItem());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void moveStock(TransTask transTask) {
        switch (transTask.getTaskType()) {
            case BizStatus.Cp_Off_Up:
                moveCpOffUp();//呼叫空货架
                break;
            case BizStatus.Cp_Off_Line:
                moveCpOffLine(transTask);//送满货架
                break;
            case BizStatus.Fx_Off_Line:
                moveFxOffLine(transTask);//滚筒
                break;
        }
    }


    private void moveCpOffUp() {
        // 尝试从成品入库缓存区查询空车
        List<Stock> emptyStockList = stockRepository.findByEnmptyStock(AreaNameDic.CPRKHCQ);
        if (emptyStockList.isEmpty()) {
            // 如果成品入库缓存区没有空车，则从入库区查询
            emptyStockList = stockRepository.findByEnmptyStock(AreaNameDic.CPRKQ);
            if (emptyStockList.isEmpty()) {
                throw new BadRequestException("成品入库区没有空车,请稍后再试!");
            }
        }

        Stock emptyStock = emptyStockList.get(0);
        Point srcPoint = emptyStock.getPoint();
        List<Point> endPointList = pointRepository.findByCode(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.CPXXQ, null, null);
        if (endPointList.isEmpty()) {
            throw new BadRequestException("成品下线区没有空闲点位,请稍后再试!");
        }
        Point endPoint = endPointList.get(0);
        // 创建并下发任务
        List<AgvTask> agvTaskList = agvTaskRepository.findRepeat(emptyStock.getCode());
        if (agvTaskList.isEmpty()) {
            AgvTask agvTask = new AgvTask(BizStatus.EMPTY_IN, emptyStock.getCode(), srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE");
            agvTaskRepository.save(agvTask);
            agvTaskService.sendAgvTaskHJImpl(agvTask);

            // 更新点位状态
            endPoint.setStatus(BaseStatus.USED);
            pointRepository.save(endPoint);
        }
    }

    private void moveCpOffLine(TransTask transTask) {
        Point srcPoint = validateSrcPoint(transTask.getPointCode());//起点
        Stock stock = stockRepository.fingByPointCode(srcPoint.getCode());
        if (stock == null) {
            throw new BadRequestException(srcPoint.getCode() + "点位没有货架!");
        }
        List<Point> endPointList = pointRepository.findByCode(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.CPRKQ, null, null);
        if (endPointList.isEmpty()) {
            throw new BadRequestException("成品入库区没有空闲点位!");
        }
        Point endPoint = endPointList.get(0);
        AgvTask agvTask = new AgvTask(BizStatus.Cp_Off_Line, stock.getCode(), srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE");
        agvTaskRepository.save(agvTask);
        agvTaskService.sendAgvTaskHJImpl(agvTask);
        endPoint.setStatus(BaseStatus.USED);
        pointRepository.save(endPoint);
        stock.setStatus(BaseStatus.USED);
        stockRepository.save(stock);
    }

    private void moveFxOffLine(TransTask transTask) {
        Point srcPoint = validateSrcPoint(transTask.getPointCode());//起点
        List<Point> pointList = pointRepository.findByCode(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.FXSXQ, null, null);
        if (pointList.isEmpty()) {
            throw new BadRequestException("返修上线区没有空闲点位!");
        }
        Point endPoint = pointList.get(0);
        AgvTask agvTask = new AgvTask(BizStatus.Fx_Off_Line, null, srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "ROLLER_MOVE");
        agvTask.setSlotCode(srcPoint.getName());
        agvTaskRepository.save(agvTask);
        agvTaskService.sendAgvTaskGTImpl(agvTask);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unBindContainer(String stockCode) {
        Stock stock = validateStock(stockCode);
        stock.setStatus(BaseStatus.FREE);
        stockRepository.save(stock);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callJlTask(Pick pick) {
        if (pick.getStock() == null) {
            throw new BadRequestException(pick.getGdCode() + "请翻包拣货!");
        }
        if (pick.getCallPoint() == null) {
            throw new BadRequestException(pick.getGdCode() + "工单没有叫料请求!");
        }
        Point srcPoint = pick.getStock().getPoint();
        Point endPoint = pick.getCallPoint();
        Stock stock = pick.getStock();
        AgvTask agvTask = new AgvTask(BizStatus.CALL_PICK, stock.getCode(), srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE");
        agvTask.setLineSlotCode(pick.getCode());
        agvTaskRepository.save(agvTask);
        agvTaskService.sendAgvTaskHJImpl(agvTask);
        srcPoint.setStatus(BaseStatus.USED);
        pointRepository.save(srcPoint);
        endPoint.setStatus(BaseStatus.USED);
        pointRepository.save(endPoint);
    }

    @Override
    public String stockMsg() {
        try {
            WebSocketServer.sendInfo(new SocketMsg("容器未解绑，请及时处理！", MsgType.INFO), "stock");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "ok";
    }

    /**
     * 验证任务
     *
     * @param taskId
     * @param orderNumber
     * @return
     */
    private Task validateTask(Long taskId, String orderNumber) {
        TaskDto taskDto = taskService.findById(taskId);
        if (taskDto == null) {
            throw new BadRequestException("未找到相应任务记录！");
        }
        Task task = taskService.toEntity(taskDto);//Dto转实体
        if (orderNumber.equals(task.getItemKey().getOrderNumber())) {
            return task;
        } else {
            //重新分配
            return reassignTask(task, orderNumber);
        }


    }

    /**
     * 重新分配任务
     */
    private Task reassignTask(Task task, String orderNumber) {
        Inventory dstinventory = inventoryRepository.findByOrderNumber(orderNumber, task.getSrcStockCode());
        if (dstinventory == null) {
            throw new BadRequestException(orderNumber + "箱号无库存，请更换箱号！");
        }
        double kyQty = dstinventory.getQuantity() - dstinventory.getQueuedQty();
        if (kyQty < task.getPlanQty()) {
            throw new BadRequestException("库存数量不足，请更换箱号！");
        }
        Inventory srcinventory = inventoryRepository.findById(task.getInvId()).get();
        srcinventory.setQueuedQty(srcinventory.getQueuedQty() - task.getPlanQty());
        inventoryRepository.save(srcinventory);
        dstinventory.setQueuedQty(dstinventory.getQueuedQty() + task.getPlanQty());
        inventoryRepository.save(dstinventory);
        task.setInvId(dstinventory.getId());
        task.setItemKey(dstinventory.getItemKey());
        taskRepository.save(task);

        return task;
    }

    /**
     * 验证目标容器
     *
     * @param dstStockCode
     * @return
     */
    private Stock validatedstStock(Pick pick, String dstStockCode) {
        if (StringUtils.isEmpty(dstStockCode)) {
            throw new BadRequestException("请扫描目标托盘号!");
        }
        Stock dstStock = stockRepository.findByCode(dstStockCode, null);
        if (dstStock == null) {
            throw new BadRequestException(dstStockCode + "目标托盘不存在,请扫描正确的托盘号!");
        }
        if (BaseStatus.USED.equals(dstStock.getStatus())) {
            throw new BadRequestException(dstStockCode + "目标托盘已使用,请更换新的托盘!");
        }
        if (dstStock.getPoint() == null) {
            throw new BadRequestException(dstStockCode + "目标托盘没有关联点位!");
        }
        if (pick.getStock() == null) {
            List<Inventory> inventoryList = inventoryRepository.findByStock(dstStock.getId());
            if (!inventoryList.isEmpty()) {
                throw new BadRequestException(dstStock + "目标托盘已绑定其他出库单,请更换其它的托盘!");
            }
            pick.setStock(dstStock);
            pickRepository.save(pick);
        } else if (!dstStockCode.equals(pick.getStock().getCode())) {
            throw new BadRequestException(pick.getGdCode() + "工单只能放入同一个货架! 请放入" + pick.getStock().getCode() + "货架!");
        }
        return dstStock;
    }

    private void moveInventory(Task task, Stock dstStock, double moveQty) {
        //将占用数量移位，原库存回库内
        Inventory inventory = inventoryRepository.findById(task.getInvId()).get();
        inventory.setQueuedQty(inventory.getQueuedQty() - moveQty);
        inventory.setQuantity(inventory.getQuantity() - moveQty);
        inventoryRepository.save(inventory);

        Inventory newInventory = new Inventory();
        newInventory.setItemKey(inventory.getItemKey());
        newInventory.setPoint(dstStock.getPoint());
        newInventory.setPointCode(dstStock.getPoint().getCode());
        newInventory.setStock(dstStock);
        newInventory.setStockCode(dstStock.getCode());
        newInventory.setQuantity(moveQty);
        newInventory.setQueuedQty(0d);
        newInventory.setDept(inventory.getDept());
        inventoryRepository.save(newInventory);
        task.setInvId(newInventory.getId());
        taskRepository.save(task);
        if (inventory.getQuantity() <= 0) {
            inventoryRepository.delete(inventory);
        }
        dstStock.setStatus(BaseStatus.USED);
        stockRepository.save(dstStock);
        //库存日志
        inventoryLogService.storeInventoryLog(BizStatus.MOVE, BizStatus.ADD, task.getPickDetail().getPo(), task.getItemKey(), task.getDstPoint(), dstStock.getPoint(), task.getSrcStock(), dstStock, 0d, moveQty, BizStatus.PICK, task.getId(), newInventory.getId(), "");

    }

    private void refreshPickStatus(Task task, double moveQty) {
        task.setTaskStatus(BizStatus.FINISH);
        task.setMoveQty(moveQty);
        taskRepository.save(task);

        PickDetail pickDetail = task.getPickDetail();
        pickDetail.setPickedQty(moveQty);
        pickDetailRepository.save(pickDetail);

        //查询出库单的所有明细
        Pick pick = pickDetail.getPick();
        List<PickDetail> allocatePickDetailList = pickDetailRepository.findByAllPickDetail(pick.getId());
        //判断是否全部拣货完成
        boolean allPICK_ALL = allocatePickDetailList.stream().allMatch(detail -> detail.getOrderQty().equals(detail.getPickedQty()));
        //判断是否有部分拣货完成
        boolean anyPICKUP = allocatePickDetailList.stream().anyMatch(detail -> detail.getOrderQty().equals(detail.getPickedQty()));
        // 当前状态
        String status = pick.getStatus();

        //更新状态且点位占用
        if (allPICK_ALL) {
            status = BizStatus.PICK_ALL;
        } else if (anyPICKUP) {
            status = BizStatus.PICKUP;
        }
        pick.setStatus(status);
        pickRepository.save(pick);
    }

    private void returnFBAgvTask(Point srcPoint, Stock stock, Item item) {
        String areaCode = stockTypeToAreaMap.getValueByKey(item.getGoodType());

        List<Point> endPointList = pointRepository.findByCode(null, BaseStatus.FREE, BaseStatus.STORAGE, areaCode, null, null);
        if (endPointList.isEmpty()) {
            throw new BadRequestException(areaCode + "没有空闲点位!");
        }
        Point endPoint = endPointList.get(0);

        AgvTask agvTask = new AgvTask(BizStatus.FB_RETURN, stock.getCode(), srcPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "FORKLIFT_MOVE");
        agvTaskRepository.save(agvTask);
        agvTaskService.sendAgvTaskCCImpl(agvTask);
        endPoint.setStatus(BaseStatus.USED);
        pointRepository.save(endPoint);
    }

    private Item validateItem(String itemCode) {
        Item item = itemService.existItem(itemCode);
        if (item == null) {
            throw new BadRequestException("请扫描正确的物料!");
        }
        return item;
    }

    private Stock validateStock(String stockCode) {
        Stock stock = findByCode(stockCode, null);
        if (stock == null) {
            throw new BadRequestException("请扫描正确的容器!");
        }
        return stock;
    }

    private void validateStockStatus(Stock stock) {
        if (BaseStatus.USED.equals(stock.getStatus())) {
            throw new BadRequestException("容器已使用,请更换新的容器!");
        }
    }

    private Point validateSrcPoint(String pointCode) {
        List<Point> pointList = pointRepository.findByCode(pointCode, null, null, null, null, null);
        if (pointList.isEmpty()) {
            throw new BadRequestException("请扫描正确的点位!");
        }
        return pointList.get(0);
    }

    private Point validateEndPoint(String areaCode) {
        List<Point> endPointList = pointRepository.findByCode(null, BaseStatus.FREE, BaseStatus.STORAGE, areaCode, null, null);
        if (endPointList.isEmpty()) {
            Area area = areaRepository.findByCode(areaCode);
            throw new BadRequestException(area.getName() + "没有空闲点位!");
        }
        return endPointList.get(0);
    }

    private void checkPointStatus(Point srcPoint) {
        if (BaseStatus.USED.equals(srcPoint.getStatus())) {
            throw new BadRequestException(srcPoint.getCode() + "点位已有任务，请勿重复操作!");
        }
    }


    private void updateStockAndPoints(Stock stock, Point srcPoint, Point endPoint) {
        stock.setStatus(BaseStatus.USED);
        stock.setPoint(srcPoint);
        stockRepository.save(stock);

        endPoint.setStatus(BaseStatus.USED);
        pointRepository.save(endPoint);

        srcPoint.setStatus(BaseStatus.USED);
        pointRepository.save(srcPoint);
    }


    /**
     * 验证AGV返回信息
     *
     * @param jsonObject
     */
    private void validateAgvContainerIn(String url, String jsonObject) {
        String resultJson = HttpPostUtil.sendPostReq(url, jsonObject);
        if (StringUtils.isEmpty(resultJson)) {
            throw new BadRequestException("AGV返回信息:容器入场接口调用失败!");
        }
        JSONObject resultObject = JSON.parseObject(resultJson);
        if (resultObject == null) {
            throw new BadRequestException("AGV返回信息:容器入场接口数据返回为空!");
        }
        String code = resultObject.getString("code");
        String message = resultObject.getString("message");
        if (!"0".equals(code)) {
            throw new BadRequestException("AGV返回信息:容器入场接口调用失败!" + message);
        }
    }

    private void validateAgvContainerOut(String url, String jsonObject) {
        String resultJson = HttpPostUtil.sendPostReq(url, jsonObject);
        if (StringUtils.isEmpty(resultJson)) {
            throw new BadRequestException("AGV返回信息:容器出场接口调用失败!");
        }
        JSONObject resultObject = JSON.parseObject(resultJson);
        if (resultObject == null) {
            throw new BadRequestException("AGV返回信息:容器出场接口数据返回为空!");
        }
        String code = resultObject.getString("code");
        String message = resultObject.getString("message");
        if (!"0".equals(code)) {
            throw new BadRequestException("AGV返回信息:容器出场接口调用失败!" + message);
        }
    }

    private boolean isRestrictedArea(String pointType) {
        return BaseStatus.BOX.equals(pointType);
    }


    /**
     * 清空库存
     *
     * @param stock
     */
    private void clearInventory(Stock stock) {
        List<Inventory> inventoryList = inventoryRepository.findByStock(stock.getId());
        inventoryRepository.deleteAll(inventoryList);
    }

    private void handleEmptyContainer(Stock stock, Point point) {
        point.setStatus(BaseStatus.USED);
        pointRepository.save(point);
        stock.setStatus(BaseStatus.FREE);
        stock.setPoint(point);
        stockRepository.save(stock);
    }

}
