package com.sckw.transport.service;


import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.sckw.core.model.base.BaseModel;
import com.sckw.core.model.enums.CarWaybillEnum;
import com.sckw.core.model.enums.CarWaybillV1Enum;
import com.sckw.core.utils.DateUtils;
import com.sckw.core.web.context.LoginEntHolder;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.fleet.api.RemoteFleetService;
import com.sckw.fleet.api.model.vo.RFleetDriverVo;
import com.sckw.fleet.api.model.vo.RFleetVo;
import com.sckw.transport.common.config.AmapProperties;
import com.sckw.transport.dao.KwtWaybillOrderMapper;
import com.sckw.transport.dao.KwtWaybillOrderSubtaskMapper;
import com.sckw.transport.model.*;
import com.sckw.transport.model.param.FleetRankingReq;
import com.sckw.transport.model.param.LogisticsOrderStatisticsReq;
import com.sckw.transport.model.param.TaskStatisticsReq;
import com.sckw.transport.model.vo.FleetRankingVo;
import com.sckw.transport.model.vo.LogOrderStatisticsVo;
import com.sckw.transport.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author PC
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class KwfTaskService {
    private final KwtWaybillOrderRepository kwtWaybillOrderRepository;
    private final AmapProperties amapProperties;
    private final KwtWaybillOrderSubtaskRepository kwtWaybillOrderSubtaskRepository;
    private final KwtVehicleExceptionRepository kwtVehicleExceptionRepository;
    private final KwtLogisticsOrderUnitRepository kwtLogisticsOrderUnitRepository;
    private final KwtWaybillOrderSubtaskMapper waybillOrderSubtaskDao;
    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
    RemoteFleetService fleetService;
    public TaskStatisticsVo getTaskStatistics(TaskStatisticsReq req) {
        log.info("开始获取任务统计信息请求参数:{}",JSON.toJSONString(req));
        Long entId = Objects.nonNull(req.getEntId()) ? req.getEntId() : LoginUserHolder.getEntId();
        //根据企业查询物流订单
        List<KwtLogisticsOrderUnit> kwtLogisticsOrderUnits = kwtLogisticsOrderUnitRepository.queryByEntId(entId);
        if (CollUtil.isEmpty(kwtLogisticsOrderUnits)) {
            log.info("该企业未查询到物流订单数据，企业id:{}", entId);
            return new TaskStatisticsVo();
        }
        //获取物流订单号
        Set<Long> logOrderIds = kwtLogisticsOrderUnits.stream()
                .map(KwtLogisticsOrderUnit::getLOrderId)
                .collect(Collectors.toSet());
        if (CollUtil.isEmpty(logOrderIds)) {
            log.info("该企业下物流订单数据，企业id:{}", entId);
            return new TaskStatisticsVo();
        }
        Date date = new Date();
        // 获取昨日开始时间
        Date startDate = DateUtils.getStartOfDay(date);
        // 获取昨日结束时间
        Date endDate = DateUtils.getEndOfDay(date);

        //查询子运单
        List<KwtWaybillOrderSubtask> kwtWaybillOrderSubtasks = waybillOrderSubtaskDao.selectList(Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
                .in(KwtWaybillOrderSubtask::getLOrderId, logOrderIds)
                .eq(BaseModel::getDelFlag, 0));
        if(org.apache.commons.collections4.CollectionUtils.isEmpty(kwtWaybillOrderSubtasks)){
            log.info("该企业下子运单的订单数据不存在，企业id:{}", entId);
            return new TaskStatisticsVo();
        }
        Set<Long> wayOrderIds = kwtWaybillOrderSubtasks.stream()
                .map(KwtWaybillOrderSubtask::getWOrderId)
                .collect(Collectors.toSet());

        List<KwtWaybillOrder> waybillOrder= kwtWaybillOrderRepository.queryByBillOrderIdsAndTime(wayOrderIds,startDate, endDate);
        //任务总数
        int totalCount =0;
        List<KwtWaybillOrder> kwtWaybillOrderStream = waybillOrder.stream()
                .filter(w -> !Objects.equals(w.getStatus(), CarWaybillV1Enum.CANCELLED.getCode()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(kwtWaybillOrderStream)) {
            TaskStatisticsVo taskStatisticsVo = new TaskStatisticsVo();
            taskStatisticsVo.setTaskTotal(String.valueOf(totalCount));
            return taskStatisticsVo;
        }else {
            totalCount = kwtWaybillOrderStream.size();
        }
        List<Integer> tasking = Arrays.asList(CarWaybillV1Enum.PENDING_VEHICLE.getCode(),
                CarWaybillV1Enum.REFUSE_TRAFFIC.getCode(),
                CarWaybillV1Enum.EXIT_COMPLETED.getCode(),
                CarWaybillV1Enum.EMPTY_WAIT_LEAVE.getCode(),
                CarWaybillV1Enum.WAIT_LEAVE.getCode(),
                CarWaybillV1Enum.UNLOADING.getCode(),
                CarWaybillV1Enum.WAIT_RELEASE.getCode(),
                CarWaybillV1Enum.REPLENISHING.getCode(),
                CarWaybillV1Enum.REPLENISH_FINISH.getCode(),
                CarWaybillV1Enum.RELEASED_NOT_EXITED.getCode(),
                CarWaybillV1Enum.WAIT_LOADING.getCode());
        //处理中的任务数
        long taskingCount = waybillOrder.stream()
                .filter(w -> tasking.contains(w.getStatus()) && StringUtils.isNotBlank(w.getTruckNo()))
                .count();
        //已经完成的任务数
        long completedCount = waybillOrder.stream()
                .filter(w -> Objects.equals(w.getStatus(),CarWaybillV1Enum.COMPLETED.getCode()))
                .count();
        //昨日累计任务数
        long yesterdayTaskCount = getYesterdayTaskCount(waybillOrder);
        // 前一天累计任务数
        long taskTotalBeforeYesterday = getTaskTotalBeforeYesterday(waybillOrder);

        //计算上月卸货吨数
        double unloadTon = getUnloadTon(waybillOrder);

        //获取本月初到当前时间的数据(卸货吨数)
        double unloadTonThisMonth = getUnloadTonThisMonth(waybillOrder);
        //获取上个月初到某个时间点的数据(卸货吨数)
        double unloadTonBeforeMonth = getUnloadTonBeforeMonth(waybillOrder);
        //较上个月同比增长或者下降
        double unloadGrowthRate = 0.0;
        if (unloadTonBeforeMonth != 0.0){
            unloadGrowthRate= (unloadTonBeforeMonth - unloadTonThisMonth) / unloadTonBeforeMonth * 100;
        }

        // 计算增长率
        double growthRate = 0.0;
        if (yesterdayTaskCount != 0.0){
            growthRate= (double) (yesterdayTaskCount - taskTotalBeforeYesterday) / taskTotalBeforeYesterday * 100;
        }
        //查询车辆异常车辆数
        List<KwtVehicleException> vehicleExceptions = kwtVehicleExceptionRepository.queryByEnt(wayOrderIds,startDate,endDate);
        Map<Long, List<KwtVehicleException>> vehicleExceptionMap = Optional.ofNullable(vehicleExceptions)
                .orElse(List.of())
                .stream()
                .collect(Collectors.groupingBy(KwtVehicleException::getTruckId));
        return getTaskStatisticsVo(totalCount, taskingCount, completedCount, yesterdayTaskCount,
                taskTotalBeforeYesterday, growthRate, unloadTon, unloadTonThisMonth, unloadTonBeforeMonth,unloadGrowthRate,vehicleExceptionMap,vehicleExceptions);
    }


    private TaskStatisticsVo getTaskStatisticsVo(int totalCount, long taskingCount, long completedCount
            , long yesterdayTaskCount, long taskTotalBeforeYesterday, double growthRate, double unloadTon
            , double unloadTonThisMonth, double unloadTonBeforeMonth,double unloadGrowthRate,
                                                 Map<Long, List<KwtVehicleException>> vehicleExceptionMap,
                                                 List<KwtVehicleException> vehicleExceptions) {
        TaskStatisticsVo taskStatisticsVo = new TaskStatisticsVo();
        taskStatisticsVo.setTaskTotal(String.valueOf(totalCount));
        taskStatisticsVo.setTaskRunning(String.valueOf(taskingCount));
        taskStatisticsVo.setTaskCompleted(String.valueOf(completedCount));
        taskStatisticsVo.setTaskPercent(taskingCount == 0 ? "0" : String.format("%.2f", (double) taskingCount / totalCount * 100));
        int size = vehicleExceptionMap.size();
        taskStatisticsVo.setTruckAbnormal(String.valueOf(size));
        int totalSize = Optional.ofNullable(vehicleExceptions).orElse(List.of()).size();
        taskStatisticsVo.setWarningTotal(String.valueOf(totalSize));
        taskStatisticsVo.setTaskTotalYesterday(String.valueOf(yesterdayTaskCount));
        taskStatisticsVo.setTaskTotalBeforeYesterday(String.valueOf(taskTotalBeforeYesterday));
        taskStatisticsVo.setTaskPercentChange(String.format("%.2f", growthRate));
        taskStatisticsVo.setUnloadTon(String.valueOf(unloadTon));
        taskStatisticsVo.setUnloadTonThisMonth(String.valueOf(unloadTonThisMonth));
        taskStatisticsVo.setUnloadTonBeforeMonth(String.valueOf(unloadTonBeforeMonth));
        taskStatisticsVo.setWaybillTotalYesterday(String.valueOf(yesterdayTaskCount));
        taskStatisticsVo.setWaybillTotalBeforeYesterday(String.valueOf(taskTotalBeforeYesterday));
        taskStatisticsVo.setWaybillPercentChange(String.format("%.2f", growthRate));
        taskStatisticsVo.setUnloadTonPercentChange(String.format("%.2f", unloadGrowthRate));
        return taskStatisticsVo;
    }

    private double getUnloadTonBeforeMonth(List<KwtWaybillOrder> waybillOrder) {
        Date startBeforeMonth = Date.from(DateUtils.getStartMonth(1).atZone(ZoneId.systemDefault()).toInstant());
        Date endBeforeMonth = Date.from(LocalDateTime.now().minusMonths(1).atZone(ZoneId.systemDefault()).toInstant());
        return geUnloadAmount(waybillOrder, startBeforeMonth, endBeforeMonth);
    }

    private double geUnloadAmount(List<KwtWaybillOrder> waybillOrder, Date startBeforeMonth, Date endBeforeMonth) {
        Set<Long> wOrderIds = waybillOrder.stream()
                .filter(w -> w.getCreateTime() != null)
                .filter(w -> !w.getCreateTime().before(startBeforeMonth) && w.getCreateTime().before(endBeforeMonth))
                .map(KwtWaybillOrder::getId).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(wOrderIds)){
            return 0;
        }
        List<KwtWaybillOrderSubtask> beforeMonthWaybillOrderSubtasks = kwtWaybillOrderSubtaskRepository.findOneByWOrderIds(wOrderIds);
        if (CollectionUtils.isEmpty(beforeMonthWaybillOrderSubtasks)){
            return 0;
        }
        return beforeMonthWaybillOrderSubtasks.stream()
                .filter(w -> w.getUnloadAmount() != null)
                .mapToDouble(x -> x.getUnloadAmount().doubleValue())
                .sum();
    }

    private double getUnloadTonThisMonth(List<KwtWaybillOrder> waybillOrder) {
        Date startThisMonth = Date.from(DateUtils.getStartMonth(0).atZone(ZoneId.systemDefault()).toInstant());
        Date endThisMonth = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
        return geUnloadAmount(waybillOrder, startThisMonth, endThisMonth);

    }

    private double getUnloadTon(List<KwtWaybillOrder> waybillOrder) {
        Date startMonth = Date.from(DateUtils.getStartMonth(1).atZone(ZoneId.systemDefault()).toInstant());
        Date endMonth = Date.from(DateUtils.getStartMonth(0).atZone(ZoneId.systemDefault()).toInstant());
        //获取运单id列表
        return geUnloadAmount(waybillOrder, startMonth, endMonth);
    }

    private static long getTaskTotalBeforeYesterday(List<KwtWaybillOrder> waybillOrder) {
        Date startOfDay = Date.from(DateUtils.getStartDay(2).atZone(ZoneId.systemDefault()).toInstant());
        Date endOfDay = Date.from(DateUtils.getStartDay(1).atZone(ZoneId.systemDefault()).toInstant());
        return waybillOrder.stream()
                .filter(w -> w.getCreateTime() != null)
                .filter(w -> !w.getCreateTime().before(startOfDay) && w.getCreateTime().before(endOfDay))
                .count();
    }

    private static long getYesterdayTaskCount(List<KwtWaybillOrder> waybillOrder) {
        Date startOfYesterday = Date.from(DateUtils.getStartDay(1).atZone(ZoneId.systemDefault()).toInstant());
        Date startOfToday = Date.from(DateUtils.getStartDay(0).atZone(ZoneId.systemDefault()).toInstant());

        // 昨日累计任务数
        return waybillOrder.stream()
                .filter(w -> w.getCreateTime() != null)
                .filter(w -> !w.getCreateTime().before(startOfYesterday) && w.getCreateTime().before(startOfToday))
                .count();
    }

    /**
     * 获取高德地图apiKey
     * @return apiKey
     */
    public String getMapApikey() {
        return amapProperties.getKey();
    }

    public LogOrderStatisticsVo getWaybillOrderStatistics(LogisticsOrderStatisticsReq req) {
        log.info("查询物流单统计参数：{}", JSON.toJSONString(req));
        //安装月份统计
        LocalDateTime startMonth = DateUtils.getStartMonth(Integer.parseInt(req.getValue()));
        //获取当前的前几个月份
        List<YearMonth> previousMonthsInclusive = DateUtils.getPreviousMonthsInclusive(Integer.parseInt(req.getValue()));
        //查询运单数据
        List<KwtWaybillOrder> waybillOrders = kwtWaybillOrderRepository.queryByCreateTime(startMonth,req.getEntId());

        //安装年月进行分组
        Map<String, List<KwtWaybillOrder>> yearMonthAndWayOrderListMap = waybillOrders.stream()
                .filter(x ->Objects.nonNull(x.getCreateTime()))
                .peek(x ->x.setYearAndMonth(DateUtils.format(x.getCreateTime(), DateUtils.FORMAT10)) )
                .collect(Collectors.groupingBy(KwtWaybillOrder::getYearAndMonth));
        //获取已接单的状态
        List<Integer> accepted = CarWaybillEnum.accepted();
        List<LogOrderStatisticsVo.LogOrderStatistics> logOrderStatisticsList = previousMonthsInclusive.stream()
                .map(x -> getLogOrderStatistics(x, yearMonthAndWayOrderListMap, accepted))
                .collect(Collectors.toList());
        LogOrderStatisticsVo logOrderStatisticsVo = new LogOrderStatisticsVo();
        logOrderStatisticsVo.setLogOrderStatistics(logOrderStatisticsList);
        return logOrderStatisticsVo;
    }

    private LogOrderStatisticsVo.LogOrderStatistics getLogOrderStatistics(YearMonth x, Map<String, List<KwtWaybillOrder>> yearMonthAndWayOrderListMap, List<Integer> accepted) {
        LogOrderStatisticsVo.LogOrderStatistics logOrderStatistics = new LogOrderStatisticsVo.LogOrderStatistics();
        logOrderStatistics.setKey(x.toString());
        List<KwtWaybillOrder> orderList = yearMonthAndWayOrderListMap.getOrDefault(x.toString(), Collections.emptyList());
        if (CollectionUtils.isEmpty(orderList)) {
            logOrderStatistics.setDispatchCount("0");
            logOrderStatistics.setCompleteCount("0");
        } else {
            long dispatchCount = orderList.stream()
                    .filter(s -> Objects.equals(s.getStatus(), CarWaybillEnum.APPROVAL_PASS.getCode())).count();
            logOrderStatistics.setDispatchCount(String.valueOf(dispatchCount));
            long completeCount = orderList.stream()
                    .filter(s -> accepted.contains(s.getStatus())).count();
            logOrderStatistics.setCompleteCount(String.valueOf(completeCount));
        }
        return logOrderStatistics;
    }

    public FleetRankingVo getFleetRanking(FleetRankingReq req) {
       log.info("查询车队排行榜");
        Long entId = req.getEntId();
        //查询订单
        List<KwtWaybillOrder> writeOrders = kwtWaybillOrderRepository.findByEntId(entId);
        if (CollectionUtils.isEmpty(writeOrders)) {
            return new FleetRankingVo();
        }

        //查询车队和司机关系
        List<RFleetDriverVo> feltDriverVos = fleetService.findFleetDriveByEntId(entId);
        if (CollectionUtils.isEmpty(feltDriverVos)) {
            return new FleetRankingVo();
        }

        List<RFleetVo> fleets = fleetService.findFleetByEntId(entId);

        Map<Long, RFleetVo> fleetIdAndFleetMap = fleets.stream()
                .collect(Collectors.toMap(RFleetVo::getId, Function.identity(), (k1, k2) -> k1));
        //车队id进行分组
        Map<Long, List<RFleetDriverVo>> fleetIdAndFeltDriverVosMap = feltDriverVos
                .stream()
                .collect(Collectors.groupingBy(RFleetDriverVo::getFleetId));

        //查询运单子运单数据
        List<KwtWaybillOrderSubtask> waybillOrderSubtasks = kwtWaybillOrderSubtaskRepository.findByEntId(entId);
        List<FleetRankingVo.FleetRanking> fleetRankings = Lists.newArrayList();
        fleetIdAndFeltDriverVosMap.forEach((k, rFleetDriverVos) -> {
            FleetRankingVo.FleetRanking fleetRanking = new FleetRankingVo.FleetRanking();
            RFleetVo fleetVo = fleetIdAndFleetMap.getOrDefault(k, new RFleetVo());
            fleetRanking.setFleetName(fleetVo.getName());
            fleetRanking.setFleetId(String.valueOf(k));
            fleetRanking.setLoadCount(CollectionUtils.isEmpty(rFleetDriverVos) ? "0" :String.valueOf(rFleetDriverVos.size()));
            double sum = 0.0;
            if (CollectionUtils.isEmpty(rFleetDriverVos)){
                fleetRanking.setLoadWeight(String.valueOf( sum));
                fleetRankings.add(fleetRanking);
                return;
            }
            Set<Long> driveIds = rFleetDriverVos.stream()
                    .map(RFleetDriverVo::getDriverId)
                    .collect(Collectors.toSet());
            List<Long> wOrderIds = writeOrders.stream()
                    .filter(x -> driveIds.contains(x.getDriverId()))
                    .map(KwtWaybillOrder::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(wOrderIds)){
                fleetRanking.setLoadWeight(String.valueOf( sum));
                fleetRankings.add(fleetRanking);
                return;
            }
            sum = waybillOrderSubtasks.stream()
                    .filter(x -> wOrderIds.contains(x.getWOrderId()))
                    .filter(x -> Objects.nonNull(x.getUnloadAmount()))
                    .mapToDouble(x -> x.getUnloadAmount().doubleValue())
                    .sum();
            fleetRanking.setLoadWeight(String.valueOf( sum));
            fleetRankings.add(fleetRanking);
        });
        FleetRankingVo fleetRankingVo = new FleetRankingVo();
        List<FleetRankingVo.FleetRanking> fleetRankingList = fleetRankings.stream()
                .sorted(Comparator.comparing(FleetRankingVo.FleetRanking::getLoadCount).reversed()
                        .thenComparing(FleetRankingVo.FleetRanking::getLoadWeight)).collect(Collectors.toList());
        fleetRankingVo.setFleetRankingList(fleetRankingList);
        return fleetRankingVo;
    }
}
