package com.clx.performance.service.impl;

import com.alibaba.fastjson.JSON;
import com.clx.open.sdk.callback.OpenCallBackClient;
import com.clx.open.sdk.callback.message.OrderChildMessage;
import com.clx.open.sdk.enums.ResultStatusEnum;
import com.clx.order.enums.OrderEnum;
import com.clx.order.enums.ResultEnum;
import com.clx.order.enums.SyncPlatformEnum;
import com.clx.order.feign.OrderFeign;
import com.clx.order.vo.feign.FeignOrderVO;
import com.clx.performance.config.ThirdAppConfig;
import com.clx.performance.constant.RedisConstants;
import com.clx.performance.constant.RedissonConstants;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.enums.OrderChildEnum;
import com.clx.performance.model.OrderChild;
import com.clx.performance.service.LastTruckService;
import com.clx.performance.struct.OrderChildStruct;
import com.msl.common.exception.ServiceSystemException;
import com.msl.common.result.Result;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName LastTruckServiceImpl
 * @Description
 * @Author kavin
 * @Date 2023/11/9 13:13
 * @Version 1.0
 */
@Service
@Slf4j
@AllArgsConstructor
public class LastTruckServiceImpl implements LastTruckService {

    private final OrderFeign orderFeign;
    private final OrderChildDao orderChildDao;
    private final OrderChildStruct orderChildStruct;
    private final ThirdAppConfig thirdAppConfig;
    private final RedissonClient redissonClient;
    private final RedisTemplate<String, String> redisTemplate;

    /**
     * @Author kavin
     * @Description 同步最后一车
     * @Param [child]
     * @return
     **/
    @Override
    public Result<?> syncLastTruckChild(OrderChild child) {
        FeignOrderVO orderInfo = orderFeign.getOrderInfoFeign(child.getOrderNo());
        OrderChildMessage message = orderChildStruct.convertMessage(child);
        //数媒宝修改卸货之后的运单状态
        smbChangeOrderChildStatus(orderInfo,message);
        message.setLastFlag(ResultStatusEnum.YES.getCode());
        String data = JSON.toJSONString(message);
        log.info("开始同步最后一车的运单，运单编号:{},运单信息:{}",child.getChildNo(), data);
        OpenCallBackClient openCallBackClient = thirdAppConfig.getOpenCallBackClient(orderInfo.getOrderSource().toString());
        Result<?> result = openCallBackClient.encryptPost(data,message.topic());
        log.info("结束同步最后一车的运单，运单编号:{}，响应结果:{}",child.getChildNo(),JSON.toJSONString(result));
        if(!result.succeed()){
            log.error("结束同步最后一车的运单发生异常，运单编号:{}，响应结果:{}",child.getChildNo(),JSON.toJSONString(result));
        }
        return result;
    }


    //数媒宝卸车之后和完成之前的状态都作为卸车状态
    public void smbChangeOrderChildStatus(FeignOrderVO orderInfo,OrderChildMessage message){
        if(!Objects.equals(orderInfo.getOrderSource(), SyncPlatformEnum.Source.TRADE_PLATFORM.getCode())){
            return;
        }
        if(message.getStatus() > OrderChildEnum.Status.UNLOAD.getCode() && message.getStatus() <= OrderChildEnum.Status.COMPLETE.getCode()){
            message.setStatus(OrderChildEnum.Status.UNLOAD.getCode());
        }
    }


    //在没有算出最后一车的前提下，订单变成已完结或者已完成状态，但是订单量可能并没有提取完，所以只需要判断订单下是否还有未到达 目的地的运单，没有则进行最后一车运算
    @Override
    public OrderChild getOrderCompleteLastTruck(String orderNo) {
        String lastTruckCacheKey = getLastTruckCacheKey(orderNo);
        //已经计算出最后一车，则直接返回运单信息
        if(Objects.equals(Boolean.TRUE,redisTemplate.hasKey(lastTruckCacheKey))){
            String childNo = redisTemplate.opsForValue().get(lastTruckCacheKey);
            return orderChildDao.getByChildNo(childNo).orElseThrow(ResultEnum.DATA_NOT_FIND);
        }else{
            //判断当前订单下是否存在未到达目的地之前的运单 && 排除掉已取消的运单
            Long count = orderChildDao.haveArriveReceiveChild(orderNo);
            log.info("已完成/已完结的订单号:{},查询运单状态非取消并且在到达目的地之前的运单数量为:{}",orderNo,count);
            if(count  == 0 ){
                //说明排除已取消的运单后，剩下的所有运单都已经是到达目的地之后的状态，根据到达目的地的时间倒序取出第一条运单作为最后一车
                return orderChildDao.getOrderCompleteLastTruck(orderNo);
            }
            return null;
        }
    }



    /**
     * @Author kavin
     * @Description 订单取消、到达最晚拉运时间 同步最后一车
     * @Param [orderNo]
     * @return
     **/
    public Result<?> syncLastTruckChild4OrderTrigger(String orderNo) {
        OrderChild child = getOrderCompleteLastTruck(orderNo);
        if(Objects.nonNull(child)){
            return syncLastTruckChild(child);
        }
        return Result.ok("订单完成/完结状态下未查询最后一车，订单编号:" + orderNo);
    }

    //调用此方法前查订单是否存在最后一车，存在则不进行该方法调用
    @Override
    public OrderChild getOrderLastTruck(String orderNo){
        FeignOrderVO orderInfo = orderFeign.getOrderInfoFeign(orderNo);
        if(Objects.isNull(orderInfo)){
            log.error("计算订单最后一车，通过订单号:{},未查询到对应的订单",orderNo);
            return null;
        }

        //为每个货主的订单进行加锁，计算最后一车进行排队，防止多个运单dts监听并发导致最后一车重复计算
        RLock lock = redissonClient.getLock(RedissonConstants.CALC_ORDER_LAST_TRUCK_LOCK + orderNo);
        try{
            boolean flag = lock.tryLock(15, 30, TimeUnit.SECONDS);
            if (!flag) {
                throw new ServiceSystemException(ResultEnum.TRY_LOCK_ERROR,"计算最后一车请求排队超时");
            }

            String lastTruckCacheKey = getLastTruckCacheKey(orderNo);
            //已经计算出最后一车，则直接返回运单信息
            if(Objects.equals(Boolean.TRUE,redisTemplate.hasKey(lastTruckCacheKey))){
                String childNo = redisTemplate.opsForValue().get(lastTruckCacheKey);
                return orderChildDao.getByChildNo(childNo).orElseThrow(ResultEnum.DATA_NOT_FIND);
            }

            BigDecimal weight = orderChildDao.listAfterArrayReceiveChild(orderNo);
            log.info("计算订单最后一车，订单号:{} ,计算是否到达可计算最后一车标准，运单累计拉运吨数：{}, 订单拉运吨数:{}",
                    orderNo,weight,orderInfo.getTransportWeight());

            if(Objects.nonNull(weight) && weight.subtract(orderInfo.getTransportWeight()).compareTo(BigDecimal.ZERO) >= 0){
                //查询最后一车的运单
                OrderChild child = orderChildDao.getLastTruckChild(orderNo);
                log.info("当前订单计算出最后一车，运单编号：{} ", child.getChildNo());
                //更新订单最后一车到缓存,并设置有效期为60天
                redisTemplate.opsForValue().set(getLastTruckCacheKey(orderNo),child.getChildNo(),60, TimeUnit.DAYS);
                return child;
            }
            return null;
        } catch (InterruptedException e){
            log.warn("计算订单最后一车发生异常，异常原因:{}", ExceptionUtils.getStackTrace(e));
        }finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return null;
    }

    public String getLastTruckCacheKey(String orderNo){
        return RedisConstants.ORDER_LAST_TRUCK_CHILD + orderNo;
    }




    @Override
    public Result<?> syncLastTruck(FeignOrderVO orderInfoFeign, OrderChildMessage after) {
        //当运单状态为到达目的地之后的状态时候，需要判断最后一车的逻辑
        if(Objects.equals(com.clx.open.sdk.enums.OrderChildEnum.Status.COMMON_CANCEL.getCode(),after.getStatus()) ||
                (after.getStatus() >= OrderChildEnum.Status.ARRIVE_RECEIVE.getCode()  && after.getStatus() <= OrderChildEnum.Status.COMPLETE.getCode())){
            OrderChild child;
            if(Objects.equals(orderInfoFeign.getOrderStatus(), OrderEnum.Status.COMPLETED.getCode()) ||
                    Objects.equals(orderInfoFeign.getOrderStatus(), OrderEnum.Status.SUCCESS.getCode())) { //订单变成已完成 || 已完结
                OrderEnum.Status byCode = OrderEnum.Status.getByCode(orderInfoFeign.getOrderStatus());
                log.info("订单号:{},运单号：{},订单状态：{},开始查询最后一车运单===========" ,orderInfoFeign.getOrderNo(),after.getChildNo(),
                        Objects.nonNull(byCode)?byCode.getName():"未知状态");
                child = this.getOrderCompleteLastTruck(after.getOrderNo());
            }else{  // 订单除  已完成 || 已完结  外的其他状态
                child = this.getOrderLastTruck(after.getOrderNo());
            }
            if(Objects.nonNull(child)){
                return this.syncLastTruckChild(child);
            }

        }
        return Result.ok("未查询到最后一车，不进行同步最后一车");
    }
    /*
     * @Author kavin
     * @Description
     * @Param [orderNo, childNo]  运单号可以是该订单下任意一个运单，为了是触发最后一车
     * @return
     **/

    @Override
    public Result<?> handExecSyncSmbLastTruck(String orderNo,String childNo){
        FeignOrderVO orderInfoFeign = orderFeign.getOrderInfoFeign(orderNo);
        OrderChild child = orderChildDao.getByChildNo(childNo).get();
        OrderChildMessage message = orderChildStruct.convertMessage(child);
        return syncLastTruck(orderInfoFeign,message);
    }





}
