package com.ningdatech.carapi.car.datascreen.service.impl;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.carapi.car.datascreen.mapper.VehicleStatisticsMapper;
import com.ningdatech.carapi.car.datascreen.model.dto.DataScreenParam;
import com.ningdatech.carapi.car.datascreen.model.po.*;
import com.ningdatech.carapi.car.datascreen.model.vo.*;
import com.ningdatech.carapi.car.datascreen.service.VehicleStatisticsService;
import com.ningdatech.carapi.car.vehicle.mapper.VehicleBaseInfoJoinMapper;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfo;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfoJoin;
import com.ningdatech.carapi.car.vehicle.service.IVehicleBaseInfoService;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionEntity;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionWrapper;
import com.ningdatech.carapi.common.constant.VehicleTypeEnum;
import com.ningdatech.carapi.common.model.KeyValueDTO;
import com.ningdatech.carapi.common.model.po.MapDataPO;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.datascope.aop.LambdaDataScope;
import com.ningdatech.carapi.datascope.aop.XmlDataScope;
import com.ningdatech.carapi.safe.entity.NdInsureInfo;
import com.ningdatech.carapi.safe.entity.NdInsureOutDanger;
import com.ningdatech.carapi.safe.service.INdInsureInfoService;
import com.ningdatech.carapi.safe.service.INdInsureOutDangerService;
import com.ningdatech.carapi.sys.entity.dto.RegionDTO;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class VehicleStatisticsServiceImpl implements VehicleStatisticsService {

    private final VehicleStatisticsMapper vehicleStatisticsMapper;
    private final VehicleBaseInfoJoinMapper vehicleBaseInfoJoinMapper;
    private final RegionsCacheHelper regionsCacheHelper;
    private final INdInsureOutDangerService insureOutDangerService;
    private final IVehicleBaseInfoService vehicleBaseInfoService;
    private final INdInsureInfoService insureInfoService;

    private final String USEFUL_LIFE_ONE = "3年以下";
    private final String USEFUL_LIFE_TWO = "3-5年";
    private final String USEFUL_LIFE_THREE = "5-10年";
    private final String USEFUL_LIFE_FOUR = "10年以上";

    /**
     * 获取车辆类型分布
     *
     * @param param
     * @return
     */
    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<VehicleTypeStatisticsVO> getVehicleTypesStatistics(DataScreenParam param) {
        List<VehicleTypeStatisticsPO> typesStatistics = vehicleStatisticsMapper.getTypesStatistics(param);
        if (CollUtil.isEmpty(typesStatistics)) {
            return Collections.emptyList();
        }
        return convertVehicleTypes(typesStatistics);
    }

    /**
     * 获取区域车辆分布
     *
     * @param param
     * @return
     */
    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<RegionDistributionStatisticsVO> getRegionDistribution(DataScreenParam param) {
        //区域车辆 根据 所传的regionId来判断 查询甚么数据
        CodeUtil.buildRegionLevel(param);
        List<RegionDistributionStatisticsVO> list = statisticsRegionDistribution(vehicleStatisticsMapper.getZjRegionsStatistics(param));
        complementaryRegion(list, param.getRegionId());
        return list;
    }

    /**
     * 获取保险公司分布
     *
     * @param param
     * @return
     */
    @Override
    public List<InsureDistributionStatisticsVO> getInsureDistribution(DataScreenParam param) {
        LambdaQueryWrapper<NdInsureOutDanger> wrapper = Wrappers.lambdaQuery(NdInsureOutDanger.class);
        VehicleStatisticsServiceImpl context = (VehicleStatisticsServiceImpl) AopContext.currentProxy();
        context.buildWrapper(wrapper,param);
        // 获取系统车牌号
        List<String> carPlates = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                .select(NdVehicleBaseInfo::getCarPlate)).stream()
                .map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
        // 获取筛选时间的车辆出险数据,并过滤出在系统内的车辆
        List<NdInsureOutDanger> insureList = insureOutDangerService.list(wrapper)
                .stream().filter(c -> carPlates.contains(c.getCarPlate())).collect(Collectors.toList());
        // 获取车辆的保险信息
        List<NdInsureInfo> insureInfos = insureInfoService.list(Wrappers.lambdaQuery(NdInsureInfo.class)
                .in(NdInsureInfo::getCarPlate, carPlates));
        Map<String, List<NdInsureInfo>> insureInfoMap = insureInfos.stream()
                .collect(Collectors.groupingBy(NdInsureInfo::getInsureCompany));

        // 按照保险机构分组，再将车牌号去重，统计数量
        Map<String, List<NdInsureOutDanger>> insureMap = insureList.stream()
                .filter(c -> Objects.nonNull(c.getInsureCompany()))
                .collect(Collectors.groupingBy(NdInsureOutDanger::getInsureCompany));
        List<InsureDistributionStatisticsVO> resultList = Lists.newArrayList();
        for (String insureCompany : insureMap.keySet()) {
            InsureDistributionStatisticsVO vo = new InsureDistributionStatisticsVO();
            vo.setInsureCompany(insureCompany);
            List<NdInsureOutDanger> outDangers = insureMap.get(insureCompany);
            long count = outDangers.stream().map(NdInsureOutDanger::getCarPlate).distinct().count();
            vo.setVehicleNum(count);
            // 获取该保险公司下保险的车辆总数
            List<NdInsureInfo> infoList = insureInfoMap.get(insureCompany);
            if (CollUtil.isEmpty(infoList)){
                // 该保险公司不在车辆承保信息中，跳过该保险机构
                continue;
            }
            long total = infoList.stream().map(NdInsureInfo::getCarPlate).distinct().count();
            vo.setRatio(BigDecimal.valueOf(count).multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(total), 2, BigDecimal.ROUND_HALF_UP));
            resultList.add(vo);
        }
        return resultList;
        //List<InsureDistributionStatisticsPO> insuresStatistics = vehicleStatisticsMapper.getInsureDistribution(param);
        //if (CollUtil.isEmpty(insuresStatistics)) {
        //    return Collections.emptyList();
        //}
        //return convertInsures(insuresStatistics);
    }

    @LambdaDataScope
    @BuildChildrenRegionWrapper
    private void buildWrapper(LambdaQueryWrapper<NdInsureOutDanger> wrapper,DataScreenParam param) {
        wrapper.between(NdInsureOutDanger::getAccidentTime, param.getStartTime(), param.getEndTime());
    }

    /**
     * 运营商分布
     *
     * @param param
     * @return
     */
    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<OperatorDistributionStatisticsVO> getOperatorDistribution(DataScreenParam param) {
        List<OperatorDistributionStatisticsPO> operatorStatistics = vehicleStatisticsMapper.getOperatorDistribution(param);
        if (CollUtil.isEmpty(operatorStatistics)) {
            return Collections.emptyList();
        }
        return convertOperators(operatorStatistics);
    }

    /**
     * 新增车辆分布图
     *
     * @param param
     * @return
     */
    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<YearVehicleStatisticsVO> getNewVehicleNum(DataScreenParam param) {
        List<YearVehicleStatisticsPO> yearVehicleStatistics = vehicleStatisticsMapper.getNewVehicleDistribution(param);
        if (CollUtil.isEmpty(yearVehicleStatistics)) {
            return Collections.emptyList();
        }
        return convertYears(yearVehicleStatistics);
    }

    /**
     * 车辆年限分布
     *
     * @param param
     * @return
     */
    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<VehicleUsefulLifeStatisticsVO> getUsefulLiftDistribution(DataScreenParam param) {
        List<VehicleUsefulLifeStatisticsPO> vehicleUsefulLife = vehicleStatisticsMapper.getVehicleUsefulLife(param);
        if (CollUtil.isEmpty(vehicleUsefulLife)) {
            return Collections.emptyList();
        }
        return vehicleUsefulLife.stream().map(v -> {
            VehicleUsefulLifeStatisticsVO vo = new VehicleUsefulLifeStatisticsVO();
            BeanUtils.copyProperties(v, vo);
            return vo;
        }).collect(Collectors.toList());
    }

    @LambdaDataScope
    @BuildChildrenRegionWrapper
    public List<NdVehicleBaseInfoJoin> searchVehicleList(LambdaQueryWrapper<NdVehicleBaseInfoJoin> wrapper,
                                                         DataScreenParam param) {
        log.info(param.toString());
        return vehicleBaseInfoJoinMapper.listJoin(wrapper);
    }

    @Override
    @XmlDataScope
    public Long getVehicleNums(DataScreenParam param) {
        return vehicleStatisticsMapper.getVehicleNums(param);
    }

    @Override
    @XmlDataScope
    public List<KeyValueDTO<Long, Long>> getVehicleNumsGroupByRegion(DataScreenParam param) {
        return vehicleStatisticsMapper.getVehicleNumsGroupByRegion(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<MapDataPO> getOverspeedRegions(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getOverspeedRegions(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<MapDataPO> getViolationRegions(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getViolationRegions(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<MapDataPO> getOutDangerRegions(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getOutDangerRegions(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<MapDataPO> getInjuriesRegion(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getInjuriesRegion(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<MapDataPO> getDeathRegion(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getDeathRegion(param);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<OnlineVehicleTimesPO> getOnlineRateToday(DataScreenParam param) {
        String tableName = "nd_vehicle_online_status_hour_" + NdDateUtils.format(new Date(), "yyyy_MM");
        return vehicleStatisticsMapper.getOnlineRateToday(param, tableName);
    }

    @Override
    @XmlDataScope
    @BuildChildrenRegionEntity
    public List<OnlineVehicleRegionsPO> getVehicleOnlineRegions(DataScreenParam param) {
        CodeUtil.buildRegionLevel(param);
        return vehicleStatisticsMapper.getVehicleOnlineRegions(param);
    }

    private List<VehicleUsefulLifeStatisticsVO> convertUsefulLife(List<NdVehicleBaseInfoJoin> vehicles) {
        List<VehicleUsefulLifeStatisticsVO> res = Lists.newArrayList();
        AtomicLong usefulLifeOne = new AtomicLong(0L);
        AtomicLong usefulLifeTwo = new AtomicLong(0L);
        AtomicLong usefulLifeThree = new AtomicLong(0L);
        AtomicLong usefulLifeFour = new AtomicLong(0L);
        vehicles.forEach(v -> {
            if (Objects.isNull(v.getUsefulLife())) {
                return;
            }
            if (0 < v.getUsefulLife() && 3 > v.getUsefulLife()) {
                usefulLifeOne.addAndGet(1L);
            } else if (3 <= v.getUsefulLife() && 5 > v.getUsefulLife()) {
                usefulLifeTwo.addAndGet(1L);
            } else if (5 <= v.getUsefulLife() && 10 > v.getUsefulLife()) {
                usefulLifeThree.addAndGet(1L);
            } else if (10 <= v.getUsefulLife()) {
                usefulLifeFour.addAndGet(1L);
            }
        });
        //1.3年以下
        res.add(new VehicleUsefulLifeStatisticsVO(usefulLifeOne.get(), USEFUL_LIFE_ONE));
        //2.3-5年
        res.add(new VehicleUsefulLifeStatisticsVO(usefulLifeTwo.get(), USEFUL_LIFE_TWO));
        //3.5-10年
        res.add(new VehicleUsefulLifeStatisticsVO(usefulLifeThree.get(), USEFUL_LIFE_THREE));
        //4.10年以上
        res.add(new VehicleUsefulLifeStatisticsVO(usefulLifeFour.get(), USEFUL_LIFE_FOUR));
        return res;
    }

    private List<YearVehicleStatisticsVO> convertYears(List<YearVehicleStatisticsPO> yearVehicleStatistics) {
        if (CollUtil.isEmpty(yearVehicleStatistics)) {
            return Collections.emptyList();
        }
        return yearVehicleStatistics.stream().map(t -> BeanUtil.copyProperties(t, YearVehicleStatisticsVO.class)).collect(Collectors.toList());
    }


    private List<VehicleTypeStatisticsVO> convertVehicleTypes(List<VehicleTypeStatisticsPO> typesStatistics) {
        long sum = typesStatistics.stream().mapToLong(VehicleTypeStatisticsPO::getVehicleNum).sum();
        return typesStatistics.stream().map(t -> {
            VehicleTypeStatisticsVO vo = BeanUtil.copyProperties(t, VehicleTypeStatisticsVO.class);
            vo.setVehicleTypesName(VehicleTypeEnum.getDescByCode(vo.getVehicleTypesId()));
            vo.setRatio(BigDecimal.valueOf(vo.getVehicleNum()).multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(sum), 2, BigDecimal.ROUND_HALF_UP));
            return vo;
        }).collect(Collectors.toList());
    }

    private List<RegionDistributionStatisticsVO> statisticsRegionDistribution(List<RegionDistributionStatisticsPO> regionsStatistics) {
        if (CollUtil.isEmpty(regionsStatistics)) {
            return Collections.emptyList();
        }
        return regionsStatistics.stream().map(t -> BeanUtil.copyProperties(t, RegionDistributionStatisticsVO.class)).collect(Collectors.toList());
    }

    private List<InsureDistributionStatisticsVO> convertInsures(List<InsureDistributionStatisticsPO> insuresStatistics) {
        long sum = insuresStatistics.stream().mapToLong(InsureDistributionStatisticsPO::getVehicleNum).sum();
        return insuresStatistics.stream().map(insure -> {
            InsureDistributionStatisticsVO vo = BeanUtil.copyProperties(insure, InsureDistributionStatisticsVO.class);
            vo.setRatio(BigDecimal.valueOf(vo.getVehicleNum()).multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(sum), 2, BigDecimal.ROUND_HALF_UP));
            return vo;
        }).collect(Collectors.toList());
    }

    private List<OperatorDistributionStatisticsVO> convertOperators(List<OperatorDistributionStatisticsPO> operatorStatistics) {
        long sum = operatorStatistics.stream().mapToLong(OperatorDistributionStatisticsPO::getVehicleNum).sum();
        return operatorStatistics.stream().map(operator -> {
            OperatorDistributionStatisticsVO vo = BeanUtil.copyProperties(operator, OperatorDistributionStatisticsVO.class);
            vo.setRatio(BigDecimal.valueOf(vo.getVehicleNum()).multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(sum), 2, BigDecimal.ROUND_HALF_UP));
            return vo;
        }).collect(Collectors.toList());
    }

    public void complementaryRegion(List<RegionDistributionStatisticsVO> list, Long regionId) {
        Map<String, RegionDistributionStatisticsVO> dataMap = CollUtils.listToMap(list, m -> m.getRegionName());
        List<RegionDTO> regions = regionsCacheHelper.listChildOnly(regionId);
        for (RegionDTO region : regions) {
            if (!dataMap.containsKey(region.getRegionName())) {
                RegionDistributionStatisticsVO data = new RegionDistributionStatisticsVO();
                data.setRegionId(region.getId());
                data.setRegionName(region.getRegionName());
                data.setVehicleNum(0L);
                list.add(data);
            }
        }
    }
}
