package com.clx.performance.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clx.open.sdk.callback.message.OrderInfoMessage;
import com.clx.order.vo.feign.FeignAddressVO;
import com.clx.order.vo.feign.FeignOrderInfoVO;
import com.clx.order.vo.feign.SystemAddressVO;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.dao.OrderGoodsDao;
import com.clx.performance.dao.PerformanceProgressDao;
import com.clx.performance.dao.PerformanceProgressLogDao;
import com.clx.performance.dto.gd.GdRouteDTO;
import com.clx.performance.enums.OrderChildEnum;
import com.clx.performance.enums.OrderEnum;
import com.clx.performance.enums.PerformanceProgressEnum;
import com.clx.performance.enums.ResultEnum;
import com.clx.performance.extranal.user.AddressService;
import com.clx.performance.extranal.user.OrderService;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.OrderGoods;
import com.clx.performance.model.PerformanceProgress;
import com.clx.performance.model.PerformanceProgressLog;
import com.clx.performance.param.pc.carrier.PagePerformanceProgress;
import com.clx.performance.param.pc.carrier.UpdatePerformanceProgressParam;
import com.clx.performance.service.PerformanceProgressLogService;
import com.clx.performance.service.PerformanceProgressService;
import com.clx.performance.struct.PerformanceProgressLogStruct;
import com.clx.performance.struct.PerformanceProgressStruct;
import com.clx.performance.utils.gd.GdService;
import com.clx.performance.vo.pc.PerformanceProgressDetailVO;
import com.clx.performance.vo.pc.PerformanceProgressOperationLogVO;
import com.clx.performance.vo.pc.PerformanceProgressVO;
import com.msl.common.base.Optional;
import com.msl.common.exception.ServiceSystemException;
import com.msl.common.utils.DateUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author kavin
 * Date 2024-07-12
 * Time 16:02
 */
@Service
@Slf4j
@AllArgsConstructor
public class PerformanceProgressServiceImpl  implements PerformanceProgressService {

    private final PerformanceProgressDao performanceProgressDao;
    private final PerformanceProgressStruct performanceProgressStruct;
    private final PerformanceProgressLogDao performanceProgressLogDao;
    private final PerformanceProgressLogStruct performanceProgressLogStruct;
    private final PerformanceProgressLogService performanceProgressLogService;
    private final AddressService addressService;
    private final GdService gdService;
    private final OrderGoodsDao orderGoodsDao;
    private final OrderChildDao orderChildDao;
    private final OrderService orderService;


    public static List<Integer> inProcessStatusList;
    public static List<Integer> endStatusList ;
    public static List<Integer> allStatusList ;


    static {
       inProcessStatusList = Arrays.asList(
                OrderEnum.Status.PLATFORM_UNDERTAKING.getCode(),
                OrderEnum.Status.SUSPEND.getCode(),
                OrderEnum.Status.ON_ORDER.getCode(),
                OrderEnum.Status.IN_TRANSIT.getCode());

        endStatusList = Arrays.asList(
                OrderEnum.Status.SUCCESS.getCode(),
                OrderEnum.Status.COMPLETED.getCode());


        allStatusList = Arrays.asList(
                OrderEnum.Status.PLATFORM_UNDERTAKING.getCode(),
                OrderEnum.Status.SUSPEND.getCode(),
                OrderEnum.Status.ON_ORDER.getCode(),
                OrderEnum.Status.IN_TRANSIT.getCode(),
                OrderEnum.Status.SUCCESS.getCode(),
                OrderEnum.Status.COMPLETED.getCode());

    }


    @Override
    public IPage<PerformanceProgressVO> pagePerformanceProgress(PagePerformanceProgress param) {

        IPage<PerformanceProgress> page = new Page<>();
        IPage<PerformanceProgressVO> returnPage = new Page<>();


        if(Objects.equals(param.getTab(), PerformanceProgressEnum.Tab.IN_PROCESS.getCode())){
            param.setPage(1);
            param.setPageSize(10000);
            page =  performanceProgressDao.pagePerformanceProgress(inProcessStatusList,param);

        }else if(Objects.equals(param.getTab(), PerformanceProgressEnum.Tab.END.getCode())){
            page =  performanceProgressDao.pagePerformanceProgress(endStatusList,param);

        }else if(Objects.equals(param.getTab(), PerformanceProgressEnum.Tab.ALL.getCode())){
            page =  performanceProgressDao.pagePerformanceProgress(allStatusList,param);

        }
        if(CollectionUtils.isEmpty(page.getRecords())){
            return returnPage;
        }

        List<PerformanceProgressVO> records = performanceProgressStruct.convertList(page.getRecords());

        returnPage.setPages(page.getPages());
        returnPage.setTotal(page.getTotal());
        returnPage.setRecords(records);


        //如果查询的是 已结束线路,则不需要计算，直接返回
        if(Objects.equals(param.getTab(), PerformanceProgressEnum.Tab.END.getCode())){
            return returnPage;
        }

        returnPage.getRecords().forEach(item ->{
            //TODO  进行订单运费预估调用  开发批量调用的接口。 订单完结完成后把运费预估查询出来的数据存入到数据库，后续不再实时查询
            if(inProcessStatusList.contains(item.getOrderStatus())){  //进行中的订单线路

            }
        });

        return returnPage;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAdjustOrder(Integer adjustOrderId, Integer adjustOrderBeforeId) {
        Optional<PerformanceProgress> one = performanceProgressDao.getEntityByKey(adjustOrderId);
        //如果上调，这条记录传的是移动数据后面的记录id，如果下调，这条记录传的是移动数据前面的记录id
        Optional<PerformanceProgress> two = performanceProgressDao.getEntityByKey(adjustOrderBeforeId);
        if(!one.isPresent() || !two.isPresent()){
            throw new ServiceSystemException(ResultEnum.DATA_NOT_FIND);
        }
        boolean isUp = false;
        //通过seq判断是上调还是下调
        if(one.get().getSeq()  <  two.get().getSeq()){
            isUp = true;
        }
        //上调：大于two 这条记录的seq + 1 ； 下调：大于等于two这条记录的seq + 1 ;
        performanceProgressDao.updateRecordOrder(two.get().getSeq(),isUp);
        if(isUp){   //上调
            //调整的记录使用 two 的seq + 1;
            PerformanceProgress updateOne = new PerformanceProgress();
            updateOne.setId(one.get().getId());
            updateOne.setSeq(two.get().getSeq() + 1);
            performanceProgressDao.updateEntityByKey(updateOne);
        }else{    //下调
            //调整的记录使用 two 的seq;
            PerformanceProgress updateOne = new PerformanceProgress();
            updateOne.setId(one.get().getId());
            updateOne.setSeq(two.get().getSeq());
            performanceProgressDao.updateEntityByKey(updateOne);
        }
    }

    //通过dts监听订单、货单、运单表进行更新履约进度表的数据
    @Override
    public void saveOrUpdatePerformanceProgress(PerformanceProgress item) {
        Optional<PerformanceProgress> optional = performanceProgressDao.getOneByField(PerformanceProgress::getOrderNo,
                item.getOrderNo());
        if(optional.isPresent()){
            item.setId(optional.get().getId());
            performanceProgressDao.updateEntityByKey(item);
        }else{
            performanceProgressDao.saveEntity(item);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePerformanceProgress(UpdatePerformanceProgressParam param, Long userNo, String userName) {
        PerformanceProgress item = performanceProgressDao.getEntityByKey(param.getId()).
                orElseThrow(ResultEnum.DATA_NOT_FIND);
        PerformanceProgress update = new PerformanceProgress();
        update.setId(param.getId());
        List<PerformanceProgressLog> logs = new ArrayList<>();
        boolean change = false;
        BigDecimal todayExpectComplete = Objects.nonNull(item.getTodayExpectComplete())
                ?item.getTodayExpectComplete():BigDecimal.ZERO;

        if(Objects.nonNull(param.getTodayExpectComplete())
                && todayExpectComplete.compareTo(param.getTodayExpectComplete()) != 0){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.TODAY_EXPECT_COMPLETE,
                    param.getTodayExpectComplete(),userNo,userName);
            update.setTodayExpectComplete(param.getTodayExpectComplete());
            logs.add(log);
            change = true;
        }

        if(!StringUtils.equals(item.getTradeRequireArriveStationTime(),param.getTradeRequireArriveStationTime())){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.TRADE_REQUIRE_ARRIVE_STATION_TIME,
                    param.getTradeRequireArriveStationTime(),userNo,userName);
            update.setTradeRequireArriveStationTime(param.getTradeRequireArriveStationTime());
            logs.add(log);
            change = true;
        }

        if(!StringUtils.equals(item.getTransportExpectArriveStationTime(),param.getTransportExpectArriveStationTime())){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.TRADE_REQUIRE_ARRIVE_STATION_TIME,
                    param.getTransportExpectArriveStationTime(),userNo,userName);
            update.setTransportExpectArriveStationTime(param.getTransportExpectArriveStationTime());
            logs.add(log);
            change = true;
        }

        if(!StringUtils.equals(item.getAbnormalRemark(),param.getAbnormalRemark())){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.ABNORMAL_REMARK,
                    param.getAbnormalRemark(),userNo,userName);
            update.setAbnormalRemark(param.getAbnormalRemark());
            logs.add(log);
            change = true;
        }
        if(!StringUtils.equals(item.getPerformanceAbnormalReason(),param.getPerformanceAbnormalReason())){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.PERFORMANCE_ABNORMAL_REASON,
                    param.getPerformanceAbnormalReason(),userNo,userName);
            update.setPerformanceAbnormalReason(param.getPerformanceAbnormalReason());
            logs.add(log);
            change = true;
        }
        if(!StringUtils.equals(item.getDispatchFollow(),param.getDispatchFollow())){
            PerformanceProgressLog log = performanceProgressLogService.generateLog(item.getOrderNo(),
                    PerformanceProgressEnum.LogType.DISPATCH_FOLLOW,
                    param.getDispatchFollow(),userNo,userName);
            update.setDispatchFollow(param.getDispatchFollow());
            logs.add(log);
            change = true;
        }

        if(change){
            performanceProgressDao.updateEntityByKey(update);
            performanceProgressLogDao.saveBatchList(logs);
        }
    }

    @Override
    public PerformanceProgressDetailVO getPerformanceProgressDetail(Integer id) {
        PerformanceProgress item = performanceProgressDao.getEntityByKey(id).orElseThrow(ResultEnum.DATA_NOT_FIND);
        return performanceProgressStruct.convert(item);
    }

    @Override
    public List<PerformanceProgressOperationLogVO> getOperationLog(String orderNo) {
        List<PerformanceProgressLog> list = performanceProgressLogDao.getOperationLog(orderNo);
        return performanceProgressLogStruct.convertList(list);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPerformanceProgress4OrderInfo(OrderInfoMessage data) {
        //如果取消，删除履约进度表这条记录
        if(Objects.equals(data.getOrderStatus(),OrderEnum.Status.CANCELED.getCode())){
            performanceProgressDao.deleteByField(PerformanceProgress::getOrderNo,data.getOrderNo());
        }

        Optional<PerformanceProgress> optional = performanceProgressDao.getOneByField(PerformanceProgress::getOrderNo,
                data.getOrderNo());

        PerformanceProgress item = new PerformanceProgress();

        item.setOrderStatus(data.getOrderStatus());
        item.setGoodsTypeCode(data.getGoodsTypeCode());
        item.setGoodsTypeName(data.getGoodsTypeName());
        item.setGoodsNameId(data.getGoodsNameId());
        item.setGoodsName(data.getGoodsName());
        item.setSendOverStandard(data.getOverWeight());
        item.setTaskWeight(data.getTransportWeight().subtract(new BigDecimal(35)));
        item.setPendingWeight(data.getTransportWeight().subtract(data.getResidueWeight()));

        item.setTransportTimeSlot(DateUtils.formatDateTime(data.getTransportBeginTime()).get() + "至" +
                DateUtils.formatDateTime(data.getTransportEndTime()).get());

        if(Objects.equals(data.getOrderStatus(),OrderEnum.Status.COMPLETED.getCode()) ||
                Objects.equals(data.getOrderStatus(),OrderEnum.Status.SUCCESS.getCode())
        ){
            //TODO 计算测算运费

        }

        if(optional.isPresent()){
            item.setId(optional.get().getId());
            if(Objects.nonNull(data.getSendSystemAddressId()) &&
                    !Objects.equals(optional.get().getSendSystemAddressId(),data.getSendSystemAddressId())){
                item.setSendSystemAddressId(data.getSendSystemAddressId());
                item.setSendSystemAddressShorter(getSystemAddressShorter(data.getSendSystemAddressId()));
            }
            performanceProgressDao.updateEntityByKey(item);
        }else{
            //查询最大的seq
            long seq = performanceProgressDao.getMaxSeq();
            item.setSeq(seq + 1);

            Optional<FeignAddressVO> sendAndReceiveAddress = addressService.getSendAndReceiveAddress(
                    data.getSendAddressId(), data.getReveiveAddressId());

            if (!sendAndReceiveAddress.isPresent()) {
                log.warn("通过发货地址Id:{},收货地址Id：{} 查询对应的地址信息结果：{}",
                        data.getSendAddressId(),data.getReveiveAddressId(),sendAndReceiveAddress);
                throw new ServiceSystemException(ResultEnum.DATA_ERROR,"履约进度表订单对应的收发货地址信息为空");
            }
            FeignAddressVO.Address sendAddress = sendAndReceiveAddress.get().getSendAddress();
            FeignAddressVO.Address receiveAddress = sendAndReceiveAddress.get().getReceiveAddress();

            BigDecimal distance = null;
            //重车运距（收发货地址距离）
            try{
                List<GdRouteDTO> route = gdService.getRoute(sendAddress.getLongitude(), sendAddress.getLatitude(),
                        receiveAddress.getLongitude(), receiveAddress.getLatitude());
                if (!route.isEmpty()){
                    distance = new BigDecimal(route.get(0).getDistance() /1000);
                }
            }catch (Exception e){
                log.warn("订单计算重车运距失败，订单号:{},失败原因：{}",data.getOrderNo(), ExceptionUtils.getStackTrace(e));
            }

            item.setOrderDistance(distance);
            item.setOrderNo(data.getOrderNo());

            if(Objects.nonNull(data.getSendSystemAddressId())){
                item.setSendSystemAddressId(data.getSendSystemAddressId());
                item.setSendSystemAddressShorter(getSystemAddressShorter(data.getSendSystemAddressId()));
            }
            item.setSendAddressId(data.getSendAddressId());
            item.setSendAddressShorter(data.getSendAddressShorter());
            item.setSendAddress(sendAddress.getProvince()+sendAddress.getCity()+sendAddress.getCounty()
                    + sendAddress.getAddress());
            performanceProgressDao.saveEntity(item);
        }
    }

    private String getSystemAddressShorter(Integer addressId){
        Optional<SystemAddressVO> systemAddress = addressService.getSystemAddress(addressId);
        if (!systemAddress.isPresent()) {
            log.warn("通过发货系统地址Id:{},查询对应的地址信息结果：{}",
                    addressId,systemAddress);
            throw new ServiceSystemException(ResultEnum.DATA_ERROR,"履约进度表订单对应的系统发货地址信息为空");
        }
        return systemAddress.get().getAddressShorter();
    }

    //履约进度表----货单部分
    @Override
    public void dealPerformanceProgress4OrderGoods(OrderGoods data) {
        Optional<PerformanceProgress> optional = performanceProgressDao.getOneByField(PerformanceProgress::getOrderNo,
                data.getOrderNo());
        if(!optional.isPresent()){
            log.error("货单对应的订单信息不存在,订单号:{},货单号:{}",data.getOrderNo(),data.getOrderGoodsNo());
            throw new ServiceSystemException(ResultEnum.DATA_NOT_FIND);
        }
        //根据挂单时间倒序排序
        List<OrderGoods> orderGoods = orderGoodsDao.listAfterCreatedByOrderNo(data.getOrderNo());
        if(CollectionUtils.isEmpty(orderGoods)){
            return;
        }
        Set<String> seniorLogisticsManagerNameSet = new HashSet<>();
        BigDecimal pendingOrderFreight =  null;  //最新的货单的司机运费，取消的也算
        LocalDateTime firstPendingTime = null;  //挂单时间，取消的也算
        for(OrderGoods item : orderGoods){
            seniorLogisticsManagerNameSet.add(item.getSeniorLogisticsManagerName());
            if(Objects.isNull(pendingOrderFreight)){
                pendingOrderFreight = item.getPendingOrderFreight();
            }
            //最后一个挂单中的货单的挂单时间为最早的挂单时间
            firstPendingTime = item.getPendingOrderTime();
        }
        PerformanceProgress update = new PerformanceProgress();
        update.setId(optional.get().getId());
        update.setSeniorLogisticsManagerName(JSON.toJSONString(new ArrayList<>(seniorLogisticsManagerNameSet)));
        update.setDriverFreightPrice(pendingOrderFreight);
        update.setPendingTime(firstPendingTime);
        performanceProgressDao.updateEntityByKey(update);
    }
    //履约进度表----运单部分
    @Override
    public void dealPerformanceProgress4OrderChild(OrderChild data) {
        Optional<PerformanceProgress> optional = performanceProgressDao.getOneByField(PerformanceProgress::getOrderNo,
                data.getOrderNo());
        if(!optional.isPresent()){
            log.error("运单对应的订单信息不存在,订单号:{},运单号:{}",data.getOrderNo(),data.getChildNo());
            throw new ServiceSystemException(ResultEnum.DATA_NOT_FIND);
        }
        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(data.getOrderNo()).get();

        //取完成之前的运单数据
        List<OrderChild> orderChildList = orderChildDao.listBeforeCompleteByOrderNo(data.getOrderNo());
        int orderedTruckNum = orderChildList.size();  //接单车数
        int arriveSendTruckNum = 0; //到达货源地车数
        int loadTruckNum = 0;  //装车车数
        int unloadTruckNum = 0;   //卸车车数
        int onTheWayTruckNum ;    // 在途车数
        BigDecimal orderedRate;       //接单率
        BigDecimal taskCompleteRatio ;    //任务完成率
        BigDecimal sumLoadWeight = BigDecimal.ZERO;    //矿发吨数
        BigDecimal sumUnloadWeight = BigDecimal.ZERO;    //到站吨数
        BigDecimal sumOnTheWayWeight ;   //在途吨数

        BigDecimal orderedWeight = BigDecimal.ZERO;     //接单吨数

        for (OrderChild child : orderChildList) {
            if(child.getStatus() >= OrderChildEnum.Status.ARRIVE_SEND.getCode()){
                arriveSendTruckNum = arriveSendTruckNum + 1;
            }
            if(child.getStatus() >= OrderChildEnum.Status.LOAD.getCode()){
                loadTruckNum = loadTruckNum + 1;
                sumLoadWeight = sumLoadWeight.add(child.getLoadNet());
                orderedWeight =  orderedWeight.add(child.getLoadNet());
            }else{
                orderedWeight =  orderedWeight.add(child.getWeight());
            }
            if(child.getStatus() >= OrderChildEnum.Status.UNLOAD.getCode()){
                unloadTruckNum = unloadTruckNum + 1;
                sumUnloadWeight = sumUnloadWeight.add(child.getWeight());

            }
        }
        //在途吨数 = 矿发量 - 到站量
        sumOnTheWayWeight = sumLoadWeight.subtract(sumUnloadWeight);

        //在途车辆数 = 装车车数 - 卸车车数
        onTheWayTruckNum = loadTruckNum - unloadTruckNum;

        //接单率 =  接单吨数 / 挂单吨数
        orderedRate = orderedWeight .divide(optional.get().getPendingWeight(),3, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100));

        //任务完成率 =  到站量 / 任务量
        taskCompleteRatio =  sumUnloadWeight.divide(orderInfo.getTransportWeight(),3, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100));


        PerformanceProgress update = new PerformanceProgress();
        update.setId(optional.get().getId());
        update.setOrderedTruckNum(orderedTruckNum);
        update.setArriveSendTruckNum(arriveSendTruckNum);
        update.setLoadTruckNum(loadTruckNum);
        update.setUnloadTruckNum(unloadTruckNum);
        update.setOnTheWayTruckNum(onTheWayTruckNum);
        update.setOrderedRate(orderedRate);
        update.setTaskCompleteRatio(taskCompleteRatio);
        update.setSumLoadWeight(sumLoadWeight);
        update.setSumUnloadWeight(sumUnloadWeight);
        update.setSumOnTheWayWeight(sumOnTheWayWeight);
        performanceProgressDao.updateEntityByKey(update);
    }
}
