package com.youchain.appupdate.service.impl;

import cn.hutool.core.date.DateUtil;
import com.youchain.appupdate.ReturnJson.ReturnTaskVo;
import com.youchain.appupdate.service.BydAppService;
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.StockRepository;
import com.youchain.basicdata.service.ItemService;
import com.youchain.basicdata.service.PointService;
import com.youchain.basicdata.service.StockService;
import com.youchain.basicdata.vo.BarCodeVo;
import com.youchain.businessdata.domain.*;
import com.youchain.businessdata.service.*;
import com.youchain.exception.BadRequestException;
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.sql.Timestamp;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class BydAppServiceImpl implements BydAppService {
    private final StockRepository stockRepository;
    private final StockService stockService;
    private final ItemService itemService;
    private final PointService pointService;
    private final AsnDetailService asnDetailService;
    private final ItemKeyService itemKeyService;
    private final TaskService taskService;
    private final InventoryLogService inventoryLogService;
    private final AgvTaskService agvTaskService;
    private final InventoryService inventoryService;
    private final KMReService kmReService;
    private final PickService pickService;
    private final StockTypeToAreaMap stockTypeToAreaMap;


    /**
     * 任务放行
     *
     * @param code 编号
     */
    @Override
    public void operationFeedback(String code) {

    }

    /**
     * 原材料入库
     *
     * @param stockCode  托盘
     * @param boxNumbers 箱号
     * @param pointCode  点位
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void materialIn(String stockCode, List<String> boxNumbers, String pointCode) {
        //验证容器
        Stock stock = stockService.validateStock(stockCode);

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

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

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

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

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

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

        switch (item.getGoodType()) {
            case BizStatus.XJ:
                xjMaterialIn(stock, pointCode, item, barCodeVos);
                break;
            case BizStatus.DJ:
                djMaterialIn(stock, pointCode, item, barCodeVos);
                break;
            default:
                throw new BadRequestException(item.getCode() + "物料类型不正确,请检查");
        }


    }

    private void djMaterialIn(Stock stock, String pointCode, Item item, List<BarCodeVo> barCodeVos) {
        //验证终点；大件入库只能扫描大件存储区的点位
        Point endPoint = pointService.validatePoint(pointCode);
        if (!AreaNameDic.DJQ.equals(endPoint.getArea().getName())) {
            throw new BadRequestException(pointCode + "不是大件存储区中的点位,请检查");
        }
        //验证终点状态
        pointService.validatePointStatus(endPoint);

        //清空库存
        inventoryService.clearInventory(stock);

        //创建ASN明细和ItemKey和Task任务库存
        for (BarCodeVo barCodeVo : barCodeVos) {

            AsnDetail asnDetail = asnDetailService.createAsnDetail(item, stock, null, barCodeVo.getPropC1(), barCodeVo.getBoxNumber(), barCodeVo.getPropC3(), barCodeVo.getPropD1() == null ? null : Timestamp.valueOf(DateUtil.formatDateTime(DateUtil.parse(barCodeVo.getPropD1()))), barCodeVo.getOrderQty(), barCodeVo.getMo());
            if (asnDetail != null) {
                asnDetail.setReceivedQty(asnDetail.getOrderQty());
                asnDetail.setStatus(BizStatus.RECEIVED);
                asnDetailService.update(asnDetail);
            }

            ItemKey itemKey = itemKeyService.createItemKey(item, barCodeVo.getPropC1(), asnDetail.getPropD1(), barCodeVo.getBoxNumber());

            Task task = taskService.createTask(item, asnDetail.getOrderQty(), itemKey, asnDetail, BizStatus.ASN, null, null, stock, null, endPoint, null);
            if (task != null) {
                task.setMoveQty(task.getPlanQty());
                task.setTaskStatus(BizStatus.FINISH);
                taskService.update(task);
            }

            //新增库存
            Inventory inventory = inventoryService.createInventory(itemKey, endPoint, stock, stock.getDept(), asnDetail.getOrderQty());

            //点位占用
            pointService.usedPoint(endPoint);

            //占用容器
            stockService.usedStock(stock, endPoint, BaseStatus.USED);

            //新增日志
            inventoryLogService.createInventoryLog(BizStatus.RECEIVING_UP, BizStatus.ADD, null, itemKey, null, endPoint, stock, stock, 0d, asnDetail.getOrderQty(), BizStatus.ASN, asnDetail.getId(), inventory.getId(), asnDetail.getRemark());

        }


    }

    private void xjMaterialIn(Stock stock, String pointCode, Item item, List<BarCodeVo> barCodeVos) {

        //验证起点点位
        Point srcPoint = pointService.validatePoint(pointCode);

        //验证起点点位状态
        pointService.validatePointStatus(srcPoint);

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

        //验证终点点位
        String areaName = stockTypeToAreaMap.getValueByKey(item.getGoodType());
        List<Point> pointList = pointService.queryPoints(null, BaseStatus.FREE, BaseStatus.STORAGE, areaName);
        if (pointList.isEmpty()) {
            throw new BadRequestException(areaName + "没有空闲的点位");
        }
        Point endPoint = pointList.get(0);

        //创建下发任务
        AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.ASN, stock, srcPoint.getCode(), endPoint.getCode(), "FORKLIFT_MOVE");

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

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

        //发送任务
        kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskCcJson(agvTask));
    }

    @Override
    public ReturnTaskVo scanStock(String stockCode) {
        List<Task> taskList = taskService.findTaskByContains(BizStatus.PICK, BizStatus.ARRIVED, stockCode);
        if (taskList.isEmpty()) {
            throw new BadRequestException("未找到" + stockCode + "托盘的翻包拣货任务!");
        }
        Task task = taskList.get(0);
        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() - task.getMoveQty());
        return returnTaskVo;
    }

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

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

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

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

        //刷新出库单状态
        pickService.refreshPickStatus(task.getPickDetail().getPick());

        //成品原托盘返库；单品不回库
        if (BaseStatus.GD_TYPE_CT.equals(pick.getOrderType())) {
            this.returnXJFBAgvTask(task);
        } else if (BaseStatus.GD_TYPE_DP.equals(pick.getOrderType())) {
            this.returnDJFBAgvTask(task);
        }
    }

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

    /**
     * 验证箱条码集合
     *
     * @param boxNumbers 箱条码集合
     */
    private void validateBoxNumbers(List<String> boxNumbers) {
        if (boxNumbers.isEmpty()) {
            throw new BadRequestException("请扫描箱号!");
        }
    }

    /**
     * 解析箱条码集合
     *
     * @param boxNumbers 箱条码集合
     */
    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;
    }

    /**
     * 解析条形码
     *
     * @param QRCode 条形码
     */
    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;
    }

    /**
     * 获取字符串中的前部分
     *
     * @param str 字符串
     */
    private String getStringCode(String str) {
        int index = str.indexOf('/');
        return str.substring(0, index);
    }

    /**
     * 获取数量
     *
     * @param str 字符串
     */
    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("数量格式错误!");
        }
    }

    /**
     * 验证箱条码集合是否为同一物料,箱号是否重复
     *
     * @param 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);
        }

    }

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

    /**
     * 验证起点点位是否在可入库区域
     *
     * @param srcPoint 起点点位
     */
    private void validateSrcPointArea(Point srcPoint) {
        String srcPointAreaName = srcPoint.getArea().getName();
        if (!AreaNameDic.DXJRKQ.equals(srcPointAreaName)) {
            throw new BadRequestException(srcPoint.getCode() + "不是小件入库点位，请检查");
        }
    }

    public void createAsnDetailsAndItemKeysAndTasks(List<BarCodeVo> barCodeVos, Item item, Stock stock, Point srcPoint, Point endPoint, AgvTask agvTask) {
        for (BarCodeVo barCodeVo : barCodeVos) {
            AsnDetail asnDetail = asnDetailService.createAsnDetail(item, stock, srcPoint, barCodeVo.getPropC1(), barCodeVo.getBoxNumber(), barCodeVo.getPropC3(), barCodeVo.getPropD1() == null ? null : Timestamp.valueOf(DateUtil.formatDateTime(DateUtil.parse(barCodeVo.getPropD1()))), barCodeVo.getOrderQty(), barCodeVo.getMo());
            ItemKey itemKey = itemKeyService.createItemKey(item, barCodeVo.getPropC1(), asnDetail.getPropD1(), barCodeVo.getBoxNumber());
            Task task = taskService.createTask(item, asnDetail.getOrderQty(), itemKey, asnDetail, BizStatus.ASN, null, null, stock, srcPoint, endPoint, agvTask);
        }
    }

    private void updateStockAndPoints(Stock stock, Point srcPoint, Point endPoint) {
        //容器占用
        stockService.usedStock(stock, srcPoint, BaseStatus.USED);

        //起点占用
        pointService.usedPoint(srcPoint);

        //终点占用
        pointService.usedPoint(endPoint);

    }

    /**
     * 验证任务
     *
     * @param taskId      任务Id
     * @param orderNumber 箱号
     */
    private Task validateTask(Long taskId, String orderNumber, Double moveQty) {
        Task task = taskService.findById(taskId);
        if (task == null) {
            throw new BadRequestException("未找到相应任务记录！");
        }
        if (moveQty > task.getPlanQty() - task.getMoveQty()) {
            throw new BadRequestException("实际拣货数量不能大于待拣货数量！");
        }
        if (orderNumber.equals(task.getItemKey().getOrderNumber())) {
            return task;
        } else {
            //重新分配
            return taskService.reassignTask(task, orderNumber);
        }
    }


    /**
     * 验证目标容器
     *
     * @param dstStockCode 容器
     */
    private Stock validatedstStock(Task task, Pick pick, String dstStockCode) {
        if (StringUtils.isEmpty(dstStockCode)) {
            throw new BadRequestException("请扫描目标托盘号!");
        }
        //验证容器
        Stock dstStock = stockService.findByCode(dstStockCode);

        if (dstStock.getPoint() == null) {
            throw new BadRequestException(dstStockCode + "目标托盘没有关联点位!");
        }

        //验证目标托盘是否在翻包存储区
        String itemType = task.getItemKey().getItem().getGoodType();
        String areaName = "大件".equals(itemType) ? AreaNameDic.DJFBCQ : AreaNameDic.XJFBCQ;
        if (!areaName.equals(dstStock.getPoint().getArea().getName())) {
            throw new BadRequestException(dstStockCode + "目标托盘没有在" + areaName);
        }

/*        if (BizStatus.XJ.equals(task.getItemKey().getItem().getGoodType())) {
            if (!dstStockCode.equals(pick.getStock())) {
                throw new BadRequestException(pick.getGdCode() + "工单只能放入同一个货架! 请放入" + pick.getCode() + "货架!");
            }
        }*/
        return dstStock;
    }

    /**
     * AGV中的任务都拣货完成托盘回库
     *
     * @param task 任务
     */
    private void returnXJFBAgvTask(Task task) {
        double planQty = 0;
        double moveQty = 0;
        List<Task> taskList = taskService.findTaskByAgvTask(task.getAgvTask().getId());
        for (Task task1 : taskList) {
            planQty += task1.getPlanQty();
            moveQty += task1.getMoveQty();
        }
        if (planQty == moveQty) {
            //成品当前托盘没有库存则不需要回库;
            List<Inventory> inventoryList = inventoryService.findByStockCode(task.getSrcStock().getCode());
            List<Inventory> smallAreaInventories = inventoryList.stream()
                    .filter(inv -> {
                        if (inv.getPoint() != null && inv.getPoint().getArea() != null) {
                            String areaName = inv.getPoint().getArea().getName();
                            return AreaNameDic.XJQ.equals(areaName) || AreaNameDic.XJFBQ.equals(areaName);
                        }
                        return false;
                    })
                    .collect(Collectors.toList());
            if (!smallAreaInventories.isEmpty()) {
                kmReService.returnFBAgvTask(task.getSrcStock().getPoint(), task.getSrcStock(), task.getItem());
            }

        }
    }

    /**
     * AGV中的任务都拣货完成托盘回库
     *
     * @param task 任务
     */
    private void returnDJFBAgvTask(Task task) {
        double planQty = 0;
        double moveQty = 0;
        List<Task> taskList = taskService.findTaskByAgvTask(task.getAgvTask().getId());
        for (Task tasks : taskList) {
            planQty += tasks.getPlanQty();
            moveQty += tasks.getMoveQty();
        }
        if (planQty == moveQty) {
            //查询大件返空区的空闲点位
            List<String> areaNames = Collections.singletonList(AreaNameDic.DJFKQ);
            List<Point> points = pointService.queryByAreaNames(areaNames, BaseStatus.FREE);
            if (!points.isEmpty()) {
                Point endPoint = points.get(0);
                // 没有空车的处理逻辑
                if (agvTaskService.isEndPointAvailable(endPoint.getCode(), BizStatus.FB_RETURN, "FORKLIFT_MOVE")) {
                    // 生成agv任务进行等待
                    AgvTask agvTask = agvTaskService.createAgvTask(BizStatus.FB_RETURN, task.getSrcStock(), task.getSrcStock().getPoint().getCode(), endPoint.getCode(), "FORKLIFT_MOVE");
                    pointService.usedPoint(endPoint);
                    kmReService.sendAgvTask(agvTask, kmReService.sendAgvTaskCcJson(agvTask));
                }
            }
        }
    }


}
