package com.ningdatech.carapi.safe.manage;

import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfo;
import com.ningdatech.carapi.car.vehicle.service.IVehicleBaseInfoService;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionWrapper;
import com.ningdatech.carapi.common.constant.UserRoleTypeEnum;
import com.ningdatech.carapi.common.constant.VehicleTypeEnum;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.common.util.ExcelDownUtil;
import com.ningdatech.carapi.datascope.aop.LambdaDataScope;
import com.ningdatech.carapi.report.constants.ReportConstant;
import com.ningdatech.carapi.safe.constant.AccidentTypeEnum;
import com.ningdatech.carapi.safe.constant.VehicleAccidentReportEnum;
import com.ningdatech.carapi.safe.constant.VehicleAccidentReportStatusEnum;
import com.ningdatech.carapi.safe.entity.NdVehicleAccident;
import com.ningdatech.carapi.safe.entity.NdVehicleAccidentAnalysis;
import com.ningdatech.carapi.safe.entity.NdVehicleAccidentDead;
import com.ningdatech.carapi.safe.entity.VehicleAccidentReport;
import com.ningdatech.carapi.safe.model.dto.AccidentAuditDTO;
import com.ningdatech.carapi.safe.model.dto.AccidentPageQuery;
import com.ningdatech.carapi.safe.model.dto.NdVehicleAccidentDTO;
import com.ningdatech.carapi.safe.model.dto.VehicleAccidentReportDTO;
import com.ningdatech.carapi.safe.model.vo.NdVehicleAccidentAnalysisVO;
import com.ningdatech.carapi.safe.model.vo.NdVehicleAccidentDeadVO;
import com.ningdatech.carapi.safe.model.vo.NdVehicleAccidentReportVO;
import com.ningdatech.carapi.safe.model.vo.NdVehicleAccidentVO;
import com.ningdatech.carapi.safe.service.INdVehicleAccidentAnalysisService;
import com.ningdatech.carapi.safe.service.INdVehicleAccidentDeadService;
import com.ningdatech.carapi.safe.service.INdVehicleAccidentService;
import com.ningdatech.carapi.safe.service.IVehicleAccidentReportService;
import com.ningdatech.carapi.scheduler.enums.DataTypeEnum;
import com.ningdatech.carapi.sys.entity.dto.CompanyDTO;
import com.ningdatech.carapi.sys.entity.dto.OperatorQueryDTO;
import com.ningdatech.carapi.sys.helper.CompaniesCacheHelper;
import com.ningdatech.carapi.sys.helper.OperatorsCacheHelper;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;
import com.ningdatech.carapi.user.security.auth.model.UserInfoDetails;
import com.ningdatech.carapi.user.util.LoginUserUtil;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.poi.excel.ExcelUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author PoffyZhang
 * @date 2022/10/19 上午11:18
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class VehicleAccidentManage {

    private final INdVehicleAccidentService vehicleAccidentService;
    private final INdVehicleAccidentDeadService vehicleAccidentDeadService;
    private final INdVehicleAccidentAnalysisService vehicleAccidentAnalysisService;
    private final IVehicleAccidentReportService vehicleAccidentReportService;
    private final ApplicationContext applicationContext;
    private final OperatorsCacheHelper operatorsCacheHelper;
    private final IVehicleBaseInfoService vehicleBaseInfoService;
    private final CompaniesCacheHelper companiesCacheHelper;
    private final RegionsCacheHelper regionsCacheHelper;

    public PageVo<NdVehicleAccidentVO> queryVehicleAccident(AccidentPageQuery accidentPageQuery) {
        ((VehicleAccidentManage) AopContext.currentProxy()).queryAccident();
        // 以车辆事故维度
        Page<NdVehicleAccident> page = accidentPageQuery.page();

        // 按照登录用户，如果是运营商账号登录，删选出该运营商关联的车辆信息
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long roleId = userDetail.getRoleId();
        if (UserRoleTypeEnum.OPERATOR_ROLE_ID.getId().equals(roleId)) {
            String companyName = userDetail.getCompanyName();
            // 暂时控制运营商名称和运营商所在企业名称相同
            OperatorQueryDTO operatorQueryDTO = operatorsCacheHelper.getByName(companyName);
            // 获取运营商ID
            Long operatorId = operatorQueryDTO.getId();
            // 查询出该运营商下所有车辆
            List<NdVehicleBaseInfo> baseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .eq(NdVehicleBaseInfo::getOperatorId, operatorId));
            List<String> carPlateList = baseInfoList.stream().map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
            accidentPageQuery.setCarPlateList(carPlateList);
        }

        vehicleAccident(page, accidentPageQuery);
        List<NdVehicleAccidentVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentVO.class);

            String desc = VehicleTypeEnum.getDescByCode(vo.getVehicleType());
            Integer injuries = e.getInjuries();
            Integer deathToll = e.getDeathToll();
            vo.setInjuries(injuries);
            vo.setDeathToll(deathToll);
            vo.setAccidentType(e.getAccidentType());
            vo.setAccidentTypeName(AccidentTypeEnum.getDescByCode(e.getAccidentType()));
            vo.setBriefCase(e.getBriefCase());
            vo.setFirmContent(e.getFirmContent());
            //if (injuries == 0 && deathToll == 0){
            //    vo.setAccidentType(AccidentTypeEnum.OTHER.getCode());
            //    vo.setAccidentTypeName(AccidentTypeEnum.OTHER.getDesc());
            //}else if (injuries != 0 && deathToll == 0){
            //    vo.setAccidentType(AccidentTypeEnum.INJURY.getCode());
            //    vo.setAccidentTypeName(AccidentTypeEnum.INJURY.getDesc());
            //}else {
            //    vo.setAccidentType(AccidentTypeEnum.DEAD.getCode());
            //    vo.setAccidentTypeName(AccidentTypeEnum.DEAD.getDesc());
            //}
            vo.setVehicleTypeName(desc);
            vo.setReportStatusName(VehicleAccidentReportStatusEnum.getDescByCode(e.getReportStatus()));
            CodeUtil.searchCompanyNameAndRegionName(vo);
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }
    public PageVo<NdVehicleAccidentVO> queryVehicleAccidentOpen(AccidentPageQuery accidentPageQuery) {
        ((VehicleAccidentManage) AopContext.currentProxy()).queryAccident();
        // 以车辆事故维度
        Page<NdVehicleAccident> page = accidentPageQuery.page();
        LambdaQueryWrapper<NdVehicleAccident> wrapper = Wrappers.lambdaQuery(NdVehicleAccident.class);
        searchVehicleAccidentWrapper(wrapper, accidentPageQuery);
        vehicleAccidentService.page(page, wrapper);
        List<NdVehicleAccidentVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentVO.class);
            String desc = VehicleTypeEnum.getDescByCode(vo.getVehicleType());
            vo.setVehicleTypeName(desc);
            vo.setReportStatusName(VehicleAccidentReportStatusEnum.getDescByCode(e.getReportStatus()));
            CodeUtil.searchCompanyNameAndRegionName(vo);
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }

    @Async
    protected void queryAccident() {
        log.info("查询时车辆出险上报校对");
        //获取事故上报审核列表中通过审核的上报信息
        List<VehicleAccidentReport> reportList = vehicleAccidentReportService.list(Wrappers.lambdaQuery(VehicleAccidentReport.class)
                .select(VehicleAccidentReport::getCarPlate, VehicleAccidentReport::getAccidentTime)
                .eq(VehicleAccidentReport::getAuditStatus, VehicleAccidentReportEnum.AUDIT_APPROVED.getCode()));
        Map<String, Set<LocalDate>> carPlateMapDate = reportList.stream()
                .collect(Collectors.groupingBy(VehicleAccidentReport::getCarPlate,
                Collectors.mapping(w -> w.getAccidentTime().toLocalDate(), Collectors.toSet())));
        //获取车辆事故信息表中的所有数据
        List<NdVehicleAccident> records = vehicleAccidentService.list(Wrappers.lambdaQuery(NdVehicleAccident.class)
                .select(NdVehicleAccident::getCarPlate, NdVehicleAccident::getAccidentTime));
        //对比两个列表中的数据，更新车辆事故信息表中的上报状态
        List<Long> reported = records.stream().filter(w -> {
            Set<LocalDate> dates = carPlateMapDate.get(w.getCarPlate());
            if (CollUtil.isNotEmpty(dates)) {
                return dates.contains(w.getAccidentTime().toLocalDate());
            }
            return false;
        }).map(NdVehicleAccident::getId).collect(Collectors.toList());
        if (!reported.isEmpty()) {
            LambdaUpdateWrapper<NdVehicleAccident> update = Wrappers.lambdaUpdate(NdVehicleAccident.class)
                    .set(NdVehicleAccident::getReportStatus, VehicleAccidentReportStatusEnum.REPORTED.getCode())
                    .in(NdVehicleAccident::getId, reported);
            vehicleAccidentService.update(update);
        }
    }

    public PageVo<NdVehicleAccidentDeadVO> queryDead(AccidentPageQuery accidentPageQuery) {
        ((VehicleAccidentManage) AopContext.currentProxy()).queryAccidentDead();
        //以死亡事故为维度
        Page<NdVehicleAccidentDead> page = accidentPageQuery.page();

        // 按照登录用户，如果是运营商账号登录，删选出该运营商关联的车辆信息
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long roleId = userDetail.getRoleId();
        if (UserRoleTypeEnum.OPERATOR_ROLE_ID.getId().equals(roleId)) {
            String companyName = userDetail.getCompanyName();
            // 暂时控制运营商名称和运营商所在企业名称相同
            OperatorQueryDTO operatorQueryDTO = operatorsCacheHelper.getByName(companyName);
            // 获取运营商ID
            Long operatorId = operatorQueryDTO.getId();
            // 查询出该运营商下所有车辆
            List<NdVehicleBaseInfo> baseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .eq(NdVehicleBaseInfo::getOperatorId, operatorId));
            List<String> carPlateList = baseInfoList.stream().map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
            accidentPageQuery.setCarPlateList(carPlateList);
        }

        vehicleAccidentDead(page, accidentPageQuery);
        List<NdVehicleAccidentDeadVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentDeadVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentDeadVO.class);
            CodeUtil.searchCompanyNameAndRegionName(vo);
            vo.setReportStatusName(VehicleAccidentReportStatusEnum.getDescByCode(e.getReportStatus()));
            vo.setBriefCase(e.getBriefCase());
            vo.setFirmContent(e.getFirmContent());
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }
    public PageVo<NdVehicleAccidentDeadVO> queryDeadOpen(AccidentPageQuery accidentPageQuery) {
        ((VehicleAccidentManage) AopContext.currentProxy()).queryAccidentDead();
        //以死亡事故为维度
        Page<NdVehicleAccidentDead> page = accidentPageQuery.page();
        LambdaQueryWrapper<NdVehicleAccidentDead> wrapper = Wrappers.lambdaQuery(NdVehicleAccidentDead.class);
        searchvehicleAccidentDeadWrapper(page, wrapper, accidentPageQuery);
        List<NdVehicleAccidentDeadVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentDeadVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentDeadVO.class);
            CodeUtil.searchCompanyNameAndRegionName(vo);
            vo.setReportStatusName(VehicleAccidentReportStatusEnum.getDescByCode(e.getReportStatus()));
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }

    @Async
    protected void queryAccidentDead() {
        log.info("查询时车辆出险上报校对");
        //获取事故上报审核列表中通过审核的上报信息
        List<VehicleAccidentReport> reportList = vehicleAccidentReportService.list(Wrappers.lambdaQuery(VehicleAccidentReport.class)
                .select(VehicleAccidentReport::getCarPlate, VehicleAccidentReport::getAccidentTime)
                .eq(VehicleAccidentReport::getAuditStatus, VehicleAccidentReportEnum.AUDIT_APPROVED.getCode()));
        Map<String, Set<LocalDate>> carPlateMapDate = reportList.stream()
                .collect(Collectors.groupingBy(VehicleAccidentReport::getCarPlate,
                        Collectors.mapping(w -> w.getAccidentTime().toLocalDate(), Collectors.toSet())));
        //获取车辆死亡事故信息表中的所有数据
        List<NdVehicleAccidentDead> records = vehicleAccidentDeadService.list(Wrappers.lambdaQuery(NdVehicleAccidentDead.class)
                .select(NdVehicleAccidentDead::getCarPlate, NdVehicleAccidentDead::getAccidentTime));
        //对比两个列表中的数据，更新车辆死亡事故信息表中的上报状态
        List<Long> reported = records.stream().filter(w -> {
            Set<LocalDate> dates = carPlateMapDate.get(w.getCarPlate());
            if (CollUtil.isNotEmpty(dates)) {
                return dates.contains(w.getAccidentTime().toLocalDate());
            }
            return false;
        }).map(NdVehicleAccidentDead::getId).collect(Collectors.toList());
        if (!reported.isEmpty()) {
            LambdaUpdateWrapper<NdVehicleAccidentDead> update = Wrappers.lambdaUpdate(NdVehicleAccidentDead.class)
                    .set(NdVehicleAccidentDead::getReportStatus, VehicleAccidentReportStatusEnum.REPORTED.getCode())
                    .in(NdVehicleAccidentDead::getId, reported);
            vehicleAccidentDeadService.update(update);
        }
    }

    public PageVo<NdVehicleAccidentAnalysisVO> queryAccidentAnalysis(AccidentPageQuery accidentPageQuery) {
        //事故分析
        Page<NdVehicleAccidentAnalysis> page = accidentPageQuery.page();

        // 按照登录用户，如果是运营商账号登录，删选出该运营商关联的车辆信息
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long roleId = userDetail.getRoleId();
        if (UserRoleTypeEnum.OPERATOR_ROLE_ID.getId().equals(roleId)) {
            String companyName = userDetail.getCompanyName();
            // 暂时控制运营商名称和运营商所在企业名称相同
            OperatorQueryDTO operatorQueryDTO = operatorsCacheHelper.getByName(companyName);
            // 获取运营商ID
            Long operatorId = operatorQueryDTO.getId();
            // 查询出该运营商下所有车辆
            List<NdVehicleBaseInfo> baseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .eq(NdVehicleBaseInfo::getOperatorId, operatorId));
            List<String> carPlateList = baseInfoList.stream().map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
            accidentPageQuery.setCarPlateList(carPlateList);
        }

        vehicleAccidentAnalysisService.pageAnalysis(page, accidentPageQuery);
        if (0L == page.getTotal()) {
            return PageVo.empty();
        }
        List<NdVehicleAccidentAnalysisVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentAnalysisVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentAnalysisVO.class);
            CodeUtil.searchRegionName(vo);
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }
    public PageVo<NdVehicleAccidentAnalysisVO> queryAccidentAnalysisOpen(AccidentPageQuery accidentPageQuery) {
        //事故分析
        Page<NdVehicleAccidentAnalysis> page = accidentPageQuery.page();
        vehicleAccidentAnalysisService.pageAnalysisOpen(page, accidentPageQuery);
        if (0L == page.getTotal()) {
            return PageVo.empty();
        }
        List<NdVehicleAccidentAnalysisVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentAnalysisVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentAnalysisVO.class);
            CodeUtil.searchRegionName(vo);
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }

    public void vehicleAccident(Page<NdVehicleAccident> page, AccidentPageQuery accidentPageQuery) {
        LambdaQueryWrapper<NdVehicleAccident> wrapper = Wrappers.lambdaQuery(NdVehicleAccident.class);
        VehicleAccidentManage clazz = (VehicleAccidentManage) AopContext.currentProxy();
        clazz.searchVehicleAccidentWrapper(wrapper, accidentPageQuery);
        vehicleAccidentService.page(page,wrapper);
    }

    @LambdaDataScope
    @BuildChildrenRegionWrapper
    public void searchVehicleAccidentWrapper(LambdaQueryWrapper<NdVehicleAccident> wrapper, AccidentPageQuery param) {
        wrapper.ge(Objects.nonNull(param.getStartTime()), NdVehicleAccident::getAccidentTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()), NdVehicleAccident::getAccidentTime, param.getEndTime())
                .like(Objects.nonNull(param.getCarPlate()), NdVehicleAccident::getCarPlate, param.getCarPlate())
                .like(StringUtils.isNotBlank(param.getCompanyName()), NdVehicleAccident::getCompanyName, param.getCompanyName())
                .eq(Objects.nonNull(param.getVehicleType()), NdVehicleAccident::getVehicleType, param.getVehicleType())
                .in(CollUtil.isNotEmpty(param.getCarPlateList()),NdVehicleAccident::getCarPlate,param.getCarPlateList())
                .eq(Objects.nonNull(param.getAccidentType()),NdVehicleAccident::getAccidentType,param.getAccidentType())
                .eq(Objects.nonNull(param.getReportStatus()), NdVehicleAccident::getReportStatus, param.getReportStatus());
        wrapper.orderBy(Boolean.TRUE, Boolean.FALSE, NdVehicleAccident::getAccidentTime);
    }

    public void vehicleAccidentDead(Page<NdVehicleAccidentDead> page, AccidentPageQuery accidentPageQuery) {
        LambdaQueryWrapper<NdVehicleAccidentDead> wrapper = Wrappers.lambdaQuery(NdVehicleAccidentDead.class);
        VehicleAccidentManage clazz = (VehicleAccidentManage) AopContext.currentProxy();
        clazz.searchvehicleAccidentDeadWrapper(page, wrapper, accidentPageQuery);
    }

    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(entityIndex = 2, wrapperIndex = 1)
    public void searchvehicleAccidentDeadWrapper(Page<NdVehicleAccidentDead> page, LambdaQueryWrapper<NdVehicleAccidentDead> wrapper, AccidentPageQuery param) {
        wrapper.ge(Objects.nonNull(param.getStartTime()), NdVehicleAccidentDead::getAccidentTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()), NdVehicleAccidentDead::getAccidentTime, param.getEndTime())
                .like(Objects.nonNull(param.getCarPlate()), NdVehicleAccidentDead::getCarPlate, param.getCarPlate())
                .like(StringUtils.isNotBlank(param.getCompanyName()), NdVehicleAccidentDead::getCompanyName, param.getCompanyName())
                .eq(Objects.nonNull(param.getReportStatus()), NdVehicleAccidentDead::getReportStatus, param.getReportStatus());
        wrapper.orderBy(Boolean.TRUE, Boolean.FALSE, NdVehicleAccidentDead::getAccidentTime);
        vehicleAccidentDeadService.page(page, wrapper);
    }

    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(entityIndex = 2, wrapperIndex = 1)
    public void searchvehicleAccidentAnalysisWrapper(Page<NdVehicleAccidentAnalysis> page, LambdaQueryWrapper<NdVehicleAccidentAnalysis> wrapper, AccidentPageQuery param) {
        wrapper.ge(Objects.nonNull(param.getStartTime()), NdVehicleAccidentAnalysis::getAccidentTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()), NdVehicleAccidentAnalysis::getAccidentTime, param.getEndTime());
        wrapper.orderBy(Boolean.TRUE, Boolean.FALSE, NdVehicleAccidentAnalysis::getAccidentTime);
        vehicleAccidentAnalysisService.page(page, wrapper);
    }

    public PageVo<NdVehicleAccidentReportVO> reportAccidentInfo(AccidentPageQuery accidentPageQuery) {
        Page<VehicleAccidentReport> page = accidentPageQuery.page();

        // 按照登录用户，如果是运营商账号登录，删选出该运营商关联的车辆信息
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long roleId = userDetail.getRoleId();
        if (UserRoleTypeEnum.OPERATOR_ROLE_ID.getId().equals(roleId)) {
            String companyName = userDetail.getCompanyName();
            // 暂时控制运营商名称和运营商所在企业名称相同
            OperatorQueryDTO operatorQueryDTO = operatorsCacheHelper.getByName(companyName);
            // 获取运营商ID
            Long operatorId = operatorQueryDTO.getId();
            // 查询出该运营商下所有车辆所在的所有企业
            List<NdVehicleBaseInfo> baseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .eq(NdVehicleBaseInfo::getOperatorId, operatorId));
            List<String> carPlateList = baseInfoList.stream().map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
            accidentPageQuery.setCarPlateList(carPlateList);
        }

        vehicleAccidentReport(page, accidentPageQuery);
        List<NdVehicleAccidentReportVO> data = CollUtils.convert(page.getRecords(), e -> {
            NdVehicleAccidentReportVO vo = BeanUtil.copyProperties(e, NdVehicleAccidentReportVO.class);
            CodeUtil.searchCompanyNameAndRegionName(vo);
            String createTime = LocalDateTimeUtil.format(e.getCreateOn(), "yyyy-MM-dd HH:mm");
            vo.setCreateTime(createTime);
            return vo;
        });
        return PageVo.of(data, page.getTotal());
    }

    private void vehicleAccidentReport(Page<VehicleAccidentReport> page, AccidentPageQuery accidentPageQuery) {
        LambdaQueryWrapper<VehicleAccidentReport> wrapper = Wrappers.lambdaQuery(VehicleAccidentReport.class);
        VehicleAccidentManage clazz = (VehicleAccidentManage) AopContext.currentProxy();
        clazz.searchvehicleAccidentReportWrapper(page, wrapper, accidentPageQuery);
    }

    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(entityIndex = 2, wrapperIndex = 1)
    public void searchvehicleAccidentReportWrapper(Page<VehicleAccidentReport> page, LambdaQueryWrapper<VehicleAccidentReport> wrapper, AccidentPageQuery param) {
        wrapper.ge(Objects.nonNull(param.getStartTime()), VehicleAccidentReport::getAccidentTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()), VehicleAccidentReport::getAccidentTime, param.getEndTime())
                .like(Objects.nonNull(param.getCarPlate()), VehicleAccidentReport::getCarPlate, param.getCarPlate())
                .in(CollUtil.isNotEmpty(param.getCarPlateList()),VehicleAccidentReport::getCarPlate,param.getCarPlateList())
                .eq(Objects.nonNull(param.getAuditStatus()), VehicleAccidentReport::getAuditStatus, param.getAuditStatus());
        wrapper.orderBy(Boolean.TRUE, Boolean.FALSE, VehicleAccidentReport::getCreateOn);
        vehicleAccidentReportService.page(page, wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    public VehicleAccidentReport save(VehicleAccidentReportDTO data, Long userId) {
        //获取登录用户信息
        UserInfoDetails userInfoDetails = LoginUserUtil.loginUserDetail();
        //获取入参信息
        Long vehicleAccidentReportId = data.getId();
        VehicleAccidentReport vehicleAccidentReport;
        if (Objects.isNull(vehicleAccidentReportId)) {
            vehicleAccidentReport = new VehicleAccidentReport();
            BeanUtil.copyProperties(data, vehicleAccidentReport);
            vehicleAccidentReport.setRegionId(userInfoDetails.getRegionId());
            vehicleAccidentReport.setRegionName(userInfoDetails.getRegionName());
            vehicleAccidentReport.setCompanyId(userInfoDetails.getCompanyId());
            vehicleAccidentReport.setCompanyName(userInfoDetails.getCompanyName());
            vehicleAccidentReport.setCreateOn(LocalDateTime.now());
            vehicleAccidentReport.setCreateBy(userId);
            vehicleAccidentReport.setUpdateBy(userId);
            vehicleAccidentReport.setUpdateOn(LocalDateTime.now());
            vehicleAccidentReport.setAuditStatus(VehicleAccidentReportEnum.UN_AUDIT.getCode());
            vehicleAccidentReportService.save(vehicleAccidentReport);
        } else {
            vehicleAccidentReport = vehicleAccidentReportService.getById(vehicleAccidentReportId);
            BeanUtil.copyProperties(data, vehicleAccidentReport);
            vehicleAccidentReport.setRegionId(userInfoDetails.getRegionId());
            vehicleAccidentReport.setRegionName(userInfoDetails.getRegionName());
            vehicleAccidentReport.setCompanyId(userInfoDetails.getCompanyId());
            vehicleAccidentReport.setCompanyName(userInfoDetails.getCompanyName());
            vehicleAccidentReport.setUpdateBy(userId);
            vehicleAccidentReport.setUpdateOn(LocalDateTime.now());
            vehicleAccidentReport.setAuditStatus(VehicleAccidentReportEnum.UN_AUDIT.getCode());
            vehicleAccidentReportService.updateById(vehicleAccidentReport);
        }
        return vehicleAccidentReport;
    }

    public VehicleAccidentReport getDetail(Long id) {
        return vehicleAccidentReportService.getById(id);
    }

    public Boolean delete(Long id) {
        VehicleAccidentReport vehicleAccidentReport = vehicleAccidentReportService.getById(id);
        Long companyId = vehicleAccidentReport.getCompanyId();
        CompanyDTO companyDto = companiesCacheHelper.getById(companyId);
        // 判断上报数据的企业所在的区域是否在登录用户所属的区域权限下
        Long countryRegionId = companyDto.getRegionId();
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long regionId = userDetail.getRegionId();
        List<Long> childList = regionsCacheHelper.listChildRegionId(regionId, true, true);
        // 不在本级及下级区域的上报数据，不允许删除
        if (!childList.contains(countryRegionId)){
            return Boolean.FALSE;
        }
        return vehicleAccidentReportService.removeById(id);
    }

    public Boolean audit(AccidentAuditDTO param) {

        VehicleAccidentReport vehicleAccidentReport = vehicleAccidentReportService.getById(param.getId());
        Long companyId = vehicleAccidentReport.getCompanyId();
        CompanyDTO companyDto = companiesCacheHelper.getById(companyId);
        // 判断上报数据的企业所在的区域是否在登录用户所属的区域权限下
        Long countryRegionId = companyDto.getRegionId();
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long regionId = userDetail.getRegionId();
        List<Long> childList = regionsCacheHelper.listChildRegionId(regionId, true, true);
        // 不在本级及下级区域的上报数据，不允许审核
        if (!childList.contains(countryRegionId)){
            return Boolean.FALSE;
        }

        if (Objects.isNull(vehicleAccidentReport)) {
            throw new BizException("审核记录不存在!");
        }
        if (!VehicleAccidentReportEnum.UN_AUDIT.getCode().equals(vehicleAccidentReport.getAuditStatus())) {
            throw new BizException("只能审核待审核的上报信息!");
        }
        vehicleAccidentReport.setAuditStatus(param.getAuditStatus());
        vehicleAccidentReport.setAuditOpinion(param.getAuditOpinion());
        vehicleAccidentReport.setAuditor(LoginUserUtil.getUsername());
        vehicleAccidentReport.setAuditorUserId(LoginUserUtil.getUserId());
        vehicleAccidentReport.setUpdateOn(LocalDateTime.now());
        boolean result = vehicleAccidentReportService.updateById(vehicleAccidentReport);
        Integer injuries = vehicleAccidentReport.getInjuries();
        Integer deathToll = vehicleAccidentReport.getDeathToll();

        // 如果审核通过
        if (VehicleAccidentReportEnum.AUDIT_APPROVED.getCode().equals(param.getAuditStatus())){
            // 判断该数据是否在车辆事故信息表和车辆死亡事故信息中，如果不在要发布事件
            LocalDateTime accidentTime = vehicleAccidentReport.getAccidentTime();
            LocalDateTime startTime = accidentTime.minusMinutes(5L);
            LocalDateTime endTime = accidentTime.plusMinutes(5L);
            String startTimeFormat = NdDateUtils.format(startTime, "yyyy-MM-dd HH:mm:00");
            String endTimeFormat = NdDateUtils.format(endTime, "yyyy-MM-dd HH:mm:59");
            LocalDateTime queryStartTime = LocalDateTimeUtil.parse(startTimeFormat,"yyyy-MM-dd HH:mm:00");
            LocalDateTime queryEndTime = LocalDateTimeUtil.parse(endTimeFormat,"yyyy-MM-dd HH:mm:59");

            NdVehicleAccident vehicleAccident = vehicleAccidentService.getOne(Wrappers.lambdaQuery(NdVehicleAccident.class)
                    .eq(NdVehicleAccident::getCarPlate, vehicleAccidentReport.getCarPlate())
                    .eq(NdVehicleAccident::getAccidentTime, vehicleAccidentReport.getAccidentTime())
                    .ge(NdVehicleAccident::getAccidentTime,queryStartTime)
                    .le(NdVehicleAccident::getAccidentTime,queryEndTime)
                    .eq(NdVehicleAccident::getAccidentDriver, vehicleAccidentReport.getAccidentDriver()));

            NdVehicleAccidentDead vehicleAccidentDead = vehicleAccidentDeadService.getOne(Wrappers.lambdaQuery(NdVehicleAccidentDead.class)
                    .eq(NdVehicleAccidentDead::getCarPlate, vehicleAccidentReport.getCarPlate())
                    .eq(NdVehicleAccidentDead::getAccidentDriver, vehicleAccidentReport.getAccidentDriver())
                    .ge(NdVehicleAccidentDead::getAccidentTime,queryStartTime)
                    .le(NdVehicleAccidentDead::getAccidentTime,queryEndTime)
                    .eq(NdVehicleAccidentDead::getDeathToll, vehicleAccidentReport.getDeathToll()));

            // 既没有受伤人数也没有死亡人数,不发布事件
            if (injuries == 0 && deathToll == 0){
                return result;
            }
        }
        return result;
    }

    public void exportVehicleAccidentData(AccidentPageQuery accidentPageQuery, HttpServletResponse response) {
        ((VehicleAccidentManage) AopContext.currentProxy()).queryAccident();
        // 以车辆事故维度
        // 按照登录用户，如果是运营商账号登录，删选出该运营商关联的车辆信息
        UserInfoDetails userDetail = LoginUserUtil.loginUserDetail();
        Long roleId = userDetail.getRoleId();
        if (UserRoleTypeEnum.OPERATOR_ROLE_ID.getId().equals(roleId)) {
            String companyName = userDetail.getCompanyName();
            // 暂时控制运营商名称和运营商所在企业名称相同
            OperatorQueryDTO operatorQueryDTO = operatorsCacheHelper.getByName(companyName);
            // 获取运营商ID
            Long operatorId = operatorQueryDTO.getId();
            // 查询出该运营商下所有车辆
            List<NdVehicleBaseInfo> baseInfoList = vehicleBaseInfoService.list(Wrappers.lambdaQuery(NdVehicleBaseInfo.class)
                    .eq(NdVehicleBaseInfo::getOperatorId, operatorId));
            List<String> carPlateList = baseInfoList.stream().map(NdVehicleBaseInfo::getCarPlate).collect(Collectors.toList());
            accidentPageQuery.setCarPlateList(carPlateList);
        }
        LambdaQueryWrapper<NdVehicleAccident> wrapper = Wrappers.lambdaQuery(NdVehicleAccident.class);
        VehicleAccidentManage clazz = (VehicleAccidentManage) AopContext.currentProxy();
        clazz.searchVehicleAccidentWrapper(wrapper, accidentPageQuery);
        List<NdVehicleAccident> accidents = vehicleAccidentService.list(wrapper);

        List<NdVehicleAccidentDTO> data = CollUtils.convert(accidents, e -> {
            NdVehicleAccidentDTO vo = BeanUtil.copyProperties(e, NdVehicleAccidentDTO.class);
            vo.setCarPlate(e.getCarPlate());
            vo.setAccidentTime(NdDateUtils.format(e.getAccidentTime(),NdDateUtils.DEFAULT_DATE_TIME_FORMAT));
            String desc = VehicleTypeEnum.getDescByCode(e.getVehicleType());
            Integer injuries = e.getInjuries();
            Integer deathToll = e.getDeathToll();
            vo.setInjuries(injuries);
            vo.setDeathToll(deathToll);
            vo.setAccidentTypeName(AccidentTypeEnum.getDescByCode(e.getAccidentType()));
            vo.setBriefCase(e.getBriefCase());
            vo.setAccidentNo(e.getAccidentNo());
            vo.setFirmContent(e.getFirmContent());
            vo.setVehicleTypeName(desc);
            vo.setReportStatusName(VehicleAccidentReportStatusEnum.getDescByCode(e.getReportStatus()));
            CodeUtil.searchCompanyNameAndRegionName(vo);
            return vo;
        });

        File directory = new File("");
        String templateName;
        String templatePath;
        List<Object> dtoList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(data)) {
            templateName = ReportConstant.VEHICLE_ACCIDENT_DATA_TEMPLATE;
            List<Object> dataList = new ArrayList<>();
            data.forEach(m -> {
                LinkedHashMap<String, Object> dataMap = new LinkedHashMap<>();
                dataMap.put("regionName",m.getRegionName());
                dataMap.put("carPlate",m.getCarPlate());
                dataMap.put("accidentTime",m.getAccidentTime());
                dataMap.put("accidentDriver",m.getAccidentDriver());
                dataMap.put("vehicleTypeName",m.getVehicleTypeName());
                dataMap.put("companyName",m.getCompanyName());
                dataMap.put("accidentAddress",m.getAccidentAddress());
                dataMap.put("injuries",m.getInjuries());
                dataMap.put("deathToll",m.getDeathToll());
                dataMap.put("reportStatusName",m.getReportStatusName());
                dataMap.put("accidentTypeName",m.getAccidentTypeName());
                dataMap.put("briefCase",m.getBriefCase());
                dataMap.put("accidentNo",m.getAccidentNo());
                dataMap.put("firmContent",m.getFirmContent());
                dataList.add(dataMap);
            });
            dtoList = dataList;
        }else {
            templateName = ReportConstant.VEHICLE_ACCIDENT_DATA_TEMPLATE_EMPTY;
        }
        templatePath = directory.getAbsolutePath() + File.separator + "template" + File.separator + templateName + ".xlsx";
        // 获取本地目录的Excel模板
        TemplateExportParams temp = new TemplateExportParams(templatePath);
        temp.setSheetNum(new Integer[] {0});
        temp.setSheetName(new String[] {templateName});
        Map<String, Object> map = new HashMap<>(4);
        map.put("mapList", dtoList);
        Workbook workbook = ExcelExportUtil.exportExcel(temp, map);
        Sheet sheet = workbook.getSheet(templateName);
        sheet.autoSizeColumn(0);
        // 设置所有列宽自适应
        for (int colnum = 0; colnum < 13; colnum++) {
            sheet.autoSizeColumn(colnum);
        }
        if (CollUtil.isEmpty(dtoList)) {
            // 输出空模板
            // 获取本地目录的空Excel模板
            try {
                InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream("template" + File.separator + templateName + ".xlsx");
                OutputStream outputStream = response.getOutputStream();
                // 设置要下载的文件的名称
                ExcelDownUtil.setFileName(templateName, response);
                // 设置文件的MIME类型
                response.setContentType("application/vnd.ms-excel;charset=UTF-8");

                byte[] b = new byte[2048];
                int len;
                while ((len = templateInputStream.read(b)) != -1) {
                    outputStream.write(b, 0, len);
                }
                templateInputStream.close();
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                throw new BizException("读取模板失败!");
            }
            return;
        }
        if (workbook == null){
            throw new BizException("读取编辑表模板失败!");
        }
        // 重置响应对象
        response.reset();
        try {
            response.setHeader("Content-disposition",
                    "attachment;filename*=utf-8''" + URLEncoder.encode(Objects.requireNonNull(templateName), "UTF-8") + ".xlsx");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setContentType(ExcelUtil.XLS_CONTENT_TYPE);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        // 写出数据输出流到页面
        try {
            OutputStream output = response.getOutputStream();
            BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);
            workbook.write(bufferedOutPut);
            bufferedOutPut.flush();
            bufferedOutPut.close();
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
