package com.clx.performance.service.impl;

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.param.mq.SyncPendingFreightInternalReportParam;
import com.clx.order.vo.feign.FeignAddressVO;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.dao.OrderGoodsDao;
import com.clx.performance.dao.PendingFreightDao;
import com.clx.performance.dto.OrderedAndLoadTruckRateDTO;
import com.clx.performance.dto.gd.GdRouteDTO;
import com.clx.performance.enums.ResultEnum;
import com.clx.performance.extranal.user.AddressService;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.OrderGoods;
import com.clx.performance.model.PendingFreight;
import com.clx.performance.param.pc.PagePendingFreightParam;
import com.clx.performance.service.PendingFreightService;
import com.clx.performance.service.PerformanceProgressService;
import com.clx.performance.struct.PendingFreightStruct;
import com.clx.performance.utils.excel.ExcelData;
import com.clx.performance.utils.excel.ExcelField;
import com.clx.performance.utils.excel.ExcelSheet;
import com.clx.performance.utils.excel.ExcelUtil;
import com.clx.performance.utils.gd.GdService;
import com.clx.performance.vo.pc.PagePendingFreightVO;
import com.msl.common.base.Optional;
import com.msl.common.exception.ServiceSystemException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
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.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author kavin
 * Date 2024-10-17
 * Time 19:37
 */
@Service
@Slf4j
@AllArgsConstructor
public class PendingFreightServiceImpl  implements PendingFreightService {


    private final PendingFreightDao pendingFreightDao;
    private final AddressService addressService;
    private final GdService gdService;
    private final PerformanceProgressService performanceProgressService;
    private final OrderGoodsDao orderGoodsDao;
    private final OrderChildDao orderChildDao;
    private final PendingFreightStruct pendingFreightStruct;


    //处理挂单运费订单部分
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPendingFreight4OrderInfo(OrderInfoMessage data) {

        Optional<PendingFreight> pendingFreightOptional = pendingFreightDao.getOneByField(PendingFreight::getOrderNo,
                data.getOrderNo());

        PendingFreight item = new PendingFreight();

        item.setOrderStatus(data.getOrderStatus());
        item.setSendAddressId(data.getSendAddressId());
        item.setSendAddressShorter(data.getSendAddressShorter());
        item.setGoodsModel(data.getGoodsModel());
        item.setGoodsNameId(data.getGoodsNameId());
        item.setGoodsName(data.getGoodsName());
        item.setSendOverStandard(data.getOverWeight());
        item.setTaskWeight(data.getTransportWeight());
        item.setSendAddressExpenses(data.getSendPayFeeBegin().movePointLeft(2)+"元-"+ data.getSendPayFeeEnd().movePointLeft(2) +"元");

        if(pendingFreightOptional.isPresent()){
            PendingFreight pendingFreight = pendingFreightOptional.get();
            item.setId(pendingFreight.getId());

            if(!Objects.equals(pendingFreight.getSendAddressId(),data.getSendAddressId())
            || !Objects.equals(pendingFreight.getReceiveAddressId(),data.getReveiveAddressId())){
                //查询收发货地址
                Optional<FeignAddressVO> sendAndReceiveAddress = getAddressDetail(data);
                FeignAddressVO.Address sendAddress = sendAndReceiveAddress.get().getSendAddress();
                FeignAddressVO.Address receiveAddress = sendAndReceiveAddress.get().getReceiveAddress();

                item.setSendAddress(addressService.generateAddressDetail(sendAddress.getProvince(),
                        sendAddress.getCity(),sendAddress.getCounty(),sendAddress.getAddress()));
                //计算重车运距
                item.setOrderDistance(calcOrderDistance(sendAddress,receiveAddress,data.getOrderNo()));

            }
            pendingFreightDao.updateEntityByKey(item);
        }else{
            item.setOrderNo(data.getOrderNo());

            //查询收发货地址
            Optional<FeignAddressVO> sendAndReceiveAddress = getAddressDetail(data);
            FeignAddressVO.Address sendAddress = sendAndReceiveAddress.get().getSendAddress();
            FeignAddressVO.Address receiveAddress = sendAndReceiveAddress.get().getReceiveAddress();
            item.setSendAddress(addressService.generateAddressDetail(sendAddress.getProvince(),
                    sendAddress.getCity(),sendAddress.getCounty(),sendAddress.getAddress()));
            //计算重车运距
            item.setOrderDistance(calcOrderDistance(sendAddress,receiveAddress,data.getOrderNo()));
            pendingFreightDao.saveEntity(item);
        }
    }

    //处理挂单运费内部上报数据
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPendingFreight4InternalReport(SyncPendingFreightInternalReportParam param) {
        log.info("开始更新挂单运费===内部上报数据,订单号:{} ,物流经理：{} ,物流运费:{} ",param.getOrderNo(),param.getName(),param.getLogisticsFreight());
        long count = pendingFreightDao.updateInternalReportData(param.getOrderNo(),param.getName(),param.getLogisticsFreight());
        log.info("结束更新挂单运费===内部上报数据,订单号:{} ,更新数量：{} ",param.getOrderNo(),count);
    }


    //处理挂单运费货单数据
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPendingFreight4OrderGoods(OrderGoods orderGoods) {
        String orderNo = orderGoods.getOrderNo();
        //计算接单率和装车率
        OrderedAndLoadTruckRateDTO dto = calcOrderedAndLoadTruckRate(orderNo);
        //计算线路挂单运费
        BigDecimal pendingFreight = this.queryLinePendingFreight(orderGoods.getSendAddressId(),
                orderGoods.getReceiveAddressId());
        //计算线路平均挂单运费
        BigDecimal avgPendingFreight = this.queryLineAvgPendingFreight(orderGoods.getSendAddressId(),
                orderGoods.getReceiveAddressId());

        pendingFreightDao.updateOrderedAndLoadTruckRate(orderNo,dto.getOrderedRate(),dto.getLoadTruckRate());

        pendingFreightDao.updatePendingFreight(orderNo,pendingFreight,avgPendingFreight);

    }


    //查询该线路最新的历史运费
    public BigDecimal queryLinePendingFreight(Integer sendAddressId,Integer receiveAddressId){
        OrderGoods orderGoods = orderGoodsDao.queryLineNewestPendingFreight(sendAddressId,receiveAddressId);
        return orderGoods.getPendingOrderFreight();
    }


    //查询该线路最新的历史平均运费
    public BigDecimal queryLineAvgPendingFreight(Integer sendAddressId,Integer receiveAddressId){
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime monthAgo  = now.minusMonths(1);

        List<OrderGoods> list = orderGoodsDao.queryLineAvgPendingFreight(sendAddressId,receiveAddressId,monthAgo,now);
        if(CollectionUtils.isEmpty(list)){
            return null;
        }
        BigDecimal total = BigDecimal.ZERO;
        for (OrderGoods orderGoods : list) {
            total = total.add(orderGoods.getPendingOrderFreight());
        }
        return total.divide(new BigDecimal(list.size()),0, RoundingMode.HALF_UP);
    }

    //处理挂单运费运单数据
    @Override
    public void dealPendingFreight4OrderChild(String orderNo) {
        OrderedAndLoadTruckRateDTO dto = calcOrderedAndLoadTruckRate(orderNo);
        pendingFreightDao.updateOrderedAndLoadTruckRate(orderNo,dto.getOrderedRate(),dto.getLoadTruckRate());
    }

    //计算接单率和装车率
    public OrderedAndLoadTruckRateDTO calcOrderedAndLoadTruckRate(String orderNo){
        List<OrderGoods> orderGoods = orderGoodsDao.listByField(OrderGoods::getOrderNo, orderNo);

        BigDecimal pendingWeight = BigDecimal.ZERO; //挂单量
        BigDecimal alreadyTransportWeight = BigDecimal.ZERO; //已拉运吨数

        BigDecimal orderedRate;  //接单率
        BigDecimal loadTruckRate;  //装车率
        BigDecimal loadNet  = BigDecimal.ZERO;  //总的装车吨数

        for (OrderGoods item : orderGoods) {
            alreadyTransportWeight = alreadyTransportWeight.add(item.getAlreadyTransportWeight());
            pendingWeight = pendingWeight.add(performanceProgressService.calcPendingWeight(item));
        }

        //计算接单率
        orderedRate  = alreadyTransportWeight.divide(pendingWeight,3, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));

        //计算装车率
        List<OrderChild> childList = orderChildDao.listNoCancelOrderChild(orderNo);
        if(CollectionUtils.isNotEmpty(childList)){
            for (OrderChild child : childList) {
                if(Objects.nonNull(child.getLoadNet())){
                    loadNet = loadNet.add(child.getLoadNet());
                }
            }
        }
        loadTruckRate  = loadNet.divide(pendingWeight,3, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
        OrderedAndLoadTruckRateDTO dto = new OrderedAndLoadTruckRateDTO();
        dto.setOrderNo(orderNo);
        dto.setPendingWeight(pendingWeight);
        dto.setAlreadyTransportWeight(alreadyTransportWeight);
        dto.setLoadNet(loadNet);
        dto.setLoadTruckRate(loadTruckRate);
        dto.setOrderedRate(orderedRate);
        return dto;
    }

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

        if (Objects.isNull(sendAndReceiveAddress.get().getSendAddress())  ||
                Objects.isNull(sendAndReceiveAddress.get().getReceiveAddress())) {
            log.warn("通过发货地址Id:{},收货地址Id：{} 查询对应的地址信息结果：{}",
                    data.getSendAddressId(),data.getReveiveAddressId(),sendAndReceiveAddress);
            throw new ServiceSystemException(ResultEnum.DATA_ERROR,"挂单运费表订单对应的收发货地址信息为空");
        }
        return sendAndReceiveAddress;
    }

    public BigDecimal calcOrderDistance(FeignAddressVO.Address sendAddress,FeignAddressVO.Address receiveAddress,String orderNo){
        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("订单计算重车运距失败，订单号:{},失败原因：{}",orderNo, ExceptionUtils.getStackTrace(e));
        }
        return distance;
    }

    @Override
    public IPage<PagePendingFreightVO> pagePendingFreight(PagePendingFreightParam param) {
        IPage<PendingFreight> page  = pendingFreightDao.pagePendingFreight(param);
        List<PagePendingFreightVO> list = pendingFreightStruct.convertList(page.getRecords());
        return new Page<PagePendingFreightVO>().setRecords(list).setTotal(page.getTotal()).setPages(page.getPages());
    }



    @Override
    public SXSSFWorkbook exportPendingFreight(PagePendingFreightParam param) {
        param.setPage(1);
        param.setPageSize(1000000);
        IPage<PagePendingFreightVO> page = pagePendingFreight(param);

        List<PagePendingFreightVO> list = page.getRecords();

        // 组装表头
        List<ExcelField> fieldList = new ArrayList<>();

        fieldList.add(new ExcelField(0, "序号", "seq", 5000));
        fieldList.add(new ExcelField(1, "货源地", "sendAddressShorter", 5000));
        fieldList.add(new ExcelField(2, "煤源位置", "sendAddress", 5000));
        fieldList.add(new ExcelField(3, "物流经理", "seniorLogisticsManagerName", 5000));
        fieldList.add(new ExcelField(4, "货物名称", "goodsName", 5000));
        fieldList.add(new ExcelField(5, "热值", "goodsTypeName", 5000));
        fieldList.add(new ExcelField(6, "是否C类煤源", "sendOverStandardMsg", 5000));
        fieldList.add(new ExcelField(7, "任务吨数(吨)", "taskWeight", 5000));
        fieldList.add(new ExcelField(8, "接单率", "orderedRate", 5000));
        fieldList.add(new ExcelField(9, "装车率", "loadTruckRate", 5000));
        fieldList.add(new ExcelField(10, "重车运距(公里)", "orderDistance", 5000));
        fieldList.add(new ExcelField(11, "煤源地开支（元）", "sendAddressExpenses", 5000));
        fieldList.add(new ExcelField(12, "历史运费（元）", "historyFreight", 5000));
        fieldList.add(new ExcelField(13, "历史平均运费（元）", "historyAvgFreight", 5000));
        fieldList.add(new ExcelField(14, "测算运费（元）", "predictionFreightPrice", 5000));
        fieldList.add(new ExcelField(15, "物流运费（元）", "logisticsFreight", 5000));
        fieldList.add(new ExcelField(16, "挂单运费", "pendingFreight", 5000));

        // 组装数据
        List<List<ExcelData>> dataList = new ArrayList<>();
        for (int i=0; i<list.size(); i++){
            List<ExcelData> rowData = new ArrayList<>();
            PagePendingFreightVO vo = list.get(i);
            rowData.add(new ExcelData(i+1));
            rowData.add(new ExcelData(vo.getSendAddressShorter(),"-"));
            rowData.add(new ExcelData(vo.getSendAddress(),"-"));
            rowData.add(new ExcelData(vo.getSeniorLogisticsManagerName(),"-"));
            rowData.add(new ExcelData(vo.getGoodsName(), "-"));
            rowData.add(new ExcelData(vo.getGoodsTypeName(), "-"));
            rowData.add(new ExcelData(vo.getSendOverStandardMsg(), "-"));
            rowData.add(new ExcelData(vo.getTaskWeight(), "-"));
            rowData.add(new ExcelData(vo.getOrderedRate(), "-"));
            rowData.add(new ExcelData(vo.getLoadTruckRate(), "-"));
            rowData.add(new ExcelData(vo.getOrderDistance(), "-"));
            rowData.add(new ExcelData(vo.getSendAddressExpenses(), "-"));
            rowData.add(new ExcelData(vo.getHistoryFreight(), "-"));
            rowData.add(new ExcelData(vo.getHistoryAvgFreight(), "-"));
            rowData.add(new ExcelData(vo.getPredictionFreightPrice(), "-"));
            rowData.add(new ExcelData(vo.getLogisticsFreight(), "-"));
            rowData.add(new ExcelData(""));
            dataList.add(rowData);
        }
        ExcelSheet excelSheet = new ExcelSheet("挂单运费表", "挂单运费表", fieldList, dataList);

        //创建excel
        return ExcelUtil.create(excelSheet);
    }
}
