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.ResultEnum;
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.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.lang.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);
        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));
        return result;
    }

    //在没有算出最后一车的前提下，订单变成已完结或者已完成状态，但是订单量可能并没有提取完，所以只需要判断订单下是否还有未到达 目的地的运单，没有则进行最后一车运算
    @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{
            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);
            }
            boolean flag = lock.tryLock(15, 30, TimeUnit.SECONDS);
            if (!flag) {
                throw new ServiceSystemException(ResultEnum.TRY_LOCK_ERROR,"计算最后一车请求排队超时");
            }

            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;
    }
}
