package org.wcs.business.stock.impl;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.wcs.annotation.ScanMethodTag;
import org.wcs.business.pub.ReportStandard;
import org.wcs.business.stock.intf.IScanMethod;
import org.wcs.constant.ConstantData;
import org.wcs.constant.enums.common.TrueOrFalseEnum;
import org.wcs.constant.enums.database.StackerLocationStatusEnum;
import org.wcs.constant.enums.database.StockComposeTaskStatusEnum;
import org.wcs.constant.enums.database.StockComposeTaskStepStatusEnum;
import org.wcs.constant.enums.database.StockComposeTaskTypeEnum;
import org.wcs.helper.PlcTaskIdHelper;
import org.wcs.mapper.intf.AppStackerLocationService;
import org.wcs.mapper.intf.AppStockComposeTaskService;
import org.wcs.model.bo.tuple.Tuple2;
import org.wcs.model.dto.client.StockLoginDataResp;
import org.wcs.model.dto.client.StockLoginReq;
import org.wcs.model.dto.client.UpperSystemDataResponse;
import org.wcs.model.po.app.AppStackerLocation;
import org.wcs.model.po.app.AppStockComposeTask;
import org.wcs.model.po.app.AppStockScan;
import org.wcs.plugin.plc.PlcCommunicationFactory;
import org.wcs.plugin.plc.model.ScanInfo;
import org.wcs.plugin.plc.model.ScanTask;
import org.wcs.plugin.webHttpClient.WebHttpClient;
import org.wcs.plugin.webHttpClient.model.HttpRequest;
import org.wcs.plugin.webHttpClient.model.HttpResponse;
import org.wcs.utils.AppStringUtils;
import org.wcs.utils.AppUUIDUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 扫码方法
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScanMethod implements IScanMethod {

    private final PlcTaskIdHelper plcTaskIdHelper;
    private final AppStockComposeTaskService stockComposeTaskService;
    private final AppStackerLocationService stackerLocationService;

    /**
     * 注册堆垛机任务，仅注册，成功则继续执行，失败则退回，成功写1，失败写2
     * @param stockScan 扫码器信息
     * @param scanInfo 扫码信息
     */
    @Override
    @ScanMethodTag("LOGIN_STACKER")
    public void loginStackerTask(AppStockScan stockScan, ScanInfo scanInfo) {
        if(stockScan == null || scanInfo == null) {
            log.info("堆垛机 注册扫码器信息数据为空");
            return;
        }
        if(scanInfo.getTag() != TrueOrFalseEnum.TRUE.getCode()) {
            return;
        }
        log.info("注册扫码器信息: {}，获得扫码数据：{}", stockScan, scanInfo);
        // 检查是否扫码失败
        String code = scanInfo.getVehicleNo();
        if(AppStringUtils.isEmpty(code)) {
            return;
        }
        code = code.trim().replaceAll("\\W+", ""); // 获取条码简单处理
        // 读码失败
        if(code.equals(ConstantData.NO_READ)) {
            log.info("扫码：{}，扫码失败，条码：{}",stockScan.getScanId(), code);
            writeSimpleScanTask(stockScan, (short)2);
            return;
        }
        // 读码成功
        // 检查是否有该条码的任务
        AppStockComposeTask stockComposeTaskCheck = new AppStockComposeTask();
        stockComposeTaskCheck.setVehicleNo(code);
        List<AppStockComposeTask> stockComposeTaskCheckList = stockComposeTaskService.query(stockComposeTaskCheck);
        if(stockComposeTaskCheckList == null) {
            log.info("扫码：{}，检索堆垛机任务失败，条码：{}",stockScan.getScanId(), code);
            writeSimpleScanTask(stockScan, (short)2);
            return;
        }
        // -- 找出是否存在未执行的堆垛机入库任务
        List<AppStockComposeTask> stockComposeTasks = stockComposeTaskCheckList.stream().filter(task -> StockComposeTaskStatusEnum.getAllNotExecuteStatus().contains(task.getTaskStatus()) &&
                (task.getTaskType().equals(StockComposeTaskTypeEnum.AUTO.getCode()) || task.getTaskType().equals(StockComposeTaskTypeEnum.IN.getCode()))).toList();
        if(!stockComposeTasks.isEmpty()) {
            // 存在任务，直接放行
            log.info("箱号：{} 在注册位置：{}，存在任务，直接放行", code, stockScan.getScanId());
            writeSimpleScanTask(stockScan, (short)1);
            return;
        }
        // 请求上位系统获取任务
        Tuple2<String, StockLoginDataResp> stringStockLoginDataResp = ReportStandard.stockLoginRequest(stockScan.getParam1(), code, stockScan.getScanId(), Integer.valueOf(scanInfo.getVehicleSize()), scanInfo.getWeight(), scanInfo.getLength(), scanInfo.getWidth(), scanInfo.getHeight());
        if(AppStringUtils.isNotEmpty(stringStockLoginDataResp.getItem1()) || stringStockLoginDataResp.getItem2() == null) {
            // 请求失败
            log.info("扫码：{} 申请堆垛机任务失败，异常信息：{}", stockScan.getScanId(), stringStockLoginDataResp.getItem1());
            writeSimpleScanTask(stockScan, (short)2);
            return;
        }
        log.info("扫码：{} 申请堆垛机任务成功", stockScan.getScanId());
        writeSimpleScanTask(stockScan, (short)1);
        // 生成任务
        StockLoginDataResp returnData = stringStockLoginDataResp.getItem2();
        if(returnData == null) {
            return;
        }
        // 检查任务格式是否正确
        if(AppStringUtils.isEmpty(returnData.getTaskId())
        || returnData.getTaskType() == null
        || (AppStringUtils.isEmpty(returnData.getOrigin()) && AppStringUtils.isEmpty(returnData.getDestination()))) {
            log.info("扫码：{} 注册堆垛机任务，任务数据不正确，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(returnData));
            return;
        }
        if(StockComposeTaskTypeEnum.getByCode(returnData.getTaskType()).equals(StockComposeTaskTypeEnum.UNKNOWN)) {
            log.info("扫码：{} 注册堆垛机任务，任务类型不正确，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(returnData));
            return;
        }
        // 检查任务是否存在
        List<AppStockComposeTask> stockComposeTaskList = stockComposeTaskService.queryByUpperTaskId(returnData.getTaskId());
        if(stockComposeTaskList == null) {
            log.info("扫码：{} 注册堆垛机任务查询任务失败", stockScan.getScanId());
            return;
        }
        if(!stockComposeTaskList.isEmpty()) {
            log.info("扫码：{} 注册堆垛机任务，任务已经已存在，任务ID：{}", stockScan.getScanId(), returnData.getTaskId());
            return;
        }
        AppStockComposeTask stockComposeTask = new AppStockComposeTask();
        stockComposeTask.setTaskId(AppUUIDUtils.getNewUUID());
        stockComposeTask.setTaskGroup(AppStringUtils.isEmptyOr(returnData.getTaskGroup(), returnData.getTaskId()));
        stockComposeTask.setUpperTaskId(returnData.getTaskId());
        stockComposeTask.setTaskType(returnData.getTaskType());
        stockComposeTask.setOrigin(returnData.getOrigin());
        stockComposeTask.setDestination(returnData.getDestination());
        stockComposeTask.setTaskStatus(StockComposeTaskStatusEnum.CREATE.getCode());
        stockComposeTask.setStepStatus(StockComposeTaskStepStatusEnum.CREATE.getCode());
        stockComposeTask.setCanCancel(TrueOrFalseEnum.TRUE.getCode());
        stockComposeTask.setPriority(returnData.getPriority() == null ? 5 : returnData.getPriority());
        stockComposeTask.setVehicleNo(returnData.getVehicleNo());
        stockComposeTask.setVehicleSize(returnData.getSize() == null ? 0 : returnData.getSize());
        stockComposeTask.setWeight(BigDecimal.valueOf(returnData.getWeight() == null ? 0 : returnData.getWeight()));
        stockComposeTask.setCreateTime(LocalDateTime.now());
        stockComposeTask.setUpdateTime(LocalDateTime.now());
        stockComposeTask.setTaskSource(returnData.getSysName());
        stockComposeTask.setCreatePerson("?");
        int insertResult = stockComposeTaskService.insert(stockComposeTask);
        if(insertResult <= 0) {
            log.info("扫码：{} 注册堆垛机任务，任务插入失败，任务数据：{}", returnData.getVehicleNo(), AppStringUtils.objectToString(stockComposeTask));
            return;
        }
        log.info("扫码：{} 注册堆垛机任务，任务插入成功，任务数据：{}", returnData.getVehicleNo(), AppStringUtils.objectToString(stockComposeTask));
    }

    /**
     * 注册任务并根据返回值确定该进那个堆垛机
     * @param stockScan 扫码器信息
     * @param scanInfo 扫码信息
     */
    @Override
    @ScanMethodTag("LOGIN_STACKER_SEED")
    public void loginStackerTaskSeed(AppStockScan stockScan, ScanInfo scanInfo) {
        if(stockScan == null || scanInfo == null) {
            log.info("堆垛机 注册扫码器信息数据为空");
            return;
        }
        if(scanInfo.getTag() != TrueOrFalseEnum.TRUE.getCode()) {
            return;
        }
        log.info("注册扫码器信息: {}，获得扫码数据：{}", stockScan, scanInfo);
        // 检查是否扫码失败
        String code = scanInfo.getVehicleNo();
        if(AppStringUtils.isEmpty(code)) {
            return;
        }
        code = code.trim().replaceAll("\\W+", ""); // 获取条码简单处理
        short errRouter = Short.parseShort(AppStringUtils.isEmptyOr(stockScan.getParam2(), "999")); // 异常路向值
        // 读码失败
        if(code.equals(ConstantData.NO_READ)) {
            log.info("扫码：{}，扫码失败，条码：{}",stockScan.getScanId(), code);
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        // 读码成功
        // 检查是否有该条码的任务
        AppStockComposeTask stockComposeTaskCheck = new AppStockComposeTask();
        stockComposeTaskCheck.setVehicleNo(code);
        List<AppStockComposeTask> stockComposeTaskCheckList = stockComposeTaskService.query(stockComposeTaskCheck);
        if(stockComposeTaskCheckList == null) {
            log.info("扫码：{}，检索堆垛机任务失败，条码：{}",stockScan.getScanId(), code);
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        // -- 找出是否存在未执行的堆垛机入库任务
        List<AppStockComposeTask> stockComposeTasks = stockComposeTaskCheckList.stream().filter(task -> StockComposeTaskStatusEnum.getAllNotExecuteStatus().contains(task.getTaskStatus()) &&
                (task.getTaskType().equals(StockComposeTaskTypeEnum.AUTO.getCode()) || task.getTaskType().equals(StockComposeTaskTypeEnum.IN.getCode()))).toList();
        if(!stockComposeTasks.isEmpty()) {
            // 存在任务
            AppStockComposeTask stockComposeTask = stockComposeTasks.getFirst();
            List<AppStackerLocation> stackerLocations = stackerLocationService.queryStackerLocationById(stockComposeTask.getDestination());
            if(stackerLocations == null) {
                log.info("扫码：{} 注册堆垛机任务，终点位置查询数据库失败，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
                writeSimpleScanTask(stockScan, errRouter);
                return;
            }
            if(stackerLocations.isEmpty()) {
                log.info("扫码：{} 注册堆垛机任务，终点位置不存在，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
                writeSimpleScanTask(stockScan, errRouter);
                return;
            }
            AppStackerLocation stackerLocation = stackerLocations.getFirst();
            short router = stackerLocation.getLaneId().shortValue();
            log.info("箱号：{} 在注册位置：{}，存在入库任务任务，巷道：{}", code, stockScan.getScanId(), router);
            writeSimpleScanTask(stockScan, router);
            return;
        }
        /* 没有任务，注册任务 */
        // 请求上位系统
        StockLoginReq stockLoginReq = new StockLoginReq();
        stockLoginReq.setRequestId(AppUUIDUtils.getNewUUID());
        stockLoginReq.setVehicleNo(scanInfo.getVehicleNo());
        stockLoginReq.setLocation(stockScan.getScanId());
        stockLoginReq.setSize(Integer.valueOf(scanInfo.getVehicleSize() == null ? 0 : scanInfo.getVehicleSize()));
        stockLoginReq.setLength(scanInfo.getLength() == null ? 0 : scanInfo.getLength());
        stockLoginReq.setWidth(scanInfo.getWidth() == null ? 0 : scanInfo.getWidth());
        stockLoginReq.setHeight(scanInfo.getHeight() == null ? 0 : scanInfo.getHeight());
        stockLoginReq.setWeight(scanInfo.getWeight() == null ? 0 : scanInfo.getWeight());
        HttpRequest httpRequest = HttpRequest.buildStart()
                .post()
                .setAddressKey(stockScan.getParam1() + "_LOGIN_STACKER")
                .setBody(AppStringUtils.objectToString(stockLoginReq))
                .buildEnd();
        HttpResponse httpResponse = WebHttpClient.httpRequest(httpRequest);
        if(!httpResponse.isSuccess()) {
            log.info("扫码：{} 申请堆垛机任务失败，异常信息：{}", stockScan.getScanId(), httpResponse.getException().toString());
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        String responseText = httpResponse.getResponseText();
        UpperSystemDataResponse<StockLoginDataResp> upperSystemDataResponse = JSONObject.parseObject(responseText, new TypeReference<>(){});
        if(upperSystemDataResponse == null) {
            log.info("扫码：{} 申请堆垛机任务失败，返回的数据无法解析，返回信息：{}", stockScan.getScanId(), responseText);
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        if(!upperSystemDataResponse.isOk()) {
            log.info("扫码：{} 申请堆垛机任务失败，返回信息：{}", stockScan.getScanId(), upperSystemDataResponse.getMessage());
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        log.info("扫码：{} 申请堆垛机任务成功，信息：{}", stockScan.getScanId(), upperSystemDataResponse.getMessage());
        // 生成任务
        StockLoginDataResp returnData = upperSystemDataResponse.getReturnData();
        if(returnData == null) {
            return;
        }
        // 检查任务格式是否正确
        if(AppStringUtils.isEmpty(returnData.getTaskId())
                || returnData.getTaskType() == null
                || (AppStringUtils.isEmpty(returnData.getOrigin()) && AppStringUtils.isEmpty(returnData.getDestination()))) {
            writeSimpleScanTask(stockScan, errRouter);
            log.info("扫码：{} 注册堆垛机任务，任务数据不正确，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(returnData));
            return;
        }
        if(StockComposeTaskTypeEnum.getByCode(returnData.getTaskType()).equals(StockComposeTaskTypeEnum.UNKNOWN)) {
            writeSimpleScanTask(stockScan, errRouter);
            log.info("扫码：{} 注册堆垛机任务，任务类型不正确，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(returnData));
            return;
        }
        // 检查任务是否存在
        List<AppStockComposeTask> stockComposeTaskList = stockComposeTaskService.queryByUpperTaskId(returnData.getTaskId());
        if(stockComposeTaskList == null) {
            writeSimpleScanTask(stockScan, errRouter);
            log.info("扫码：{} 注册堆垛机任务查询任务失败", stockScan.getScanId());
            return;
        }
        if(!stockComposeTaskList.isEmpty()) {
            // 存在任务
            AppStockComposeTask stockComposeTask = stockComposeTasks.getFirst();
            List<AppStackerLocation> stackerLocations = stackerLocationService.queryStackerLocationById(stockComposeTask.getDestination());
            if(stackerLocations == null) {
                log.info("扫码：{} 注册堆垛机任务，终点位置查询数据库失败，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
                writeSimpleScanTask(stockScan, errRouter);
                return;
            }
            if(stackerLocations.isEmpty()) {
                log.info("扫码：{} 注册堆垛机任务，终点位置不存在，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
                writeSimpleScanTask(stockScan, errRouter);
                return;
            }
            AppStackerLocation stackerLocation = stackerLocations.getFirst();
            short router = stackerLocation.getLaneId().shortValue();
            log.info("箱号：{} 在注册位置：{}，存在入库任务任务，申请是返回的任务存在，巷道：{}", code, stockScan.getScanId(), router);
            writeSimpleScanTask(stockScan, router);
            return;
        }
        AppStockComposeTask stockComposeTask = new AppStockComposeTask();
        stockComposeTask.setTaskId(AppUUIDUtils.getNewUUID());
        stockComposeTask.setTaskGroup(AppStringUtils.isEmptyOr(returnData.getTaskGroup(), returnData.getTaskId()));
        stockComposeTask.setUpperTaskId(returnData.getTaskId());
        stockComposeTask.setTaskType(returnData.getTaskType());
        stockComposeTask.setOrigin(returnData.getOrigin());
        stockComposeTask.setDestination(returnData.getDestination());
        stockComposeTask.setTaskStatus(StockComposeTaskStatusEnum.CREATE.getCode());
        stockComposeTask.setStepStatus(StockComposeTaskStepStatusEnum.CREATE.getCode());
        stockComposeTask.setCanCancel(TrueOrFalseEnum.TRUE.getCode());
        stockComposeTask.setPriority(returnData.getPriority() == null ? 5 : returnData.getPriority());
        stockComposeTask.setVehicleNo(returnData.getVehicleNo());
        stockComposeTask.setVehicleSize(returnData.getSize() == null ? 0 : returnData.getSize());
        stockComposeTask.setWeight(BigDecimal.valueOf(returnData.getWeight() == null ? 0 : returnData.getWeight()));
        stockComposeTask.setCreateTime(LocalDateTime.now());
        stockComposeTask.setUpdateTime(LocalDateTime.now());
        stockComposeTask.setTaskSource(returnData.getSysName());
        stockComposeTask.setCreatePerson("?");
        int insertResult = stockComposeTaskService.insert(stockComposeTask);
        if(insertResult <= 0) {
            log.info("扫码：{} 注册堆垛机任务，任务插入失败，任务数据：{}", returnData.getVehicleNo(), AppStringUtils.objectToString(stockComposeTask));
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        List<AppStackerLocation> stackerLocations = stackerLocationService.queryStackerLocationById(stockComposeTask.getDestination());
        if(stackerLocations == null) {
            log.info("扫码：{} 注册堆垛机任务，终点位置查询数据库失败，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        if(stackerLocations.isEmpty()) {
            log.info("扫码：{} 注册堆垛机任务，终点位置不存在，任务数据：{}", stockScan.getScanId(), AppStringUtils.objectToString(stockComposeTask));
            writeSimpleScanTask(stockScan, errRouter);
            return;
        }
        AppStackerLocation stackerLocation = stackerLocations.getFirst();
        short router = stackerLocation.getLaneId().shortValue();
        log.info("箱号：{} 在注册位置：{}，任务插入成功，巷道：{}，任务数据：{}", code, stockScan.getScanId(), router, AppStringUtils.objectToString(stockComposeTask));
        writeSimpleScanTask(stockScan, router);
    }





    /* ***********常用方法*********** */

    /**
     * 写入一个简单任务
     * @param stockScan 扫描任务
     * @param router 路由
     */
    private void writeSimpleScanTask(AppStockScan stockScan, Short router) {
        ScanTask scanTask = new ScanTask();
        scanTask.setPlcTaskId(plcTaskIdHelper.newTaskId());
        scanTask.setDestination(router);
        String writePlcErrorMessage = PlcCommunicationFactory.getPlcCommunicationByPlcId(stockScan.getPlcId()).writeScannerTask(stockScan.getScanId(), scanTask);
        if(AppStringUtils.isNotEmpty(writePlcErrorMessage)) {
            log.error("扫码：{}，写入扫码任务失败，任务：{}，异常信息：{}",stockScan.getScanId(), AppStringUtils.objectToString(scanTask), writePlcErrorMessage);
            return;
        }
        log.info("扫码：{}，扫码写入任务成功，任务：{}",stockScan.getScanId(),AppStringUtils.objectToString(scanTask));
        return;
    }



}
