package com.sckw.transport.handler;


import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sckw.core.common.enums.enums.ErrorCodeEnum;
import com.sckw.core.exception.BusinessException;
import com.sckw.core.exception.BusinessPlatfromException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.enums.AddressTypeEnum;
import com.sckw.core.model.enums.CarWaybillV1Enum;
import com.sckw.core.model.enums.LogisticsOrderV1Enum;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.utils.DateUtils;
import com.sckw.core.utils.DistanceUtils;
import com.sckw.core.web.constant.HttpStatus;
import com.sckw.core.web.response.BaseResult;
import com.sckw.fleet.api.model.vo.DriverConductRulesVO;
import com.sckw.fleet.api.model.vo.TruckDispatchCoefficientVO;
import com.sckw.order.api.model.OrderDetailVo;
import com.sckw.order.api.model.TradeOrderSettlePara;
import com.sckw.payment.api.feign.PaymentFeignService;
import com.sckw.payment.api.model.feign.WalletPayableDto;
import com.sckw.transport.model.*;
import com.sckw.transport.model.param.WaybillOrderUnloadParam;
import com.sckw.transport.repository.KwtLogisticsOrderUnitRepository;
import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Author: donglang
 * Time: 2025-11-19
 * Des: 卸货
 * Version: 1.0
 */
@Slf4j
@Service
public class UnloadingHandler extends AbstractWaybillOrderHandler<WaybillOrderUnloadParam>{

    @Autowired
    private KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;

    @Autowired
    private KwtLogisticsOrderUnitRepository kwtLogisticsOrderUnitRepository;

    @Autowired(required = false)
    private PaymentFeignService paymentFeignService;


    // 定义运单未完结的状态集合
    private static final List<Integer> UNFINISHED_STATUSES = Arrays.asList(
            CarWaybillV1Enum.PENDING_VEHICLE.getCode(),
            CarWaybillV1Enum.REFUSE_TRAFFIC.getCode(),
            CarWaybillV1Enum.EXIT_COMPLETED.getCode(),
            CarWaybillV1Enum.EMPTY_WAIT_LEAVE.getCode(),
            CarWaybillV1Enum.WAIT_LEAVE.getCode(),
            CarWaybillV1Enum.UNLOADING.getCode(),
            CarWaybillV1Enum.WAIT_RELEASE.getCode(),
            CarWaybillV1Enum.REPLENISHING.getCode(),
            CarWaybillV1Enum.REPLENISH_FINISH.getCode(),
            CarWaybillV1Enum.RELEASED_NOT_EXITED.getCode(),
            CarWaybillV1Enum.WAIT_LOADING.getCode(),
            CarWaybillV1Enum.COMPLETION_LOADING.getCode(),
            CarWaybillV1Enum.REVIEW_REJECTION.getCode()
    );


    @Override
    protected KwtWaybillOrder getWaybillOrder(WaybillOrderUnloadParam param) {
        return getWaybillOrder(param.getWaybillOrderId());
    }


    @Override
    protected void checkState(WaybillOrderUnloadParam param, KwtWaybillOrder waybill) {
        if (waybill == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_FOUND, "运单不存在");
        }
        if (!Objects.equals(CarWaybillV1Enum.WAIT_LOADING.getCode(), waybill.getStatus())
                && !Objects.equals(CarWaybillV1Enum.REVIEW_REJECTION.getCode(), waybill.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_STATUS_ERROR, "当前物流运单不是“已离场”或“审核驳回”状态，无法推进下一节点！");
        }
    }

    @Override
    protected void doBusiness(WaybillOrderUnloadParam param, KwtWaybillOrder waybill) {
        //查询物流订单
        KwtLogisticsOrder logisticsOrder = getLogisticsOrder(waybill.getLOrderId());

        OrderDetailVo orderDetailVo = tradeOrderInfoService.queryByTradeOrderId(logisticsOrder.getTOrderId());
        if (orderDetailVo == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.TRADE_ORDER_NOT_FOUND, "交易订单数据不存在！");
        }
        //计算净装货量
        BigDecimal loadAmount = getLoadAmount(param);
        //按照装货量计算：推送到已完成状态
        if (orderDetailVo.getChargeType() == 1) {
            waybill.setTargetStatus(Global.NUMERICAL_ONE);

            //1.更新物流订单装货量
            updateLogisticsOrder(waybill, logisticsOrder, loadAmount);

            //2.更新贸易订单装货量
            updateTradeOrder(logisticsOrder, loadAmount);

            //3.解绑车辆
            unbindTruck(waybill.getId());

        } else if (orderDetailVo.getChargeType() == 2) {
            if (param.getUnloadAmount() == null || StringUtils.isBlank(param.getUnloadUrl())) {
                throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "卸货净重和卸货凭证不能为空！");
            }
            waybill.setTargetStatus(Global.NUMERICAL_TWO);

            //1.更新子运单卸货量
            updateWaybillOrderSubtask(param, waybill);

            //按照卸货方式，运单驳回后，不再更新订单装货量
            if (Objects.equals(CarWaybillV1Enum.WAIT_LOADING.getCode(), waybill.getStatus())) {
                //2.更新物流订单装货量
                updateLogisticsOrderByTake(logisticsOrder, loadAmount);

                //3.更新贸易订单装货量
                updateTradeOrder(logisticsOrder, loadAmount);
            }
        }
    }

    /**
     * 计算装货单装货净量
     * @param param
     * @return
     */
    private BigDecimal getLoadAmount(WaybillOrderUnloadParam param) {
        KwtWaybillOrderTicket ticket = waybillOrderTicketRepository.queryByWOrderIdByIdAndType(param.getWaybillOrderId(), 1);
        if (ticket == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_TICKET_NOT_FOUND, "当前物流运单装货单信息不存在！");
        }
        //装货净重
        return ticket.getAmount();
    }

    /**
     * 按照卸货量计算：推送到已卸货状态，更新子运单卸货净重
     * @param param
     * @param waybill
     */
    private void updateWaybillOrderSubtask(WaybillOrderUnloadParam param, KwtWaybillOrder waybill) {
        KwtWaybillOrderSubtask subtask = getWaybillSubtask(waybill.getId());
        subtask.setUnloadAmount(param.getUnloadAmount());
        subtask.setUnloadTime(new Date());
        subtask.setUnloadUrl(param.getUnloadUrl());
        subtask.setUnloadUploadTime(new Date());
        subtask.setUnloadOperator(waybill.getDriverId());
        waybillOrderSubtaskRepository.updateById(subtask);
    }

    /**
     * 更新物流订单装货量和已委托量
     */
    private void updateLogisticsOrder(KwtWaybillOrder waybill, KwtLogisticsOrder logisticsOrder, BigDecimal loadAmount) {
        //查询子运单
        KwtWaybillOrderSubtask subtask = getWaybillSubtask(waybill.getId());

        //物流订单装货量累加
        BigDecimal currentTotalLoad = Optional.ofNullable(logisticsOrder.getTotalLoadAmount())
                .filter(amount -> amount.compareTo(BigDecimal.ZERO) > 0)
                .orElse(BigDecimal.ZERO);
        BigDecimal totalLoadAmount = currentTotalLoad.add(Optional.ofNullable(loadAmount).orElse(BigDecimal.ZERO));
        logisticsOrder.setTotalLoadAmount(totalLoadAmount);

        // 物流订单已委托量扣减
        BigDecimal currentEntrust = Optional.ofNullable(logisticsOrder.getEntrustAmount())
                .filter(amount -> amount.compareTo(BigDecimal.ZERO) > 0)
                .orElse(BigDecimal.ZERO);
        BigDecimal subtractAmount = Optional.ofNullable(subtask.getEntrustAmount()).orElse(BigDecimal.ZERO);
        BigDecimal totalEntrustAmount = currentEntrust.subtract(subtractAmount);
        // 确保已委托量不为负数
        logisticsOrder.setEntrustAmount(totalEntrustAmount.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : totalEntrustAmount);
        //如果当前运单的物流订单下没有其它进行中的运单，则修改物流订单状态已完成
        if (Objects.equals(LogisticsOrderV1Enum.NEARING_COMPLETION.getCode(), logisticsOrder.getStatus())) {
            List<KwtWaybillOrder> waybillOrderList = waybillOrderRepository
                    .findOneByLogOrderIdAndStatus(logisticsOrder.getId(), UNFINISHED_STATUSES, subtask.getWOrderId());
            if (CollectionUtils.isEmpty(waybillOrderList)) {
                logisticsOrder.setStatus(LogisticsOrderV1Enum.COMPLETED.getCode());
            }
        }
        logisticsOrderRepository.updateById(logisticsOrder);
        if(Objects.equals(logisticsOrder.getStatus(),LogisticsOrderV1Enum.COMPLETED.getCode())){
            //物流订单已完结，计算待付运费
            calculateFreight(logisticsOrder);
        }
    }

    private void calculateFreight(KwtLogisticsOrder logisticsOrder){
        if(Objects.equals(logisticsOrder.getSettlement(),1)){
            //todo 普通结算

        }else if(Objects.equals(logisticsOrder.getSettlement(),2)){
            //记账模式
            //1解冻运费
            List<KwtLogisticsOrderUnit> orderUnits = kwtLogisticsOrderUnitRepository.queryByLOrderId(logisticsOrder.getId());
            Map<Integer, Long> map = orderUnits.stream().collect(Collectors.toMap(KwtLogisticsOrderUnit::getUnitType, KwtLogisticsOrderUnit::getEntId, (v1, v2) -> v1));
            WalletPayableDto unFreezePayableDto = new WalletPayableDto();
            unFreezePayableDto.setConsignEntId(map.get(1));
            unFreezePayableDto.setCarriageEntId(map.get(2));
            unFreezePayableDto.setOrderNo(logisticsOrder.getLOrderNo());
            unFreezePayableDto.setOrderType(5);
            unFreezePayableDto.setTradeType(5);
            unFreezePayableDto.setRemark("物流订单号："+unFreezePayableDto.getOrderNo()+"，订单完结解冻");
            log.info("物流订单号：{}，订单完结解冻进行中运费，参数：{}", unFreezePayableDto.getOrderNo(),JSON.toJSONString(unFreezePayableDto));
            BaseResult<Object> unFreezeResult = paymentFeignService.updatePayable(unFreezePayableDto);
            if(unFreezeResult.getCode()!= HttpStatus.SUCCESS_CODE){
                log.error("物流订单号：{}，订单完结解冻失败,异常信息为：{}",unFreezePayableDto.getOrderNo(),unFreezeResult.getMessage());
                throw new BusinessException("物流订单号："+unFreezePayableDto.getOrderNo()+"，订单完结解冻失败");
            }
            //2计算待付运费
            WalletPayableDto consumePayableDto = new WalletPayableDto();
            consumePayableDto.setConsignEntId(map.get(1));
            consumePayableDto.setCarriageEntId(map.get(2));
            consumePayableDto.setOrderNo(logisticsOrder.getLOrderNo());
            consumePayableDto.setOrderType(5);
            consumePayableDto.setTradeType(6);
            consumePayableDto.setTradeAmount(logisticsOrder.getTotalLoadAmount().multiply(logisticsOrder.getPrice()));
            consumePayableDto.setRemark("物流订单号："+consumePayableDto.getOrderNo()+"，订单完结消费");
            log.info("物流订单号：{}，订单完结消费，参数：{}", consumePayableDto.getOrderNo(),JSON.toJSONString(consumePayableDto));
            BaseResult<Object> consumeResult = paymentFeignService.updatePayable(consumePayableDto);
            if(consumeResult.getCode()!= HttpStatus.SUCCESS_CODE){
                log.error("物流订单号：{}，订单完结消费失败,异常信息为：{}",unFreezePayableDto.getOrderNo(),consumeResult.getMessage());
                throw new BusinessException("物流订单号："+unFreezePayableDto.getOrderNo()+"，订单完结消费失败");
            }
        }
    }

    /**
     * 更新贸易订单装货量
     */
    private void updateTradeOrder(KwtLogisticsOrder logisticsOrder, BigDecimal loadAmount) {
        // 原矿运输没有贸易订单
        if (Objects.equals(1, logisticsOrder.getOrderType())) {
            return;
        }
        //贸易订单装货量累加
        TradeOrderSettlePara tradeOrderSettlePara = new TradeOrderSettlePara();
        tradeOrderSettlePara.setTOrderId(logisticsOrder.getTOrderId());
        tradeOrderSettlePara.setLoadAmount(loadAmount);

        tradeOrderInfoService.orderSettle(tradeOrderSettlePara);
        log.info("离场过磅更新贸易订单累计装货净重成功！贸易订单ID: {}, 本次装货净重: {}", logisticsOrder.getTOrderId(), loadAmount);
    }


    /**
     * 更新物流订单装货量和已委托量
     */
    private void updateLogisticsOrderByTake(KwtLogisticsOrder logisticsOrder, BigDecimal loadAmount) {
        //物流订单装货量累加
        BigDecimal currentTotalLoad = Optional.ofNullable(logisticsOrder.getTotalLoadAmount())
                .filter(amount -> amount.compareTo(BigDecimal.ZERO) > 0)
                .orElse(BigDecimal.ZERO);
        BigDecimal totalLoadAmount = currentTotalLoad.add(Optional.ofNullable(loadAmount).orElse(BigDecimal.ZERO));
        logisticsOrder.setTotalLoadAmount(totalLoadAmount);
        logisticsOrderRepository.updateById(logisticsOrder);
        log.info("卸货更新物流订单累计装货净重成功！物流订单ID: {}, 本次装货净重: {}", logisticsOrder.getId(), loadAmount);
    }

    /**
     * 解绑车辆
     * @param wOrderId
     */
    private void unbindTruck(Long wOrderId) {
        //查询运单运单信息
        KwtWaybillOrder waybillOrder = getWaybillOrder(wOrderId);
        remoteFleetService.unbindTruck(waybillOrder.getEntId(), waybillOrder.getDriverId());
        log.info("运单完成，解绑司机与车辆关系，运单id：{},企业id:{},司机id：{}", wOrderId, waybillOrder.getEntId(), waybillOrder.getDriverId());
    }

    @Override
    protected String getProcessName() {
        return "卸货";
    }

    @Override
    protected void getStatus(KwtWaybillOrder waybillOrder) {
        if (waybillOrder.getTargetStatus() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.SYSTEM_ERROR, "[卸货]计算方式为空！");
        }
        CarWaybillV1Enum status = Objects.equals(waybillOrder.getTargetStatus(), Global.NUMERICAL_ONE) ? CarWaybillV1Enum.COMPLETED
                : CarWaybillV1Enum.COMPLETION_LOADING;

        // 1. 修改运单状态
        waybillOrder.setStatus(status.getCode());
        waybillOrderRepository.updateById(waybillOrder);

        // 2. 修改子运单状态
        KwtWaybillOrderSubtask waybillSubtask = getWaybillSubtask(waybillOrder.getId());
        waybillSubtask.setStatus(status.getCode());
        waybillOrderSubtaskRepository.updateById(waybillSubtask);

    }

    @Override
    protected String getRemark(WaybillOrderUnloadParam param, KwtWaybillOrder waybillOrder) {
        return null;
    }

    /**
     * 重写 createNodeTrace，生成两条轨迹记录
     */
    protected void createNodeTrace(WaybillOrderUnloadParam param, KwtWaybillOrder waybillOrder) {
        if (waybillOrder.getTargetStatus() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.SYSTEM_ERROR, "[卸货]计算方式为空！");
        }
        //订单按照装货方式计算
        if (waybillOrder.getTargetStatus() == Global.NUMERICAL_ONE) {
            // 第一条：卸货已完成
            KwtWaybillOrderNode node1 = getWaybillOrderNode(param, waybillOrder);
            node1.setRemark("司机[" + waybillOrder.getDriverName() + "]已卸货");
            node1.setOrderStatus(CarWaybillV1Enum.COMPLETION_LOADING.getCode());
            waybillOrderNodeRepository.save(node1);
            log.info("记录【卸货】节点轨迹成功，节点ID：{}", node1.getId());

            // 第二条：运单完成
            KwtWaybillOrderNode node2 = getWaybillOrderNode(param, waybillOrder);
            node2.setRemark("运单[" + waybillOrder.getId() + "]已完成");
            waybillOrderNodeRepository.save(node2);
            log.info("记录【完成】节点轨迹成功，节点ID：{}", node2.getId());
        } else {
            //订单按照卸货货方式计算
            KwtWaybillOrderNode node = getWaybillOrderNode(param, waybillOrder);
            node.setRemark("司机[" + waybillOrder.getDriverName() + "]已上传卸货凭证,卸货量[" + param.getUnloadAmount() + "吨]");
            waybillOrderNodeRepository.save(node);
            log.info("记录【卸货】节点轨迹成功，节点ID：{}", node.getId());
        }
    }

    @Override
    protected void calculateAutoDispatchScore(WaybillOrderUnloadParam param, KwtWaybillOrder waybillOrder) {
        //查询物流订单
        KwtLogisticsOrder logisticsOrder = logisticsOrderRepository.queryByLogisticsOrderId(waybillOrder.getLOrderId());
        //供应商
        Long supEntId = getSupplierId(waybillOrder);
        // 获取司机行为规则配置
        DriverConductRulesVO driverRulesVO = getDriverConductRulesByEntId(supEntId);

        //1. 校验司机是否虚假卸货
        checkFakeUnloadDistance(waybillOrder, driverRulesVO);

        //订单若按照装货方式计算：此时整个运单已经结束，计算运单总耗时。如按照卸货方式计算，运单还未结算，不进行司机加减分
        if (waybillOrder.getTargetStatus() == null || waybillOrder.getTargetStatus() != Global.NUMERICAL_ONE) {
            log.info("当前运单是按照“卸货方式计算“,运单未完成，跳过超时校验！运单id: {}", waybillOrder.getId());
            return;
        }

        //2. 校验司机运单完成是否超时
        checkWaybillCompletionTimeout(waybillOrder, logisticsOrder, driverRulesVO);

        //3. 校验司机连续准时卸货
        checkContinuousUnload(waybillOrder, logisticsOrder, driverRulesVO);
    }


    /**
     * 校验司机是否虚假卸货
     * @param waybillOrder
     * @return
     */
    @NotNull
    private void checkFakeUnloadDistance(KwtWaybillOrder waybillOrder, DriverConductRulesVO driverRulesVO) {
        log.info("校验司机是否虚假卸货：waybillOrderId：{}", waybillOrder.getId());
        //虚假卸货分数
        Integer fakeUnloadScore = driverRulesVO.getFakeUnloadScore();
        //虚假卸货偏离距离
        Integer distance = driverRulesVO.getFakeUnloadDistance();
        if (fakeUnloadScore <= 0 || distance <= 0) {
            log.warn("【虚假卸货】企业{}的虚假卸货配置分数/偏差距离异常，运单ID：{}", driverRulesVO.getEntId(), waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "【虚假卸货】配置分数/偏差距离需大于0!");
        }

        //查询运单装卸货地址
        List<KwtWaybillOrderAddress> addressList = waybillOrderAddressRepository.queryByWOrderId(waybillOrder.getId());
        if (CollectionUtils.isEmpty(addressList)) {
            log.info("物流运单无装卸货地址信息，运单id:{}", waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.LOGISTICS_ORDER_NOT_ADDRESS, "物流订单无装卸货地址信息");
        }
        Map<Integer, KwtWaybillOrderAddress> addressMap = addressList.stream()
                .filter(addr -> addr.getAddressType() != null)
                .collect(Collectors.toMap(KwtWaybillOrderAddress::getAddressType, Function.identity(), (x,y) ->x));

        //运单应卸货地址
        KwtWaybillOrderAddress takeAddress = addressMap.getOrDefault(AddressTypeEnum.TAKE.getCode(), new KwtWaybillOrderAddress());
        //实际卸货地址
        KwtWaybillOrderNode unloadingNodes = getNodesByOrderId(waybillOrder.getId(), CarWaybillV1Enum.COMPLETION_LOADING.getCode());

        //应卸货地与实际卸货地之间距离
        double distanceKm = com.sckw.core.utils.LocUtils.calculateDistanceByAmap(
                Optional.ofNullable(takeAddress.getLng()).map(String::valueOf).orElse(null),
                Optional.ofNullable(takeAddress.getLat()).map(String::valueOf).orElse(null),
                Optional.ofNullable(unloadingNodes.getLng()).map(String::valueOf).orElse(null),
                Optional.ofNullable(unloadingNodes.getLat()).map(String::valueOf).orElse(null));
        // 如果高德API调用失败，使用原始方法作为备选
        if (distanceKm == -1) {
            distanceKm = DistanceUtils.calculateDistance(
                    Optional.ofNullable(takeAddress.getLng()).map(Double::valueOf).orElse(null),
                    Optional.ofNullable(takeAddress.getLat()).map(Double::valueOf).orElse(null),
                    Optional.ofNullable(unloadingNodes.getLng()).map(Double::valueOf).orElse(null),
                    Optional.ofNullable(unloadingNodes.getLat()).map(Double::valueOf).orElse(null));
        }
        if (distanceKm > distance) {
            log.info("【虚假卸货】运单ID={}，卸货地址偏差={}KM",waybillOrder.getId(), distanceKm);
            //1、更新司机分数（减分）
            fakeUnloadScore = -Math.abs(fakeUnloadScore);
            updateDriverScore(driverRulesVO.getEntId(), waybillOrder.getEntId(), waybillOrder.getDriverId(), fakeUnloadScore, "虚假卸货");
            //2、更新企业分数（减分）
            updateLogEntScore(waybillOrder, driverRulesVO.getEntId(), fakeUnloadScore, "虚假卸货");
        }
    }

    /**
     * 校验司机运单完成是否超时
     * @param waybillOrder 运单
     * @param logisticsOrder  物流订单
     */
    protected void checkWaybillCompletionTimeout(KwtWaybillOrder waybillOrder, KwtLogisticsOrder logisticsOrder, DriverConductRulesVO driverRulesVO) {
        //严重超时倍数
        BigDecimal timeoutMultiple = driverRulesVO.getUnloadSeriousTimeoutMultiple();
        //严重超时分数
        Integer timeoutScore = driverRulesVO.getUnloadSeriousTimeoutScore();
        if (timeoutMultiple == null || timeoutMultiple.compareTo(BigDecimal.ZERO) <= 0 || timeoutScore <= 0) {
            log.warn("【卸货严重超时】扣分失败，企业{}的司机卸货严重超时倍数/分数配置为空，运单ID：{}", driverRulesVO.getEntId(), waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "【卸货严重超时】配置严重超时倍数/分数需大于0!");
        }
        //校验是否超时
        Boolean timeOut = isTimeOut(waybillOrder, logisticsOrder, driverRulesVO.getEntId(), timeoutMultiple);
        if (timeOut) {
            //1、更新司机分数（减分）
            timeoutScore = -Math.abs(timeoutScore);
            updateDriverScore(driverRulesVO.getEntId(), waybillOrder.getEntId(), waybillOrder.getDriverId(), timeoutScore, "卸货严重超时");

            //2、更新企业分数（减分）
            updateLogEntScore(waybillOrder, driverRulesVO.getEntId(), timeoutScore, "卸货严重超时");
        }

    }

    /**
     * 校验司机运单完成是否超时
     * @param waybillOrder 运单
     * @param supEntId  供应商企业id
     */
    protected Boolean isTimeOut(KwtWaybillOrder waybillOrder, KwtLogisticsOrder logisticsOrder, Long supEntId, BigDecimal timeoutMultiple) {
        log.info("校验司机运单完成是否超时：运单ID：{}", waybillOrder.getId());
        //运单总耗时（秒）
        Long orderTotalTimes = calOrderTotalTimes(waybillOrder);
        //平台配置的单趟总耗时（秒）
        Long singleTripTotalTimes = calSingleTripTotalTimes(waybillOrder,logisticsOrder, supEntId);
        //计算超时阈值
        BigDecimal threshold = timeoutMultiple.compareTo(BigDecimal.ZERO) == 0 ?
                new BigDecimal(singleTripTotalTimes) : timeoutMultiple.multiply(new BigDecimal(singleTripTotalTimes));
        BigDecimal actualTimes = new BigDecimal(orderTotalTimes);
        if (actualTimes.compareTo(threshold) > 0) {
            log.warn("【司机运单超时】运单ID：{}，实际总耗时：{}分钟，平台单趟耗时：{}分钟", waybillOrder.getId(), orderTotalTimes, singleTripTotalTimes);
            return true;
        }
        return false;
    }

    /**
     * 计算司机接单到结束的总耗时
     * @param waybillOrder
     * @return
     */
    private Long calOrderTotalTimes(KwtWaybillOrder waybillOrder) {
        //司机接单时间
        KwtWaybillOrderNode takingOrderNodes = getNodesByOrderId(waybillOrder.getId(), CarWaybillV1Enum.PENDING_VEHICLE.getCode());
        Date takingOrderTime = takingOrderNodes.getCreateTime();
        //司机完成运单时间
        KwtWaybillOrderNode overOrderNodes = getNodesByOrderId(waybillOrder.getId(), CarWaybillV1Enum.COMPLETED.getCode());
        Date overOrderTime = overOrderNodes.getCreateTime();
        //运单总耗时（秒）
        Long orderTotalTimes = DateUtils.calculateTimeDiffMinutes(takingOrderTime, overOrderTime);
        log.info("司机完成运单总耗时：{}", orderTotalTimes);
        return orderTotalTimes;
    }

    /**
     * 计算平台配置的运单单趟总耗时
     * @param waybillOrder
     * @param logisticsOrder
     * @return
     */
    private Long calSingleTripTotalTimes(KwtWaybillOrder waybillOrder, KwtLogisticsOrder logisticsOrder, Long supEntId) {
        // 获取自动派单系数配置（单趟耗时 = 装货时间 + 卸货时间 + 运输时间 + 返回时间）
        TruckDispatchCoefficientVO truckDispatchVO = getAutoTruckDispatchByEntId(supEntId);
        //装货时长
        Integer vehicleLoadingHours = truckDispatchVO.getVehicleLoadingHours();
        //卸货货时长
        Integer vehicleUnloadingHours = truckDispatchVO.getVehicleUnloadingHours();
        //平均行驶速度
        Integer vehicleAvgSpeed = truckDispatchVO.getVehicleAvgSpeed();
        if (vehicleLoadingHours <= 0 || vehicleUnloadingHours <= 0 || vehicleAvgSpeed <= 0) {
            log.warn("企业{}的司机装货时长/卸货时长/平均行驶速度配置数据异常（装货：{}，卸货：{}，速度：{}），运单ID：{}",
                    supEntId, vehicleLoadingHours, vehicleUnloadingHours, vehicleAvgSpeed, waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "司机配置的装货时长/卸货时长/平均行驶速度需要大于0!");
        }
        //应卸货地与实际卸货地之间距离
        String distance = logisticsOrder.getDistance();
        if (StringUtils.isBlank(distance)) {
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "物流运单装货地址有误!");
        }
        BigDecimal bdDividend = new BigDecimal(distance);

        BigDecimal bdDivisor = BigDecimal.valueOf(vehicleAvgSpeed);
        //运输时间(小时) = 两地距离/平均行驶速度
        BigDecimal transportTimeHours = bdDividend.divide(bdDivisor, 4, RoundingMode.HALF_UP);
        //运输时间（秒）
        long transportTimeMillis = transportTimeHours.multiply(BigDecimal.valueOf(3600))
                .setScale(0, RoundingMode.HALF_UP).longValue();

        //往返时间=运输时间
        long returnTimeMinutes = transportTimeMillis;
        //单趟总耗时（秒）
        Long singleTripTotalTimes = TimeUnit.MINUTES.toSeconds(vehicleLoadingHours) + TimeUnit.MINUTES.toSeconds(vehicleUnloadingHours) + transportTimeMillis + returnTimeMinutes;
        log.info("平台配置的运单单趟总耗时：{}", singleTripTotalTimes);
        return singleTripTotalTimes;

    }

    /**
     * 校验连续准时卸货
     * @param waybillOrder
     */
    private void checkContinuousUnload(KwtWaybillOrder waybillOrder, KwtLogisticsOrder logisticsOrder,  DriverConductRulesVO driverRulesVO) {
        //连续卸货次数
        Integer continuousOnTimes = driverRulesVO.getContinuousOnTimeUnloadTimes();
        //连续卸货分数
        Integer continuousUnloadScore = driverRulesVO.getContinuousOnTimeUnloadScore();
        if (continuousOnTimes <= 0 || continuousUnloadScore <= 0) {
            log.warn("【连续准时卸货】该司机无需加分，企业{}的连续按时到场配置次数/分数异常，运单ID：{}", driverRulesVO.getEntId(), waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "【连续准时卸货】配置次数/分数需大于0!");
        }

        //查询该司机连续准时卸货的节点数据
        List<KwtWaybillOrderNode> waybillOrderNodeList = getWaybillOrderNodesByStatus(waybillOrder, continuousOnTimes, CarWaybillV1Enum.COMPLETED);
        //取continuousOnTimes条节点数据
        List<KwtWaybillOrderNode> fileterWaybillOrderNodeList = waybillOrderNodeList.stream().sorted(Comparator.comparing(KwtWaybillOrderNode::getCreateTime).reversed())
                .limit(continuousOnTimes).collect(Collectors.toList());
        if (fileterWaybillOrderNodeList.size() != continuousOnTimes) {
            log.info("司机{}最近连续准时卸货记录不足{}条，当前：{}", waybillOrder.getDriverId(), continuousOnTimes, waybillOrderNodeList.size());
            return;
        }

        //记录连续准时卸货
        List<Long> nodeIds = new ArrayList<>();
        boolean allOnTime = true;
        for (KwtWaybillOrderNode orderNode : waybillOrderNodeList) {
            if (orderNode.getContinuous() == 1) {
                break;
            }
            //计算每次运单是否准时卸货
            KwtWaybillOrder wOrder = getWaybillOrder(orderNode.getWOrderId());
            Boolean timeOut = isTimeOut(wOrder, logisticsOrder, driverRulesVO.getEntId(), BigDecimal.ZERO);
            if (timeOut) {
                log.info("司机{}存在超时情况，运单id：{}", orderNode.getDriverId(), orderNode.getWOrderId());
                allOnTime = false;
                break;
            }
            nodeIds.add(orderNode.getId());
        }
        // 校验是否达到次数
        if (allOnTime && nodeIds.size() == continuousOnTimes) {
            //1. 给连续的N次节点数据打标
            updateNode(waybillOrder, nodeIds, continuousUnloadScore);

            //2 .更新司机分数（加分）
            updateDriverScore(driverRulesVO.getEntId(), waybillOrder.getEntId(), waybillOrder.getDriverId(), continuousUnloadScore, "连续准时卸货");

            //3. 更新企业分数（加分）
            updateLogEntScore(waybillOrder, driverRulesVO.getEntId(), continuousUnloadScore, "连续准时卸货");
        }
    }

    /**
     * 给连续的N次的节点数据打标
     */
    protected void updateNode(KwtWaybillOrder waybillOrder, List<Long> nodeIds, Integer continuousOnTimeScore) {
        KwtWaybillOrderNode updateEntity = new KwtWaybillOrderNode();
        updateEntity.setContinuous(1);
        LambdaQueryWrapper<KwtWaybillOrderNode> updateWrapper = new LambdaQueryWrapper<KwtWaybillOrderNode>()
                .in(KwtWaybillOrderNode::getId, nodeIds);
        waybillOrderNodeRepository.update(updateEntity, updateWrapper);
        log.info("司机{}连续准时卸货,加分：{}，节点ID：{}", waybillOrder.getDriverId(), continuousOnTimeScore, JSON.toJSONString(nodeIds));
    }

}
