package com.sckw.transport.handler;


import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.sckw.core.common.enums.enums.ErrorCodeEnum;
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.fleet.api.model.vo.RDriverVo;
import com.sckw.fleet.api.model.vo.RTruckVo;
import com.sckw.order.api.model.OrderDetailVo;
import com.sckw.order.api.model.UpdateActualAmountParam;
import com.sckw.transport.model.*;
import com.sckw.transport.model.param.OrderCirculateTakingQueryParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * Author: donglang
 * Time: 2025-11-19
 * Des: 接单处理器Handler
 * Version: 1.0
 */

@Slf4j
@Service
public class TakingOrderHandler extends AbstractWaybillOrderHandler<OrderCirculateTakingQueryParam> {

    // 定义常量
    private static final BigDecimal EIGHTY_PERCENT = new BigDecimal("0.8");
    private static final BigDecimal TWO_TONS = new BigDecimal("2");
    // 定义禁止运单接单的状态集合
    private static final List<Integer> FORBIDDEN_STATUSES = Arrays.asList(
            CarWaybillV1Enum.PENDING_VEHICLE.getCode(),
            CarWaybillV1Enum.REFUSE_TRAFFIC.getCode(),
            CarWaybillV1Enum.EXIT_COMPLETED.getCode(),
            CarWaybillV1Enum.WAIT_LOADING.getCode(),
            CarWaybillV1Enum.COMPLETION_UNLOADING.getCode()
    );


    @Override
    protected KwtWaybillOrder getWaybillOrder(OrderCirculateTakingQueryParam param) {
        // 接单时运单未创建
        return null;
    }


    @Override
    protected void checkState(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder) {
        // 1.校验物流订单
        KwtLogisticsOrder logOrder = checkLogOrder(param);
        // 2.校验交易订单
        OrderDetailVo tradeOrder = checkTradeOrder(param, logOrder);
        // 3.校验司机
        RDriverVo driver = checkDriver(param);
        // 4.校验车辆
        RTruckVo truck = checkTruck(param);
        // 5.校验车辆是否已有未完成运单
        checkTruckUnfinished(param);

        // 存储到线程上下文
        BusinessContext.set("logOrder", logOrder);
        BusinessContext.set("tradeOrder", tradeOrder);
        BusinessContext.set("driver", driver);
        BusinessContext.set("truck", truck);
    }


    @Override
    protected void doBusiness(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybill) {
        KwtLogisticsOrder logOrder = BusinessContext.get("logOrder");
        OrderDetailVo tradeOrder = BusinessContext.get("tradeOrder");
        RDriverVo driver = BusinessContext.get("driver");
        RTruckVo truck = BusinessContext.get("truck");

        // 计算任务量（含接单校验）
        BigDecimal truckLoadVolume = queryCurTruckLoadVolume(truck, tradeOrder);

        //1、生成物流运单
        KwtWaybillOrder waybillOrder = createWaybillOrder(param, driver, truck);

        //2、生成物流子运单
        KwtWaybillOrderSubtask waybillOrderSubtask = createWaybillOrderSubtask(param, waybillOrder, truckLoadVolume);

        //3、生成物流运单地址
        List<KwtWaybillOrderAddress> addresses = createWaybillOrderAddress(param, waybillOrder, waybillOrderSubtask);

        //4、生成车辆运单-装卸货信息
        createWaybillOrderTicket(param, logOrder, waybillOrder, waybillOrderSubtask, addresses, truck);

        //5、更新上游订单：回写贸易订单
        updateTradeOrder(tradeOrder, truckLoadVolume);

        //6、更新上游订单：回写物流订单
        updateLogOrder(logOrder, truckLoadVolume);

        BusinessContext.set("createdWaybillOrder", waybillOrder);

        log.info("物流订单接单完成，运单ID：{}", waybillOrder.getId());
    }





    /**
     * 校验物流订单是否是可接单状态
     * @param param
     * @return
     */
    private KwtLogisticsOrder checkLogOrder(OrderCirculateTakingQueryParam param) {
        KwtLogisticsOrder logOrder = logisticsOrderRepository.queryByLogOrderIdAndStatus(param.getLogOrderId(), LogisticsOrderV1Enum.IN_TRANSIT.getCode());
        if (logOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.LOGISTICS_ORDER_STATUS_ERROR, "物流订单非可接单状态");
        }
        return logOrder;
    }

    /**
     * 校验交易订单状态
     * @param param
     * @param logOrder
     * @return
     */
    private OrderDetailVo checkTradeOrder(OrderCirculateTakingQueryParam param, KwtLogisticsOrder logOrder) {
        OrderDetailVo orderDetail = tradeOrderInfoService.queryByTradeOrderId(logOrder.getTOrderId());
        if (orderDetail == null || orderDetail.getStatus() == Global.NUMERICAL_ONE) {
            throw new BusinessPlatfromException(ErrorCodeEnum.TRADE_ORDER_STATUS_ERROR, "当前物流订单的交易订单已锁定");
        }
        return orderDetail;
    }

    /**
     * 校验司机状态
     * @param param
     * @return
     */
    private RDriverVo checkDriver(OrderCirculateTakingQueryParam param) {
        RDriverVo driver = remoteFleetService.findDriver(param.getDriverId());
        if (driver == null || driver.getStatus() == Global.NUMERICAL_ONE) {
            throw new BusinessPlatfromException(ErrorCodeEnum.DRIVER_STATUS_ERROR, "当前司机已锁定");
        }
        return driver;
    }

    /**
     * 校验车辆状态
     * @param param
     * @return
     */
    private RTruckVo checkTruck(OrderCirculateTakingQueryParam param) {
        RTruckVo truckNo = remoteFleetService.findTruckByTruckNo(param.getTruckNo());
        if (truckNo == null || truckNo.getStatus() == Global.NUMERICAL_ONE) {
            throw new BusinessPlatfromException(ErrorCodeEnum.TRUCK_STATUS_ERROR, "当前车辆已锁定");
        }
        //核定载重
        if (truckNo.getActualWeight() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.RESOURCE_NOT_FOUND, "车辆核定载重不能为空！");
        }

        return truckNo;
    }

    /**
     * 校验车辆是否已有未完成运单(不可继续接单状态：已接单、已入场、已装货、已离场、审核驳回)
     * @param param
     */
    private void checkTruckUnfinished(OrderCirculateTakingQueryParam param) {
        List<KwtWaybillOrder> wbOrderByTruckNo = waybillOrderRepository.findWbOrderByTruckNoAndStatus(param.getTruckNo(), FORBIDDEN_STATUSES);
        if (CollectionUtils.isNotEmpty(wbOrderByTruckNo)) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_NOT_UNFINISHED, "当前车辆有未完成的状态，不可继续接单");
        }
    }


    /**
     * 计算当前车辆任务量：
     * 若有皮重：任务量 = 核定载重 - 皮重
     * 若无皮重：任务量 = 核定载重 × 80%
     * <p>
     * 1.当订单余量>=车辆的（法定载重-首次皮重）且<（法定载重-首次皮重 + 2）时，接取的任务量为车辆的（法定载重-首次皮重）；
     * 2.订单余量<车辆的（法定载重-首次皮重）时，该车辆无法接单；
     * 3.当订单余量>= (法定载重-首次皮重 + 2)时，接取的任务量为 (法定载重-首次皮重 + 2)
     *
     * @param truckNo
     */
    private BigDecimal queryCurTruckLoadVolume(RTruckVo truckNo, OrderDetailVo tradeOrder) {
        //核定载重
        BigDecimal actualWeight = truckNo.getActualWeight();
        log.info("车辆核定载重：{}", actualWeight);
        //首次皮重
        BigDecimal tareWeight = truckNo.getTareWeight();
        //任务量
        BigDecimal loadVolume;
        if (tareWeight != null) {
            // 有皮重：任务量 = 核定载重 - 皮重
            loadVolume = actualWeight.subtract(tareWeight);
            log.info("车辆有皮重，皮重：{}， 任务量:{}", tareWeight, loadVolume);

        } else {
            // 无皮重：任务量 = 核定载重 × 80%
            loadVolume = actualWeight.multiply(EIGHTY_PERCENT);
            log.info("车辆无皮重，按80%核定载重计算，车辆任务量：{}", loadVolume);
        }

        //订单分配总量
        BigDecimal entrustAmount = tradeOrder.getEntrustAmount();
        //订单实际交付量
        BigDecimal actualAmount = tradeOrder.getActualAmount();
        if (entrustAmount == null || actualAmount == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "订单分配总量或订单实际交付量不能为空！");
        }
        //订单余量 = 分配总量 - 实际交付量
        BigDecimal remainingAmount = entrustAmount.subtract(actualAmount);
        log.info("订单余量 = 分配总量:{} - 实际交付量:{} = 车辆任务量:{}", entrustAmount, actualAmount, remainingAmount);

        // 最终接取的任务量
        BigDecimal taskAmount;
        BigDecimal loadVolumePlus2 = loadVolume.add(TWO_TONS);
        if (remainingAmount.compareTo(loadVolume) < 0) {
            // 规则2：订单余量 < 任务量 → 无法接单
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, String.format("订单余量不足，无法接单, 订单余量:%s, 车辆任务量:%s", remainingAmount, loadVolume));
        } else if (remainingAmount.compareTo(loadVolumePlus2) >= 0) {
            // 规则3：订单余量 >= 任务量+2 → 接取任务量+2
            taskAmount = loadVolumePlus2;
            log.info("订单余量充足，按(任务量+2吨)接单,最终任务量: {}", taskAmount);
        } else {
            // 规则1：订单余量 >= 任务量 且 < 任务量+2 → 接取任务量
            taskAmount = loadVolume;
            log.info("订单余量适中，按基础任务量接单，最终任务量: {}", taskAmount);
        }
        log.info("最终任务量：{}", taskAmount);
        return taskAmount;
    }

    /**
     * 创建物流运单
     *
     * @param param
     */
    private KwtWaybillOrder createWaybillOrder(OrderCirculateTakingQueryParam param, RDriverVo driver, RTruckVo truckNo) {
        log.info("创建物流运单入参参数：{}", JSON.toJSONString(param));
        KwtWaybillOrder waybillOrder = new KwtWaybillOrder();
        waybillOrder.setEntId(param.getEntId());
        waybillOrder.setLOrderId(param.getLogOrderId());
        waybillOrder.setWOrderNo(null); //TODO donglang
        waybillOrder.setType(1);
        waybillOrder.setTruckId(truckNo.getId());
        waybillOrder.setTruckNo(param.getTruckNo());
        waybillOrder.setDriverId(param.getDriverId());
        waybillOrder.setDriverName(driver.getName());
        waybillOrder.setDriverPhone(driver.getPhone());
        waybillOrder.setDriverIdcard(driver.getIdcard());
        waybillOrder.setStatus(CarWaybillV1Enum.PENDING_VEHICLE.getCode());
        waybillOrder.setTaskStartTime(new Date());
        waybillOrderRepository.save(waybillOrder);
        log.info("创建物流运单成功，运单ID：{}", waybillOrder.getId());

        return waybillOrder;

    }

    /**
     * 创建物流子运单
     *
     * @param param
     */
    private KwtWaybillOrderSubtask createWaybillOrderSubtask(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder,
                                                             BigDecimal truckLoadVolume) {
        log.info("创建物流子运单入参参数：{}", JSON.toJSONString(param));
        KwtWaybillOrderSubtask subtask = new KwtWaybillOrderSubtask();

        subtask.setEntId(param.getEntId());
        subtask.setLOrderId(param.getLogOrderId());
        subtask.setWOrderId(waybillOrder.getId());
        subtask.setWOrderNo(waybillOrder.getWOrderNo());
        subtask.setUnit(null);

        subtask.setEntrustAmount(truckLoadVolume);
        subtask.setLoadTime(waybillOrder.getTaskStartTime());
        subtask.setUnloadTime(waybillOrder.getEndTime());
        subtask.setStatus(CarWaybillV1Enum.PENDING_VEHICLE.getCode());
        waybillOrderSubtaskRepository.save(subtask);
        log.info("创建物流子运单成功，子运单ID：{}", subtask.getId());

        return subtask;

    }

    /**
     * 创建物流运单地址
     *
     * @param param
     */
    private List<KwtWaybillOrderAddress> createWaybillOrderAddress(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder,
                                                                   KwtWaybillOrderSubtask waybillOrderSubtask) {
        log.info("创建物流运单地址入参参数，logOrderId：{}", param.getLogOrderId());
        //地址
        Map<String, KwtLogisticsOrderAddress> logOrderIdAndAddressMap = getKwtLogisticsOrderAddressMap(Lists.newArrayList(param.getLogOrderId()));
        List<KwtWaybillOrderAddress> createdAddresses = new ArrayList<>();

        //装货地址
        KwtLogisticsOrderAddress shipmentSource = logOrderIdAndAddressMap.getOrDefault(
                param.getLogOrderId() + "-" + AddressTypeEnum.SHIPMENT.getCode(), new KwtLogisticsOrderAddress());
        KwtWaybillOrderAddress shipmentAddress = createAddress(waybillOrder, waybillOrderSubtask, shipmentSource, AddressTypeEnum.SHIPMENT);
        createdAddresses.add(shipmentAddress);
        log.info("创建物流运单装货地址成功，运单地址ID：{}", shipmentAddress.getId());

        //卸货地址
        KwtLogisticsOrderAddress takeSource = logOrderIdAndAddressMap.getOrDefault(
                param.getLogOrderId() + "-" + AddressTypeEnum.TAKE.getCode(), new KwtLogisticsOrderAddress());
        KwtWaybillOrderAddress takeAddress = createAddress(waybillOrder, waybillOrderSubtask, takeSource, AddressTypeEnum.TAKE);
        createdAddresses.add(takeAddress);
        log.info("创建物流运单卸货地址成功，运单地址ID：{}", takeAddress.getId());

        log.info("成功创建{}个运单地址", createdAddresses.size());
        return createdAddresses;
    }

    /**
     * 保存运单物流地址
     * @param waybillOrder
     * @param subtask
     * @param orderAddress
     */
    private KwtWaybillOrderAddress createAddress(KwtWaybillOrder waybillOrder, KwtWaybillOrderSubtask subtask,
                                                 KwtLogisticsOrderAddress orderAddress, AddressTypeEnum typeEnum) {
        KwtWaybillOrderAddress address = new KwtWaybillOrderAddress();
        address.setWOrderId(waybillOrder.getId());
        address.setWSubtaskId(subtask.getId());
        address.setLAddressId(orderAddress.getId());
        address.setAddressType(typeEnum.getCode());
        address.setName(orderAddress.getName());
        address.setType(orderAddress.getType());
        address.setContacts(orderAddress.getContacts());
        address.setPhone(orderAddress.getPhone());
        address.setCityCode(orderAddress.getCityCode());
        address.setCityName(orderAddress.getCityName());
        address.setDetailAddress(orderAddress.getDetailAddress());
        address.setLat(orderAddress.getLat());
        address.setLng(orderAddress.getLng());
        address.setFence(orderAddress.getFence());
        address.setEntryType(orderAddress.getEntryType());
        address.setPlanTime(waybillOrder.getTaskStartTime());
        address.setSort(Global.NUMERICAL_ONE);
        address.setEntrustAmount(subtask.getEntrustAmount());

        waybillOrderAddressRepository.save(address);
        return address;
    }

    /**
     * 生成车辆运单-装卸货信息
     *
     * @param param
     * @param waybillOrder
     */
    private void createWaybillOrderTicket(OrderCirculateTakingQueryParam param, KwtLogisticsOrder logOrder,
                                          KwtWaybillOrder waybillOrder, KwtWaybillOrderSubtask waybillOrderSubtask,
                                          List<KwtWaybillOrderAddress> waybillOrderAddresses, RTruckVo truckNo) {
        log.info("创建车辆运单-装卸货信息入参参数：{}", JSON.toJSONString(param));

        //装货地址
        KwtWaybillOrderAddress shipmentAddress = getWaybillOrderAddress(waybillOrderAddresses, AddressTypeEnum.SHIPMENT);
        createTicket(waybillOrder, waybillOrderSubtask, shipmentAddress, logOrder, truckNo);
        log.info("创建车辆运单-装卸货信息成功！");

        //卸货地址
        KwtWaybillOrderAddress takeAddress = getWaybillOrderAddress(waybillOrderAddresses, AddressTypeEnum.TAKE);
        createTicket(waybillOrder, waybillOrderSubtask, takeAddress, logOrder, truckNo);
        log.info("创建车辆运单-装卸货信息成功！");

    }

    /**
     * 运单地址(装货、卸货地址)
     *
     * @param waybillOrderAddresses
     * @param typeEnum
     * @return
     */
    private KwtWaybillOrderAddress getWaybillOrderAddress(List<KwtWaybillOrderAddress> waybillOrderAddresses, AddressTypeEnum typeEnum) {
        return waybillOrderAddresses.stream()
                .filter(address -> address != null && typeEnum.getCode() == address.getType())
                .findFirst()
                .orElseThrow(() -> new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_ADDRESS_NOT_FOUND, "未找到装货地址数据！"));
    }

    /**
     * 保存车辆运单-装卸货信息
     *
     * @param waybillOrder
     * @param waybillOrderSubtask
     * @param shipmentAddress
     */
    private void createTicket(KwtWaybillOrder waybillOrder, KwtWaybillOrderSubtask waybillOrderSubtask,
                              KwtWaybillOrderAddress shipmentAddress, KwtLogisticsOrder logOrder, RTruckVo truckNo) {
        KwtWaybillOrderTicket ticket = new KwtWaybillOrderTicket();
        ticket.setWOrderId(waybillOrder.getId());
        ticket.setWSubtaskId(waybillOrderSubtask.getId());
        ticket.setWAddressId(shipmentAddress.getId());
        ticket.setType(shipmentAddress.getAddressType());
        ticket.setUnit(logOrder.getUnit());
        ticket.setAmount(BigDecimal.ZERO);
        ticket.setGrossAmount(truckNo.getGrossWeight());
        ticket.setTareAmount(truckNo.getTareWeight());
        waybillOrderTicketRepository.save(ticket);
    }

    /**
     * 回写贸易订单数据
     * @param tradeOrder
     */
    private void updateTradeOrder(OrderDetailVo tradeOrder, BigDecimal truckLoadVolume) {
        log.info("更新上游贸易订单运输量数据，tradeOrderId：{}", tradeOrder.getId());
        // 计算当前车辆运输量
        BigDecimal totalLoadVolume = Optional.ofNullable(tradeOrder.getActualAmount())
                .map(entrust -> entrust.add(truckLoadVolume))
                .orElse(truckLoadVolume);

        UpdateActualAmountParam tradeParam = new UpdateActualAmountParam();
        tradeParam.setTOrderId(tradeOrder.getId());
        tradeParam.setActualLoadAmount(totalLoadVolume);
        tradeOrderInfoService.updateOrderActualAmount(tradeParam);
        log.info("更新上游贸易订单运输量成功, 贸易订单ID：{}", tradeOrder.getId());
    }


    /**
     * 回写物流订单数据
     * @param logOrder
     */
    private void updateLogOrder(KwtLogisticsOrder logOrder, BigDecimal truckLoadVolume) {
        log.info("更新上游物流运输量数据，logOrderId：{}", logOrder);
        // 计算当前车辆总运输量
        BigDecimal totalLoadVolume = Optional.ofNullable(logOrder.getEntrustAmount())
                .map(entrust -> entrust.add(truckLoadVolume))
                .orElse(truckLoadVolume);

        Integer totalTake = Optional.ofNullable(logOrder.getTotalTake())
                .map(val ->val + 1)
                .orElse(0);

        logOrder.setEntrustAmount(totalLoadVolume);
        logOrder.setTotalTake(totalTake);
        logisticsOrderRepository.updateById(logOrder);
        log.info("更新上游物流运输量成功, 物流订单ID：{}", logOrder.getId());
    }

    /**
     * 装卸货地址
     * @param logOrderIdList
     * @return
     */
    private Map<String, KwtLogisticsOrderAddress> getKwtLogisticsOrderAddressMap(List<Long> logOrderIdList) {
        List<KwtLogisticsOrderAddress> logOrderAddressList = logisticsOrderAddressRepository.queryByLogOrderIds(logOrderIdList);
        if (CollectionUtils.isEmpty(logOrderAddressList)) {
            log.info("物流订单无装卸货地址信息，logOrderIdList:{}", JSON.toJSONString(logOrderIdList));
            throw new BusinessPlatfromException(ErrorCodeEnum.LOGISTICS_ORDER_NOT_ADDRESS, "物流订单无装卸货地址信息");
        }
        return logOrderAddressList.stream().collect(Collectors
                .toMap(address -> address.getLOrderId() + "-" + address.getType(),
                        Function.identity(), (x, y) -> x));
    }

    @Override
    protected void updateStatus(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder) {
        // 状态已在 createWaybillOrder 中初始化为 PENDING_VEHICLE，无需额外更新
    }


    @Override
    protected String getProcessName() {
        return "接单";
    }

    @Override
    protected String getRemark(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder) {
        KwtWaybillOrder actualOrder = BusinessContext.get("createdWaybillOrder");
        KwtWaybillOrderSubtask subtask = getWaybillSubtask(actualOrder.getId());
        return  "司机[" + actualOrder.getDriverName() + "]接单成功，预计任务量[" + subtask.getEntrustAmount() + "吨]";
    }

    // 7. 接单需要单独创建接单轨迹数据
    protected void afterProcess(OrderCirculateTakingQueryParam param, KwtWaybillOrder waybillOrder) {
        KwtWaybillOrder actualOrder = BusinessContext.get("createdWaybillOrder");
        createNodeTrace(param, actualOrder);
    }


}
