package com.youchain.businessdata.service.impl;

import com.youchain.RequestData.ItemDate;
import com.youchain.RequestData.TransTask;
import com.youchain.RequestData.Yclbl;
import com.youchain.basicdata.domain.Item;
import com.youchain.basicdata.domain.Point;
import com.youchain.basicdata.domain.Stock;
import com.youchain.basicdata.repository.ItemRepository;
import com.youchain.basicdata.service.PointService;
import com.youchain.basicdata.service.StockService;
import com.youchain.businessdata.domain.AgvTask;
import com.youchain.businessdata.domain.Pick;
import com.youchain.businessdata.domain.PickDetail;
import com.youchain.businessdata.domain.Task;
import com.youchain.businessdata.repository.PickDetailRepository;
import com.youchain.businessdata.repository.PickRepository;
import com.youchain.businessdata.service.AgvTaskService;
import com.youchain.businessdata.service.MesService;
import com.youchain.businessdata.service.TaskService;
import com.youchain.exception.BadRequestException;
import com.youchain.modules.system.service.DeptService;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class MesServiceImpl implements MesService {

    private final PickRepository pickRepository;
    private final PickDetailRepository pickDetailRepository;
    private final ItemRepository itemRepository;
    private final StockService stockService;
    private final PointService pointService;
    private final TaskService taskService;
    private final AgvTaskService agvTaskService;
    private final KMReServiceImpl kmReService;
    private final DeptService deptService;
    private final GtLineMap gtLineMap;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void materialBl(Yclbl yclbl) {
        //验证参数有效性
        validatePick(yclbl);

        //验证出库单是否存在
        checkIfGdExists(yclbl);

        //验证物料是否存在
        Map<String, Item> itemMap = checkIfItemExists(yclbl);

        List<Pick> createPicks = new ArrayList<>();
        List<PickDetail> createPickDetails = new ArrayList<>();
        AtomicInteger lineNo = new AtomicInteger(1);

        String orderNo = yclbl.getOrderNo();
        String taskCode = yclbl.getTaskCode();

        //遍历成品集合
        yclbl.getBlzc().forEach(zcData -> {
            Pick pick = createPick(orderNo, taskCode, lineNo.getAndIncrement(), zcData.getCompleteCode(), zcData.getStation(), BaseStatus.GD_TYPE_CT);
            createPicks.add(pick);
            zcData.getBlzcmx().forEach(itemDate -> {
                Item item = itemMap.get(itemDate.getItemCode());
                PickDetail pickDetail = createPickDetail(pick, item, BaseStatus.GD_TYPE_CT, itemDate.getItemQty(), zcData.getCompleteQty());
                createPickDetails.add(pickDetail);
            });
        });

        //遍历单品品集合
        yclbl.getBlzcmx().forEach(itemDate -> {
            Pick pick = createPick(orderNo, taskCode, lineNo.getAndIncrement(), itemDate.getItemCode(), itemDate.getStation(), BaseStatus.GD_TYPE_DP);
            createPicks.add(pick);
            Item item = itemMap.get(itemDate.getItemCode());
            PickDetail pickDetail = createPickDetail(pick, item, BaseStatus.GD_TYPE_DP, itemDate.getItemQty(), null);
            createPickDetails.add(pickDetail);
        });

        // 批量插入
        if (!createPicks.isEmpty()) {
            pickRepository.saveAll(createPicks);
        }

        if (!createPickDetails.isEmpty()) {
            pickDetailRepository.saveAll(createPickDetails);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void itemCall(String gdNo, String itemCode, String station, String pointCode) {
        Pick pick = pickRepository.findByPick(gdNo, itemCode, station);
        if (pick == null) {
            throw new BadRequestException(gdNo + "工单未备料，叫料失败!");
        }

        Point point = pointService.queryPoint(pointCode, null, null, null);
        if (point == null) {
            throw new BadRequestException(pointCode + "点位不存在或已失效!");
        }

        if (pick.getIsCall()) {
            throw new BadRequestException(gdNo + "工单已叫料，请勿重复叫料!");
        }

        pick.setIsCall(true);
        pick.setCallPoint(point);
        pickRepository.save(pick);

        taskService.callEmptyStock(BaseStatus.GD_TYPE_DP);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bigItemCall(String gdNo, String completeCode, String station, String pointCode) {
        Pick pick = pickRepository.findByPick(gdNo, completeCode, station);
        if (pick == null) {
            throw new IllegalArgumentException(gdNo + "工单未备料，叫料失败!");
        }

        Point point = pointService.queryPoint(pointCode, null, null, null);
        if (point == null) {
            throw new BadRequestException(pointCode + "点位不存在或已失效!");
        }

        if (pick.getIsCall()) {
            throw new BadRequestException(gdNo + "工单已叫料，请勿重复叫料!");
        }
        pick.setIsCall(true);
        pick.setCallPoint(point);
        pickRepository.save(pick);
        taskService.callEmptyStock(BaseStatus.GD_TYPE_CT);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void performTransportTask(TransTask transTask) {
        switch (transTask.getTaskType()) {
            case BizStatus.Cp_Off_Up:
                moveCpOffUp(transTask.getPointCode());//呼叫空货架
                break;
            case BizStatus.Cp_Off_Line:
                moveCpOffLine(transTask);//送满货架
                break;
            case BizStatus.Fx_Off_Line:
                moveFxOffLine(transTask);//返修呼叫空托盘、返修下线
                break;
            default:
                throw new BadRequestException("任务类型错误!");
        }
    }

    /**
     * 验证参数有效性
     *
     * @param yclbl 备料参数
     */
    private void validatePick(Yclbl yclbl) {
        if (StringUtils.isEmpty(yclbl.getOrderNo())) {
            throw new IllegalArgumentException("工单号必填!");
        }
        if (StringUtils.isEmpty(yclbl.getTaskCode())) {
            throw new IllegalArgumentException("任务号必填!");
        }

        if (yclbl.getBlzc().isEmpty() && yclbl.getBlzcmx().isEmpty()) {
            throw new IllegalArgumentException("成品明细和单品明细至少一项必填!");
        }
    }

    /**
     * 验证出库单是否存在
     *
     * @param yclbl -> 备料参数
     */
    private void checkIfGdExists(Yclbl yclbl) {
        List<Pick> pick = pickRepository.findByPickGdCode(yclbl.getOrderNo());
        if (!pick.isEmpty()) {
            throw new IllegalArgumentException(yclbl.getOrderNo() + "出库单已存在!");
        }
    }

    /**
     * 验证物料是否存在
     *
     * @param yclbl 备料参数
     */
    private Map<String, Item> checkIfItemExists(Yclbl yclbl) {
        // 获取所有成品和单品的itemCode
        Set<String> itemCodes = yclbl.getBlzc().stream().flatMap(zcData -> zcData.getBlzcmx().stream()).map(ItemDate::getItemCode).collect(Collectors.toSet());

        itemCodes.addAll(yclbl.getBlzcmx().stream().map(ItemDate::getItemCode).collect(Collectors.toSet()));

        // 查找数据库中存在的items
        List<Item> items = itemRepository.findByCodes(itemCodes, null);
        Set<String> newItemCodes = items.stream().map(Item::getCode).collect(Collectors.toSet());

        // 取itemCodes和newItemCodes的差集，说明有不存在的物料
        Set<String> diffItemCodes = itemCodes.stream().filter(code -> !newItemCodes.contains(code)).collect(Collectors.toSet());

        if (!diffItemCodes.isEmpty()) {
            throw new IllegalArgumentException("WMS不存在的物料集合,请维护：" + diffItemCodes);
        }

        // 构建itemCode到Item的映射
        return items.stream().collect(Collectors.toMap(Item::getCode, item -> item));
    }

    public Pick createPick(String gdCode, String taskCode, int lineNo, String cpCodeOrpCode, String station, String orderType) {
        Pick pick = new Pick();
        pick.setTaskCode(taskCode);
        pick.setLineNo(lineNo);
        pick.setCode(gdCode + "_" + lineNo);
        pick.setGdCode(gdCode);
        pick.setStation(station);
        pick.setCpCodeOrDpCode(cpCodeOrpCode);
        pick.setStatus(BizStatus.OPEN);
        pick.setIsCall(false);
        pick.setDept(deptService.getDefaultDept());
        pick.setOrderType(orderType);
        return pick;
    }

    public PickDetail createPickDetail(Pick pick, Item item, String orderType, Double itemQty, Double bigItemQty) {
        AtomicInteger lineNo = new AtomicInteger(1);
        PickDetail pickDetail = new PickDetail();
        pickDetail.setPick(pick);
        pickDetail.setItem(item);
        pickDetail.setLineNo(lineNo.getAndIncrement());
        pickDetail.setStatus(BizStatus.OPEN);
        if (BaseStatus.GD_TYPE_CT.equals(orderType)) {
            pickDetail.setOrderQty(itemQty * bigItemQty);
        } else {
            pickDetail.setOrderQty(itemQty);
        }
        pickDetail.setDept(pick.getDept());
        return pickDetail;
    }

    private synchronized void moveCpOffUp(String pointCode) {
        // 从成品入库缓存区查询空车；没有在从成品入库区查询空车；
        List<Stock> emptyStockList = stockService.findByFreeOrUsedStock(AreaNameDic.CPRKHCQ, BaseStatus.FREE);
        if (emptyStockList.isEmpty()) {
            emptyStockList = stockService.findByFreeOrUsedStock(AreaNameDic.CPRKQ, BaseStatus.FREE);
            if (emptyStockList.isEmpty()) {
                throw new BadRequestException("成品入库区、成品入库缓存区都没有空车,请稍后再试!");
            }
        }
        //容器
        Stock emptyStock = emptyStockList.get(0);
        //起点
        Point srcPoint = emptyStock.getPoint();
        //终点
        Point endPoint = pointService.validatePoint(pointCode);
        //验证终点是否有任务
        if (!agvTaskService.findByEndSlotCode(endPoint.getCode(), BizStatus.Cp_Off_Up, "RACK_MOVE")) {
            // 直接在条件判断为false时抛出异常，表示该点位有任务
            throw new BadRequestException(endPoint.getCode() + "点位有任务，请稍后再试!");
        }
        //创建Agv任务
        AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.Cp_Off_Up, emptyStock, srcPoint.getCode(), endPoint.getCode(), "RACK_MOVE");
        // 更新点位状态
        pointService.usedPoint(endPoint);
        //下发任务
        kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskHjJson(agvTask));
    }

    private synchronized void moveCpOffLine(TransTask transTask) {
        //查询下线缓存区是否有满货架；
        List<Stock> fullStockList = stockService.findByFreeOrUsedStock(AreaNameDic.CPXXHC, BaseStatus.USED);
        if (!fullStockList.isEmpty()) {
            List<Point> endPointList = pointService.queryPoints(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.CPRKQ);
            if (!endPointList.isEmpty()) {
                //容器
                Stock fullStock = fullStockList.get(0);
                //起点
                Point srcPoint = fullStock.getPoint();
                //终点
                Point endPoint = endPointList.get(0);
                //验证终点是否有任务
                if (agvTaskService.findByEndSlotCode(endPoint.getCode(), BizStatus.Cp_Off_Line, "RACK_MOVE")) {
                    //创建Agv任务
                    AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.Cp_Off_Line, fullStock, srcPoint.getCode(), endPoint.getCode(), "RACK_MOVE");
                    // 更新点位状态
                    pointService.usedPoint(endPoint);
                    //占用容器
                    stockService.usedStock(fullStock, srcPoint, BaseStatus.USED);
                    //下发任务
                    kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskHjJson(agvTask));
                }
            }
        }

        //下线缓存区没有满货架，直接下发当前任务
        Point srcPoint = pointService.validatePoint(transTask.getPointCode());
        //容器
        Stock stock = stockService.findByPointCode(srcPoint.getCode());
        if (stock == null) {
            throw new BadRequestException(srcPoint.getCode() + "点位没有货架!");
        }
        List<Point> endPointList = pointService.queryPoints(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.CPRKQ);
        if (endPointList.isEmpty()) {
            endPointList = pointService.queryPoints(null, BaseStatus.FREE, BaseStatus.BOX, AreaNameDic.CPXXHC);
            if (endPointList.isEmpty()) {
                throw new BadRequestException("成品入库区和成品下线缓存区都没有空闲点位!");
            }
        }
        //终点
        Point endPoint = endPointList.get(0);
        //验证终点是否有任务
        if (!agvTaskService.findByEndSlotCode(endPoint.getCode(), BizStatus.Cp_Off_Line, "RACK_MOVE")) {
            // 直接在条件判断为false时抛出异常，表示该点位有任务
            throw new BadRequestException(endPoint.getCode() + "点位有任务，请稍后再试!");
        }

        //创建Agv任务
        AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.Cp_Off_Line, stock, srcPoint.getCode(), endPoint.getCode(), "RACK_MOVE");
        // 更新点位状态
        pointService.usedPoint(endPoint);
        //占用容器
        stockService.usedStock(stock, srcPoint, BaseStatus.USED);
        //下发任务
        kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskHjJson(agvTask));
    }

    private void moveFxOffLine(TransTask transTask) {
        //返修上线起点
        String srcPointCode = transTask.getPointCode();
        //终点
        String dstPointCode;
        //验证点位是否为上线点
        if (gtLineMap.isOnlinePoint(srcPointCode)) {
            if ("BYDGT002".equals(srcPointCode)) {
                dstPointCode = "BYDGT006";
            } else {
                dstPointCode = gtLineMap.getKeyByValue(srcPointCode);
            }
        } else if (gtLineMap.isOfflinePoint(srcPointCode)) {
            dstPointCode = gtLineMap.getValueByKey(srcPointCode);
        } else {
            throw new BadRequestException(srcPointCode + "点位不存在,请核实");
        }


        // 验证终点是否有任务
        if (!agvTaskService.findByEndSlotCode(dstPointCode, BizStatus.Fx_Off_Line, "ROLLER_MOVE")) {
            throw new BadRequestException(dstPointCode + "点位有任务，请稍后再试!");
        }

        // 生成任务
        AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.Fx_Off_Line, null, srcPointCode, dstPointCode, "ROLLER_MOVE");

        // 下发任务
        kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskGtJson(agvTask));

    }
}
