package com.yeejoin.amos.boot.module.jg.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.module.common.api.dto.FormValue;
import com.yeejoin.amos.boot.module.jg.api.dto.JgUseRegistrationManageDto;
import com.yeejoin.amos.boot.module.jg.api.dto.UseFlagParamDto;
import com.yeejoin.amos.boot.module.jg.api.entity.*;
import com.yeejoin.amos.boot.module.jg.api.enums.BusinessTypeEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.CertificateStatusEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.CylinderTypeEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.EquipSourceEnum;
import com.yeejoin.amos.boot.module.jg.api.mapper.*;
import com.yeejoin.amos.boot.module.jg.api.service.IJgChangeRegistrationTransferService;
import com.yeejoin.amos.boot.module.jg.api.service.IJgUseRegistrationManageService;
import com.yeejoin.amos.boot.module.jg.api.vo.SortVo;
import com.yeejoin.amos.boot.module.jg.biz.feign.TzsServiceFeignClient;
import com.yeejoin.amos.boot.module.ymt.api.common.StringUtil;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgMaintenanceRecordInfo;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgProjectContraption;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgUseInfo;
import com.yeejoin.amos.boot.module.ymt.api.enums.ApplicationFormTypeEnum;
import com.yeejoin.amos.boot.module.ymt.api.enums.EquimentEnum;
import com.yeejoin.amos.boot.module.ymt.api.enums.EquipmentCategoryEnum;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.rdbms.service.BaseService;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import org.typroject.tyboot.core.restful.utils.ResponseModel;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.alibaba.fastjson.JSON.parseArray;
import static com.yeejoin.amos.boot.module.jg.biz.service.impl.CommonServiceImpl.isValidCreditCode;
import static com.yeejoin.amos.boot.module.jg.biz.service.impl.JgUseRegistrationServiceImpl.getAuditPassedDate;

/**
 * 服务实现类
 *
 * @author system_generator
 * @date 2024-07-03
 */
@Service
public class JgUseRegistrationManageServiceImpl extends BaseService<JgUseRegistrationManageDto, JgUseRegistrationManage, JgUseRegistrationManageMapper> implements IJgUseRegistrationManageService {

    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Autowired
    private JgUseRegistrationManageMapper jgUseRegistrationManageMapper;

    @Autowired
    private JgUseRegistrationMapper jgUseRegistrationMapper;

    @Autowired
    private JgUseRegistrationEqMapper jgUseRegistrationEqMapper;

    @Autowired
    private CommonServiceImpl commonServiceImpl;

    @Autowired
    private IdxBizJgUseInfoServiceImpl idxBizJgUseInfoService;

    @Autowired
    private JgVehicleInformationMapper jgVehicleInformationMapper;


    @Autowired
    private JgCertificateChangeRecordServiceImpl jgCertificateChangeRecordService;

    @Autowired
    private JgUseRegistrationServiceImpl jgUseRegistrationService;

    @Autowired
    private JgVehicleInformationServiceImpl jgVehicleInformationService;

    @Autowired
    private IJgChangeRegistrationTransferService jgChangeRegistrationTransferService;

    @Autowired
    private CommonServiceImpl commonService;

    @Autowired
    private IdxBizJgMaintenanceRecordInfoServiceImpl idxBizJgMaintenanceRecordInfoService;

    @Value("classpath:/json/registrationBasic.json")
    private Resource registrationBasicJson;

    private static Map<String, String> regionCodeOrgCodeMap = new ConcurrentHashMap<>();
    @Autowired
    CommonMapper commonMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    TzsServiceFeignClient tzsServiceFeignClient;

    @Autowired
    private JgCertificateChangeRecordMapper certificateChangeRecordMapper;

    private static final long SCROLL_TIMEOUT = 180000;
    private static final int SIZE = 1000;


    /**
     * 将已经通过使用登记审批的证信息录入到 jg-use-registration-manage 表中
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean brushHistoryCertificate(String type) {

        if (type.equals("1")){
            this.baseMapper.delete(new LambdaQueryWrapper<JgUseRegistrationManage>().eq(JgUseRegistrationManage::getRegType, BusinessTypeEnum.JG_USAGE_REGISTRATION.getName()));
            // 使用登记表中已经审批通过的单子
            List<JgUseRegistration> jgUseRegistrations = jgUseRegistrationMapper.selectList(new LambdaQueryWrapper<JgUseRegistration>()
                    .eq(JgUseRegistration::getStatus, "已完成")
                    .eq(JgUseRegistration::getIsDelete, false));
            for (JgUseRegistration useRegistration : jgUseRegistrations) {
                // 使用单位信息
                Map<String, Object> enterpriseInfo = commonServiceImpl.getEnterpriseInfo(useRegistration.getUseUnitCreditCode());
                // 设备使用地址
                String fullAddress = "";
                // 设备种类/类别/品种
                Map<String, String> equType = jgUseRegistrationMapper.getEquTypeByUseRegSeq(String.valueOf(useRegistration.getSequenceNbr()));
                if (!"unit".equals(useRegistration.getManageType())) {
                    List<JgUseRegistrationEq> jgUseRegistrationEqs = jgUseRegistrationEqMapper.selectList(new LambdaQueryWrapper<JgUseRegistrationEq>()
                            .eq(JgUseRegistrationEq::getEquipTransferId, useRegistration.getSequenceNbr()));
                    if (!ValidationUtil.isEmpty(jgUseRegistrationEqs)) {
                        String equId = jgUseRegistrationEqs.get(0).getEquId();
                        IdxBizJgUseInfo useInfo = idxBizJgUseInfoService.getOne(new QueryWrapper<IdxBizJgUseInfo>()
                                .eq("RECORD", equId));
                        if (!ObjectUtils.isEmpty(useInfo.getProvinceName())) {
                            fullAddress += useInfo.getProvinceName();
                        }
                        if (!ObjectUtils.isEmpty(useInfo.getCityName())) {
                            fullAddress += useInfo.getCityName();
                        }
                        if (!ObjectUtils.isEmpty(useInfo.getCountyName())) {
                            fullAddress += useInfo.getCountyName();
                        }
                        if (!ObjectUtils.isEmpty(useInfo.getStreetName())) {
                            fullAddress += useInfo.getStreetName();
                        }
                        if (!ObjectUtils.isEmpty(useInfo.getAddress())) {
                            fullAddress += useInfo.getAddress();
                        }
                    }
                }
                // 组装数据
                JgUseRegistrationManage jgUseRegistrationManage = new JgUseRegistrationManage();
                jgUseRegistrationManage.setUseUnitName(useRegistration.getUseUnitName());
                jgUseRegistrationManage.setApplyNo(useRegistration.getApplyNo());
                jgUseRegistrationManage.setCertificateStatus(CertificateStatusEnum.YIDENGJI.getName());
                jgUseRegistrationManage.setReceiveOrgName(useRegistration.getReceiveOrgName());
                jgUseRegistrationManage.setAuditPassDate(useRegistration.getAuditPassDate());
                jgUseRegistrationManage.setRegType(BusinessTypeEnum.JG_USAGE_REGISTRATION.getName());
                jgUseRegistrationManage.setRegDate(useRegistration.getRegDate());
                jgUseRegistrationManage.setEquList(equType.get("equList"));
                jgUseRegistrationManage.setEquListCode(equType.get("equListCode"));
                jgUseRegistrationManage.setEquCategory(equType.get("equCategory"));
                jgUseRegistrationManage.setEquCategoryCode(equType.get("equCategoryCode"));
                jgUseRegistrationManage.setEquDefine(equType.get("equDefine"));
                jgUseRegistrationManage.setEquDefineCode(equType.get("equDefineCode"));
                jgUseRegistrationManage.setIsDelete(Boolean.FALSE);
                jgUseRegistrationManage.setRecUserId(useRegistration.getRecUserId());
                jgUseRegistrationManage.setRecUserName(useRegistration.getRecUserName());
                jgUseRegistrationManage.setRecDate(useRegistration.getRecDate());
                jgUseRegistrationManage.setCreateUserId(useRegistration.getCreateUserId());
                jgUseRegistrationManage.setCreateDate(useRegistration.getRecDate());
                jgUseRegistrationManage.setEquUseAddress(fullAddress);
                jgUseRegistrationManage.setManageType(useRegistration.getManageType());
                jgUseRegistrationManage.setUseUnitAddress(!ValidationUtil.isEmpty(enterpriseInfo) ? (String) enterpriseInfo.get("address") : "");
                jgUseRegistrationManage.setUseRegistrationCode(useRegistration.getUseRegistrationCode());
                jgUseRegistrationManage.setUseUnitCreditCode(useRegistration.getUseUnitCreditCode());
                jgUseRegistrationManage.setReceiveCompanyCode(useRegistration.getReceiveCompanyCode());
                jgUseRegistrationManage.setCertificateNo(commonServiceImpl.generateCertificateNo(equType, useRegistration.getAuditPassDate(), useRegistration.getReceiveCompanyCode()));
                this.baseMapper.insert(jgUseRegistrationManage);
            }
        }
        if (type.equals("2")){
            this.baseMapper.delete(new LambdaQueryWrapper<JgUseRegistrationManage>().eq(JgUseRegistrationManage::getRegType, BusinessTypeEnum.JG_VEHICLE_GAS_APPLICATION.getName()));
            // 车用气瓶使用登记表中已经审批通过的单子
            List<JgVehicleInformation> jgVehicleInformations = jgVehicleInformationMapper.selectList(new LambdaQueryWrapper<JgVehicleInformation>()
                    .eq(JgVehicleInformation::getStatus, "已完成")
                    .eq(JgVehicleInformation::getIsDelete, false));
            for (JgVehicleInformation vehicleInformation : jgVehicleInformations) {
                // 使用单位信息
                Map<String, Object> enterpriseInfo = commonServiceImpl.getEnterpriseInfo(vehicleInformation.getUseUnitCreditCode());
                // 设备种类/类别/品种
                Map<String, String> equType = jgUseRegistrationMapper.getEquTypeByVehSeq(String.valueOf(vehicleInformation.getSequenceNbr()));
                // 组装数据
                JgUseRegistrationManage jgUseRegistrationManage = new JgUseRegistrationManage();
                jgUseRegistrationManage.setUseUnitName(vehicleInformation.getUseUnitName());
                jgUseRegistrationManage.setApplyNo(vehicleInformation.getApplyNo());
                jgUseRegistrationManage.setCertificateStatus(CertificateStatusEnum.YIDENGJI.getName());
                jgUseRegistrationManage.setReceiveOrgName(vehicleInformation.getReceiveOrgName());
                jgUseRegistrationManage.setAuditPassDate(vehicleInformation.getAuditPassDate());
                jgUseRegistrationManage.setRegType(BusinessTypeEnum.JG_VEHICLE_GAS_APPLICATION.getName());
                jgUseRegistrationManage.setRegDate(vehicleInformation.getRegDate());
                jgUseRegistrationManage.setEquList(equType.get("equList"));
                jgUseRegistrationManage.setEquListCode(equType.get("equListCode"));
                jgUseRegistrationManage.setEquCategory(equType.get("equCategory"));
                jgUseRegistrationManage.setEquCategoryCode(equType.get("equCategoryCode"));
                jgUseRegistrationManage.setEquDefine(equType.get("equDefine"));
                jgUseRegistrationManage.setEquDefineCode(equType.get("equDefineCode"));
                jgUseRegistrationManage.setIsDelete(Boolean.FALSE);
                jgUseRegistrationManage.setRecUserId(vehicleInformation.getRecUserId());
                jgUseRegistrationManage.setRecUserName(vehicleInformation.getRecUserName());
                jgUseRegistrationManage.setRecDate(vehicleInformation.getRecDate());
                jgUseRegistrationManage.setCreateUserId(vehicleInformation.getCreateUserId());
                jgUseRegistrationManage.setCreateDate(vehicleInformation.getRecDate());
                jgUseRegistrationManage.setEquUseAddress("");
                jgUseRegistrationManage.setManageType("unit");
                jgUseRegistrationManage.setCarNumber(vehicleInformation.getCarNumber());
                jgUseRegistrationManage.setUseUnitAddress(!ValidationUtil.isEmpty(enterpriseInfo) ? (String) enterpriseInfo.get("address") : "");
                jgUseRegistrationManage.setUseRegistrationCode(vehicleInformation.getUseRegistrationCode());
                jgUseRegistrationManage.setUseUnitCreditCode(vehicleInformation.getUseUnitCreditCode());
                jgUseRegistrationManage.setReceiveCompanyCode(vehicleInformation.getReceiveCompanyCode());
                jgUseRegistrationManage.setGasNum(vehicleInformation.getGasNum());
                jgUseRegistrationManage.setFillingMedium(vehicleInformation.getFillingMedium());
                jgUseRegistrationManage.setVolume(vehicleInformation.getVolume());
                jgUseRegistrationManage.setCertificateNo(commonServiceImpl.generateCertificateNo(equType, vehicleInformation.getAuditPassDate(), vehicleInformation.getReceiveCompanyCode()));
                this.baseMapper.insert(jgUseRegistrationManage);
            }
        }

        return Boolean.TRUE;
    }


    /**
     * 分页查询
     */
    public Page<JgUseRegistrationManageDto> queryForJgUseRegistrationManagePage(Page<JgUseRegistrationManageDto> page,
                                                                                JgUseRegistrationManageDto dto,
                                                                                String sort) {
        SortVo sortMap = commonServiceImpl.sortFieldConversion(sort);
        if (ApplicationFormTypeEnum.BF.getBusinessCode().equals(dto.getApplyType()) && (!CylinderTypeEnum.CYLINDER.getCode().equals(dto.getEquCategoryCode()) || dto.getRegType().equals(BusinessTypeEnum.JG_VEHICLE_GAS_APPLICATION.getName()))){
            dto.setCertificateStatus(null);
            dto.setIsScrap("0");
        }
        return jgUseRegistrationManageMapper.queryForPage(page, dto, sortMap);

    }

    public Page<JgUseRegistrationManageDto> getRecords(Page<JgUseRegistrationManageDto> page, JgUseRegistrationManageDto dto, String cityCode) {
        String orgCode = getAndSetOrgCode(cityCode);
        return jgUseRegistrationManageMapper.getRecords(page, dto, orgCode);

    }

    public String getAndSetOrgCode(String cityCode) {
        String orgCode = regionCodeOrgCodeMap.get(cityCode);
        if (orgCode == null) {
            orgCode = commonMapper.getOrgCodeByCompanyCode(cityCode);
            if (orgCode != null) {
                regionCodeOrgCodeMap.put(cityCode, orgCode);
            }
        }
        return orgCode;
    }
    /**
     * 根据sequenceNbr查询---使用登记证详情用
     *
     * @param sequenceNbr 主键
     * @return
     */
    public JgUseRegistrationManageDto queryDetailBySeq(String sequenceNbr) {
        JgUseRegistrationManage jgUseRegistrationManage = this.baseMapper.selectById(sequenceNbr);
        JgUseRegistrationManageDto jgUseRegistrationManageDto=new JgUseRegistrationManageDto();
        if(Objects.nonNull(jgUseRegistrationManage)){
            BeanUtil.copyProperties(jgUseRegistrationManage,jgUseRegistrationManageDto);
            try {
                jgUseRegistrationManageDto.setAuditPassDate(DateUtils.dateFormat(jgUseRegistrationManage.getAuditPassDate(),DateUtils.DATE_PATTERN));
                jgUseRegistrationManageDto.setRegDate(DateUtils.dateFormat(jgUseRegistrationManage.getRegDate(),DateUtils.DATE_PATTERN));
                jgUseRegistrationManageDto.setCreateDate(DateUtils.dateFormat(jgUseRegistrationManage.getCreateDate(),DateUtils.DATE_TIME_PATTERN));
            } catch (ParseException e) {
               log.error(e.getMessage(),e);
            }
        }
        return  jgUseRegistrationManageDto;
    }

    /**
     * 根据sequenceNbr查询使用登记证 操作流水---使用登记证详情用
     *
     * @param sequenceNbr 主键
     * @return
     */
    public List<Map<String, String>> operationRecord(String sequenceNbr) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        JgUseRegistrationManage jgUseRegistrationManage = this.baseMapper.selectById(sequenceNbr);
        List<JgCertificateChangeRecord> changeRecordList = jgCertificateChangeRecordService.list(new LambdaQueryWrapper<JgCertificateChangeRecord>()
                .eq(JgCertificateChangeRecord::getUseRegistrationCode, jgUseRegistrationManage.getUseRegistrationCode())
                .or().eq(JgCertificateChangeRecord::getCertificateNo, jgUseRegistrationManage.getCertificateNo())
                .orderByDesc(JgCertificateChangeRecord::getCreateDate));
        return changeRecordList.stream()
                .map(x -> {
                    Map<String, String> map = new HashMap<>();
                    map.put("operatingTime", simpleDateFormat.format(x.getRecDate()));
                    map.put("content", x.getChangeContent());
                    map.put("routePath", x.getRoutePath());
                    return map;
                }).collect(Collectors.toList());
    }

    /**
     * 根据sequenceNbr查询使用登记证对应设备列表---使用登记证详情用
     * 分页接口
     *
     * @param sequenceNbr 主键
     * @return
     */
    public Page<JSONObject> certificateEquList(int current, int size, String sequenceNbr) {
        return queryEquForPageByCertificateSeqList("",Collections.singletonList(Long.parseLong(sequenceNbr)), current, size);
    }

    /**
     * 列表查询
     */
    public List<JgUseRegistrationManageDto> queryByUseUnitCreditCode(JgUseRegistrationManageDto dto) {
        if("1".equals(dto.getWhetherVehicleCylinder())){
            dto.setEquCategoryCode(CylinderTypeEnum.CYLINDER.getCode());
        }
        return jgUseRegistrationManageMapper.queryByUseUnitCreditCode(dto);
    }

    /**
     * 根据证的sequenceNbr，查询证下面的所有【已认领】设备
     */
    public List<JSONObject> queryEquByCertificateSeq(Long sequenceNbr) {
        return queryEquByCertificateSeqList(Collections.singletonList(sequenceNbr));
    }

    /**
     * 根据证的sequenceNbr集合，批量查询证下面的所有【已认领】设备
     */
    public List<JSONObject> queryEquByCertificateSeqList(List<Long> sequenceNbrList) {
        List<JgUseRegistrationManage> jgUseRegistrationManageList = this.baseMapper.selectList(new LambdaQueryWrapper<JgUseRegistrationManage>()
                .in(JgUseRegistrationManage::getSequenceNbr, sequenceNbrList)
                .eq(JgUseRegistrationManage::getIsDelete, 0)
                .select(JgUseRegistrationManage::getUseRegistrationCode));
        if (ValidationUtil.isEmpty(jgUseRegistrationManageList)) {
            return new ArrayList<>();
        }
        // List<JSONObject> result = new ArrayList<>();
        Set<String> useOrgCodes = jgUseRegistrationManageList.stream().map(JgUseRegistrationManage::getUseRegistrationCode).collect(Collectors.toSet());

        // es中通过查询【使用登记证编号】所有设备
        // SearchRequest request = new SearchRequest("idx_biz_view_jg_all");
        // SearchSourceBuilder builder = new SearchSourceBuilder();
        // builder.trackTotalHits(true);

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        TermsQueryBuilder termsQuery = QueryBuilders.termsQuery("USE_ORG_CODE", useOrgCodes);
        boolQuery.must(termsQuery);
        // builder.query(boolQuery);
        // builder.size(useOrgCodes.size());
        // request.source(builder);
        BoolQueryBuilder meBuilder = QueryBuilders.boolQuery();
        meBuilder.should(QueryBuilders.matchQuery("STATUS", "已认领"));
        meBuilder.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("STATUS")));
        meBuilder.should(QueryBuilders.boolQuery().must(QueryBuilders.matchPhraseQuery("STATUS", "")));
        meBuilder.minimumShouldMatch(1);
        boolQuery.must(meBuilder);

        List<JSONObject> result = Collections.emptyList();
        try {
            result = searchResponse("idx_biz_view_jg_all", boolQuery, hit -> JSONObject.parseObject(hit.getSourceAsString(), JSONObject.class));
        }catch (Exception ex){
            ex.printStackTrace();
        }
        // try {
        //     SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //     for (SearchHit hit : response.getHits().getHits()) {
        //         JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
        //         JSONObject dto2 = jsonObject.getJSONObject("sourceAsMap");
        //         result.add(dto2);
        //     }
        // } catch (Exception e) {
        //     e.printStackTrace();
        // }
        return result;
    }

    /**
     * 构建SearchResponse
     *
     * @param indices 索引
     * @param query   queryBuilder
     * @param fun     返回函数
     * @param <T>     返回类型
     * @return List, 可以使用fun转换为T结果
     * @throws Exception e
     */
    public <T> List<T> searchResponse(String indices, QueryBuilder query, Function<SearchHit, T> fun) throws Exception {
        SearchRequest request = new SearchRequest(indices);
        Scroll scroll = new Scroll(TimeValue.timeValueMillis(SCROLL_TIMEOUT));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(query);
        sourceBuilder.size(SIZE);

        request.scroll(scroll);
        request.source(sourceBuilder);

        List<String> scrollIdList = new ArrayList<>();
        List<T> result = new ArrayList<>();

        SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        String scrollId = searchResponse.getScrollId();
        SearchHit[] hits = searchResponse.getHits().getHits();
        scrollIdList.add(scrollId);

        try {
            while (ArrayUtils.isNotEmpty(hits)) {
                for (SearchHit hit : hits) {
                    result.add(fun.apply(hit));
                }
                if (hits.length < SIZE) {
                    break;
                }
                SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                searchScrollRequest.scroll(scroll);
                SearchResponse searchScrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
                scrollId = searchScrollResponse.getScrollId();
                hits = searchScrollResponse.getHits().getHits();
                scrollIdList.add(scrollId);
            }
        } finally {
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.setScrollIds(scrollIdList);
            restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        }
        return result;
    }

    /**
     * 根据证的sequenceNbr集合，批量查询证下面的所有设备
     *
     * @param enableType 启用停用/报废业务使用 - 过滤设备状态 （1：在用 2：停用） 对应枚举：EquimentEnum
     * @param sequenceNbrList 证的sequenceNbr集合
     * @param current         分页-当前页
     * @param size            分页-分页数
     * @return 查询结果
     */
    public Page<JSONObject> queryEquForPageByCertificateSeqList(String enableType, List<Long> sequenceNbrList, int current, int size) {
        List<JgUseRegistrationManage> jgUseRegistrationManageList = this.baseMapper.selectList(new LambdaQueryWrapper<JgUseRegistrationManage>()
                .in(JgUseRegistrationManage::getSequenceNbr, sequenceNbrList)
                .eq(JgUseRegistrationManage::getIsDelete, 0));
        if (ValidationUtil.isEmpty(jgUseRegistrationManageList)) {
            return new Page<>();
        }

        List<JSONObject> list = new LinkedList<>();
        long totle = 0;

        Page<JSONObject> result = new Page<>(Optional.of(current).orElse(1), Optional.of(size).orElse(10));
        Set<String> useOrgCodes = jgUseRegistrationManageList.stream().map(JgUseRegistrationManage::getUseRegistrationCode).collect(Collectors.toSet());

        // es中通过查询【使用登记证编号】所有设备
        SearchRequest request = new SearchRequest("idx_biz_view_jg_all");
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.trackTotalHits(true);


        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 且登记证筛选
        TermsQueryBuilder termsQuery = QueryBuilders.termsQuery("USE_ORG_CODE", useOrgCodes);
        boolQuery.must(termsQuery);
        // 且已认领状态
        boolQuery.must(QueryBuilders.termQuery("STATUS", EquipmentCategoryEnum.YRL.getName()));
        // 且已纳管状态
        boolQuery.must(QueryBuilders.termQuery("IS_INTO_MANAGEMENT", true));

        // 设备状态 对应枚举EquimentEnum
        if (!ObjectUtils.isEmpty(enableType)) {
            BoolQueryBuilder elcBuilder = QueryBuilders.boolQuery();
            if (EquimentEnum.BAOFEI.getCode().equals(Integer.valueOf(enableType))) { // 报废业务选择未报废的设备
                elcBuilder.mustNot(QueryBuilders.matchPhraseQuery("EQU_STATE", QueryParser.escape(enableType)));
            } else {
                elcBuilder.must(QueryBuilders.matchPhraseQuery("EQU_STATE", QueryParser.escape(enableType)));
            }
            boolQuery.must(elcBuilder);
        }

        builder.query(boolQuery);
        builder.from((current - 1) * size);
        builder.size(size);
        request.source(builder);

        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            for (SearchHit hit : response.getHits().getHits()) {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
                JSONObject dto = jsonObject.getJSONObject("sourceAsMap");
                dto.put("record", dto.get("SEQUENCE_NBR"));
                dto.put("equipAddress", dto.get("USE_PLACE") + "/" + dto.get("ADDRESS"));
                dto.put("EQU_STATE_NAME", !ValidationUtil.isEmpty(dto.getString("EQU_STATE")) ? EquimentEnum.getName.get(Integer.valueOf(dto.getString("EQU_STATE"))) : null);
                list.add(dto);
            }
            totle = Objects.requireNonNull(response.getInternalResponse().hits().getTotalHits()).value;
        } catch (Exception e) {
            e.printStackTrace();
        }
        result.setRecords(list);
        result.setTotal(totle);
        return result;
    }

    /**
     * 标志/使用登记证/汇总表 打印
     *
     * @param response       响应
     * @param printType      打印类型
     * @param certificateSeq 使用登记证的seq
     */
    public void printCertificate(HttpServletResponse response, String printType, String certificateSeq) {

        if (StringUtils.isEmpty(printType) || StringUtils.isEmpty(certificateSeq)) {
            throw new BadRequest("打印失败，请联系管理员！");
        }

        JgUseRegistrationManage manage = this.baseMapper.selectById(certificateSeq);
        String useRegistrationCode = manage.getUseRegistrationCode();
        String equCategoryCode = manage.getEquCategoryCode();
        switch (printType) {
            case "certificateNormalPrint":// 使用登记证 普打
            case "certificateNestedPrint":// 使用登记证 套打
            case "useFlagNormalPrint":// 使用标志 普打
            case "useFlagNestedPrint":// 使用标志 套打
                this.exportUseRegistrationCertificate(manage, response, getPrintTypeCode(printType));
                break;
            case "exportSummaryTable":// 工业管道和气瓶 汇总表下载
                List<JgUseRegistration> jgUseRegistrations = jgUseRegistrationService.getBaseMapper().selectList(new LambdaQueryWrapper<JgUseRegistration>()
                        .eq(JgUseRegistration::getUseRegistrationCode, useRegistrationCode)
                        .eq(JgUseRegistration::getStatus, "已完成"));
                List<Long> useRegistrationSeqs = jgUseRegistrations.stream().map(JgUseRegistration::getSequenceNbr).collect(Collectors.toList());
                jgUseRegistrationService.exportSummaryBasicInfo(useRegistrationSeqs, response, equCategoryCode);
                break;
            default:
                break;
        }
    }

    private String getPrintTypeCode(String printType) {
        switch (printType) {
            case "certificateNormalPrint":
                return "0";
            case "certificateNestedPrint":
                return "1";
            case "useFlagNormalPrint":
                return "2";
            case "useFlagNestedPrint":
                return "3";
            default:
                return "";
        }
    }

    public void exportUseRegistrationCertificate(JgUseRegistrationManage manage, HttpServletResponse response, String printType) {
        Map<String, Object> exportParamsMap = new HashMap<>();
        // 查询使用登记详情

        List<JSONObject> deviceList = this.queryEquByCertificateSeq(manage.getSequenceNbr());
        if (ValidationUtil.isEmpty(manage) || ValidationUtil.isEmpty(deviceList)) {
            throw new BadRequest("使用登记证导出失败,请稍后重试!");
        }
        // 登记机关
        if (ValidationUtil.isEmpty(manage.getReceiveOrgName())) {
            throw new BadRequest("使用登记证导出失败,登记机关为空!");
        }
        exportParamsMap.put("receiveOrgName", manage.getReceiveOrgName());
        // 使用登记证编号
        if (ValidationUtil.isEmpty(manage.getUseRegistrationCode())) {
            throw new BadRequest("使用登记证导出失败,使用登记证编号为空!");
        }
        exportParamsMap.put("useRegistrationCode", manage.getUseRegistrationCode());
        // 使用单位名称
        if (ValidationUtil.isEmpty(manage.getUseUnitName())) {
            throw new BadRequest("使用登记证导出失败,使用单位名称为空!");
        }
        exportParamsMap.put("useUnitName", manage.getUseUnitName());

        // 监管码
        if (ValidationUtil.isEmpty(deviceList.get(0))) {
            throw new BadRequest("使用登记证导出失败,监管码为空!");
        }
        exportParamsMap.put("supervisoryCode", deviceList.get(0).get("SUPERVISORY_CODE"));
        getAuditPassedDate(manage.getAuditPassDate(), exportParamsMap);
        exportParamsMap.put("equList", manage.getEquList());
        exportParamsMap.put("equCategory", manage.getEquCategory());
        exportParamsMap.put("equDefine", manage.getEquDefine());
        exportParamsMap.put("manageType", manage.getManageType());
        if (BusinessTypeEnum.JG_VEHICLE_GAS_APPLICATION.getName().equals(manage.getRegType())){
            exportParamsMap.put("fullAddress", manage.getUseUnitAddress());
            exportParamsMap.put("manageType", "");
            exportParamsMap.put("equDefineCode", "23T0");
            exportParamsMap.put("equCode", deviceList.stream()
                    .map(equ -> String.valueOf(equ.get("EQU_CODE")))
                    .filter(Objects::nonNull)
                    .collect(Collectors.joining(", ")));
            exportParamsMap.put("factoryNum", deviceList.stream()
                    .map(equ -> String.valueOf(equ.get("FACTORY_NUM")))
                    .filter(Objects::nonNull)
                    .collect(Collectors.joining(", ")));
            exportParamsMap.put("useInnerCode", deviceList.stream()
                    .map(equ -> String.valueOf(equ.get("USE_INNER_CODE")))
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.joining(", ")));
        }else{
            exportParamsMap.put("fullAddress", manage.getEquUseAddress());
            exportParamsMap.put("equCode", deviceList.get(0).get("EQU_CODE"));
            exportParamsMap.put("factoryNum", deviceList.get(0).get("FACTORY_NUM"));
            exportParamsMap.put("useInnerCode", deviceList.get(0).get("USE_INNER_CODE"));
            if ("unit".equals(manage.getManageType())) {
                exportParamsMap.put("fullAddress", manage.getUseUnitAddress());
            }
        }
        // 调整为证编号 全库唯一
        exportParamsMap.put("certificateNo", manage.getCertificateNo());
        // 与certificateNo一样，冗余字段防止漏调整
        exportParamsMap.put("applyNo", manage.getCertificateNo());
        exportParamsMap.put("version", manage.getVersion() + "");
        exportParamsMap.put("carNumber", manage.getCarNumber());
        exportParamsMap.put("equListCode", manage.getEquListCode());

        if ("0".equals(printType)) {
            // 调用生成使用登记证
            commonService.generateCertificateReport(exportParamsMap, response);
        } else if ("1".equals(printType)) {
            // 套打
            commonService.generateCertificateReportDoc(exportParamsMap, response);
        } else if ("2".equals(printType)) {
            // 使用标志普通打印
            commonService.useFlagGenerate(this.buildUseFlagParamDto(deviceList, manage, exportParamsMap), response);
        } else if ("3".equals(printType)) {
            // 使用标志套打
            commonService.fightUseFlagGenerate(this.buildUseFlagParamDto(deviceList, manage, exportParamsMap), response);
        }
    }


    private UseFlagParamDto buildUseFlagParamDto(List<JSONObject> deviceList, JgUseRegistrationManage manage, Map<String, Object> exportParamsMap) {
        UseFlagParamDto useFlagParamDto = new UseFlagParamDto();
        useFlagParamDto.setReceiveCompanyCode(manage.getReceiveCompanyCode());
        useFlagParamDto.setEquList(exportParamsMap.get("equList").toString());
        if (BusinessTypeEnum.JG_VEHICLE_GAS_APPLICATION.getName().equals(manage.getRegType())){
            useFlagParamDto.setEquListCode("0000");
            useFlagParamDto.setEquipDefine("车用气瓶");
        }else{
            useFlagParamDto.setEquListCode(manage.getEquListCode());
            useFlagParamDto.setEquipDefine(Optional.ofNullable(exportParamsMap.get("equDefine"))
                    .orElse(exportParamsMap.get("equCategory").toString())
                    .toString());
        }
        useFlagParamDto.setEquipCode(exportParamsMap.get("equCode").toString());
        useFlagParamDto.setEquipCategory(manage.getEquCategory());
        useFlagParamDto.setUseUnitName(exportParamsMap.get("useUnitName").toString());
        useFlagParamDto.setUseInnerCode(exportParamsMap.get("useInnerCode").toString());
        useFlagParamDto.setReceiveOrgName(manage.getReceiveOrgName());
        useFlagParamDto.setUseRegistrationCode(exportParamsMap.get("useRegistrationCode").toString());
        useFlagParamDto.setFactoryNumber(exportParamsMap.get("factoryNum").toString());
        useFlagParamDto.setCarNumber(manage.getCarNumber());
        useFlagParamDto.setCertificateNo(manage.getCertificateNo());
        useFlagParamDto.setVersion(manage.getVersion());
        // 检验信息字段设置
        setInspectField((String) deviceList.get(0).get("SEQUENCE_NBR"), useFlagParamDto);
        // 电梯维保信息字段设置
        setMainInfoField((String) deviceList.get(0).get("SEQUENCE_NBR"), useFlagParamDto);
        // 车用气瓶专用字段
        useFlagParamDto.setCylinderNumOrVolume(manage.getGasNum() + "/" + manage.getVolume());
        useFlagParamDto.setFillingMedium(manage.getFillingMedium());
        return useFlagParamDto;
    }

    private void setInspectField(String record, UseFlagParamDto useFlagParamDto) {
        Map<String, Object> inspectDetail = jgUseRegistrationMapper.getInspectDetail(record, null);
        useFlagParamDto.setInspectionUnitName(inspectDetail.get("inspectOrgName") == null ? "" : inspectDetail.get("inspectOrgName").toString());
        useFlagParamDto.setNextInspectionDate(inspectDetail.get("nextInspectDate") == null ? null : (Date) inspectDetail.get("nextInspectDate"));
    }

    private void setMainInfoField(String record, UseFlagParamDto useFlagParamDto) {
        IdxBizJgMaintenanceRecordInfo idxBizJgMaintenanceRecordInfo = idxBizJgMaintenanceRecordInfoService.queryNewestDetailByRecord(record);

        useFlagParamDto.setEmergencyTel(this.buildEmergencyTel(idxBizJgMaintenanceRecordInfo));
        useFlagParamDto.setMaintenanceUnitName(idxBizJgMaintenanceRecordInfo.getMeUnitName() == null ? "" : idxBizJgMaintenanceRecordInfo.getMeUnitName());
    }

    private String buildEmergencyTel(IdxBizJgMaintenanceRecordInfo idxBizJgMaintenanceRecordInfo) {
        if (StringUtil.isNotEmpty(idxBizJgMaintenanceRecordInfo.getMeMasterPhone()) && StringUtil.isNotEmpty(idxBizJgMaintenanceRecordInfo.getMeMaster1Phone())) {
            return idxBizJgMaintenanceRecordInfo.getMeMasterPhone() + "/" + idxBizJgMaintenanceRecordInfo.getMeMaster1Phone();
        }
        return StringUtil.isNotEmpty(idxBizJgMaintenanceRecordInfo.getMeMasterPhone()) ? idxBizJgMaintenanceRecordInfo.getMeMasterPhone() : StringUtil.isNotEmpty(idxBizJgMaintenanceRecordInfo.getMeMaster1Phone()) ? idxBizJgMaintenanceRecordInfo.getMeMaster1Phone() : "";
    }

    /**
     * 根据sequenceNbr查询---大屏使用
     *
     * @param sequenceNbr 主键
     * @return
     */
    public Object getDetail(String sequenceNbr) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        List<FormValue> jsonData = getJsonData(registrationBasicJson);
        HashMap<String, Object> result = new HashMap<>();
        // 基本信息
        JgUseRegistrationManage jgUseRegistrationManage = this.baseMapper.selectById(sequenceNbr);
        Map<String, Object> objectMap = Bean.BeantoMap(jgUseRegistrationManage);
        jsonData.forEach(f -> {
            Object o = objectMap.get(f.getKey());
            if (!ObjectUtils.isEmpty(o)) {
                f.setValue(o.toString());
                if ("auditPassDate".equals(f.getKey())){
                    f.setValue(simpleDateFormat.format(jgUseRegistrationManage.getAuditPassDate()));
                }
            }
        });
        // 流水信息
        List<JgCertificateChangeRecord> changeRecordList = jgCertificateChangeRecordService.list(new LambdaQueryWrapper<JgCertificateChangeRecord>()
                .eq(JgCertificateChangeRecord::getUseRegistrationCode, jgUseRegistrationManage.getUseRegistrationCode())
                .or().eq(JgCertificateChangeRecord::getCertificateNo, jgUseRegistrationManage.getCertificateNo())
                .orderByDesc(JgCertificateChangeRecord::getCreateDate));
        List<Map<String, String>> collect = changeRecordList.stream()
                .map(x -> {
                    Map<String, String> map = new HashMap<>();
                    map.put("operatingTime", simpleDateFormat.format(x.getRecDate()));
                    map.put("operater", x.getChangeContent());
                    map.put("label", simpleDateFormat.format(x.getRecDate()));
                    return map;
                }).collect(Collectors.toList());
        HashMap<String, Object> map = new HashMap<>();
        map.put("datas", collect);
        map.put("title", "使用登记证流水");
        map.put("renderType", "timeline");
        result.put("title", objectMap.get("useUnitName"));
        result.put("keyParams", jsonData);
        result.put("infoRecords", map);
        return result;
    }


    private List<FormValue> getJsonData(Resource resource) {
        String json;
        try {
            json = IOUtils.toString(resource.getInputStream(), String.valueOf(StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return parseArray(json, FormValue.class);
    }

    public void initUseRegistrationCodeIntoRedis(String year) {
        // 获取当前年份的两位数
        String currentYearSuffix = String.valueOf(LocalDate.now().getYear() % 100);
        Date endOfYear = com.yeejoin.amos.boot.module.ymt.api.common.DateUtils.calculateEndOfYear(new Date());
        // 非车用气瓶-从数据库中加载使用登记证最大序列码刷新到Redis中
        jgUseRegistrationManageMapper.initUseRegistrationCodeIntoRedis(year)
                .forEach(resultMap ->
                        Optional.ofNullable(resultMap)
                                .ifPresent(map -> setValueWithoutExpiration(map.get("prefix"), map.get("code")))
                );
        // 车用气瓶-从数据库中加载使用登记证最大序列码刷新到Redis中
        jgUseRegistrationManageMapper.initVehicleUseRegCodeIntoRedis(year)
                .forEach(resultMap ->
                        Optional.ofNullable(resultMap)
                                .ifPresent(map -> setValueWithoutExpiration(map.get("prefix"), map.get("code")))
                );
    }

    /**
     * redis 设置不过期的 key
     *
     * @param key   key
     * @param value value
     */
    public void setValueWithoutExpiration(String key, String value) {
        ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
        valueOps.set(key, value);
    }

    /**
     * redis设置key
     *
     * @param key            key
     * @param value          value
     * @param expirationDate 过期时间
     */
    public void setValueWithExpiration(String key, String value, Date expirationDate) {
        ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
        valueOps.set(key, value);

        long expirationTimeInSeconds = expirationDate.getTime() - System.currentTimeMillis();
        redisTemplate.expire(key, expirationTimeInSeconds, TimeUnit.MILLISECONDS);
    }

    public Object handleErrorDeviceCode(String applyNo) {
        LambdaQueryWrapper<JgUseRegistrationManage> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(JgUseRegistrationManage::getApplyNo, applyNo);
        JgUseRegistrationManage jgUseRegistrationManage = jgUseRegistrationManageMapper.selectOne(queryWrapper);
        if (jgUseRegistrationManage != null) {
            String ym = "";
            try {
                ym = Optional.of(DateUtils.dateFormat(jgUseRegistrationManage.getAuditPassDate(), DateUtils.DATE_PATTERN_MM)).orElse(DateUtils.dateFormat(new Date(), DateUtils.DATE_PATTERN_MM));
            } catch (ParseException e) {
                log.error("日期转换失败：", e);
            }
            ResponseModel<String> responseModel;
            if (isValidCreditCode(jgUseRegistrationManage.getReceiveCompanyCode())) {
                String equCode = Optional.ofNullable(jgUseRegistrationManage.getEquDefineCode()).orElse(jgUseRegistrationManage.getEquCategoryCode());
                String registrationCode = equCode + jgUseRegistrationManage.getReceiveCompanyCode() + ym;
                responseModel = tzsServiceFeignClient.deviceRegistrationCode(registrationCode);
                jgUseRegistrationManage.setCertificateNo(responseModel.getResult());
                jgUseRegistrationManageMapper.updateById(jgUseRegistrationManage);

                LambdaUpdateWrapper<JgCertificateChangeRecord> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(JgCertificateChangeRecord::getApplyNo, applyNo);
                updateWrapper.set(JgCertificateChangeRecord::getCertificateNo, responseModel.getResult());
                certificateChangeRecordMapper.update(null, updateWrapper);
            }
        }
        return "修复成功！";
    }

    /**
     * 工程/装置分页查询
     * @param page
     * @param params
     * @param sort
     * @return
     */
    public Page<IdxBizJgProjectContraption> queryForUnitByProjectContraptionPage(Page<IdxBizJgProjectContraption> page, Map<String, String> params, String sort) {
        SortVo sortMap = commonServiceImpl.sortFieldConversion(sort);
        Page<IdxBizJgProjectContraption> projectContraptionPage = this.baseMapper.queryForUnitByProjectContraptionPage(page, params, sortMap);
        List<IdxBizJgProjectContraption> records = projectContraptionPage.getRecords();
        if (!ValidationUtil.isEmpty(records)) {
            records.forEach(item -> {
                BigDecimal pipelineLength = BigDecimal.valueOf(item.getPipelineLength());
                BigDecimal roundedValue = pipelineLength.setScale(3, RoundingMode.HALF_UP);
                BigDecimal strippedValue = roundedValue.stripTrailingZeros();
                item.setPipelineLength(Double.valueOf(strippedValue.toPlainString()));
                item.setDataSourceName(EquipSourceEnum.getDataSourceName(item.getDataSource()));
                item.setFullAddress(
                        Stream.of(item.getProvinceName(), item.getCityName(), item.getCountyName(), item.getStreetName(), item.getAddress())
                                .map(value -> value == null ? "" : value)
                                .collect(Collectors.joining())
                );
            });
        }
        return projectContraptionPage;
    }
}