package com.yeejoin.amos.boot.module.cylinder.flc.biz.service.impl;

import cn.hutool.json.ObjectMapper;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.module.cylinder.api.entity.ESCylinderFillingRecordDto;
import com.yeejoin.amos.boot.module.cylinder.biz.dao.ESCylinderFillingRecordRepository;
import com.yeejoin.amos.boot.module.cylinder.flc.api.dto.CylinderFillingRecordDto;
import com.yeejoin.amos.boot.module.cylinder.flc.api.entity.CylinderFillingRecord;
import com.yeejoin.amos.boot.module.cylinder.flc.api.mapper.CylinderFillingRecordMapper;
import com.yeejoin.amos.boot.module.cylinder.flc.api.service.ICylinderFillingRecordService;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * 液化气体气瓶充装信息-充装记录服务实现类
 *
 * @author system_generator
 * @date 2022-03-04
 */
@Service
@Slf4j
public class CylinderFillingRecordServiceImpl extends BaseService<CylinderFillingRecordDto, CylinderFillingRecord, CylinderFillingRecordMapper> implements ICylinderFillingRecordService {

    @Autowired
    ESCylinderFillingRecordRepository esCylinderFillingRecordRepository;

    @Autowired
    CylinderFillingRecordMapper cylinderFillingRecordMapper;

    @Autowired
    RestHighLevelClient restHighLevelClient;

    /**
     * 分页查询
     */
    public Page<CylinderFillingRecordDto> queryForCylinderFillingRecordPage(Page<CylinderFillingRecordDto> page) {
        return this.queryForPage(page, null, false);
    }

    /**
     * 列表查询 示例
     */
    public List<CylinderFillingRecordDto> queryForCylinderFillingRecordList() {
        return this.queryForList("", false);
    }

    public Double queryIntegirtyByAppId(String appId) {
        return this.baseMapper.queryIntegirtyByAppId(appId);
    }

    public Double getFillingSum(String r, Date time) {
        return this.baseMapper.getFillingSum(r, time);
    }

    public Page<CylinderFillingRecordDto> queryListByQueryDto(Page<CylinderFillingRecordDto> page, CylinderFillingRecordDto cylinderFillingRecordDto, String sortParam, String sortRule) {
        Page<List<CylinderFillingRecordDto>> list = this.baseMapper.queryListByQueryDto(
                page,
//                cylinderFillingRecordDto.getFillingUnitName(),
//                cylinderFillingRecordDto.getPropertyUnit(),
//                cylinderFillingRecordDto.getFactoryNum(),
//                cylinderFillingRecordDto.getCylinderVariety(),
//                cylinderFillingRecordDto.getQrCode(),
//                cylinderFillingRecordDto.getElectronicLabelCode(),
//                cylinderFillingRecordDto.getSequenceCode(),
//                cylinderFillingRecordDto.getUnitInnerCode(),
                cylinderFillingRecordDto.getFillingStarttime(),
                cylinderFillingRecordDto.getFillingEndtime(),
                sortParam, sortRule, cylinderFillingRecordDto.getAppId()
        );
        Page<CylinderFillingRecordDto> page1 = new Page<>();
        List<CylinderFillingRecordDto> resultDtoList = JSONArray.parseArray(JSONArray.toJSONString(list.getRecords()), CylinderFillingRecordDto.class);
        page1.setCurrent(page.getCurrent());
        page1.setSize(page.getSize());
        page1.setTotal(list.getTotal());
        page1.setRecords(resultDtoList);
        return page1;
    }

    public Double getFillingSumByMonth(String appId, Date time) {
        return baseMapper.getFillingSumByMonth(appId, time);
    }

    public Integer getFillingCountByMonth(String appId, Date time) {
        return baseMapper.getFillingCountByMonth(appId, time);
    }

    public Double getFillingSumByDate(String appId, Date time) {
        return baseMapper.getFillingSumByDate(appId, time);
    }


    @Scheduled(cron = "${tzs.cylinder.fill.cron}")
    @SchedulerLock(name = "cylinderFillingRecord2ESTask", lockAtMostFor = "PT6H")
    public void setTimeSaveCylinderInfoToES() {
        Integer count = cylinderFillingRecordMapper.getCylinderFillingRecordTotal();
        Integer times = 0;
        if (count != 0) {
            times = count / 5000;
            int last = count % 5000;
            if (last > 0) {
                times++;
            }
        } else {
            return;
        }
        for (int i = 0; i <= times; i++) {
            List<ESCylinderFillingRecordDto> cylinderFillingRecord = cylinderFillingRecordMapper.getCylinderFillingRecord();
            if (!ObjectUtils.isEmpty(cylinderFillingRecord)) {
                List<String> appIds = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getAppId).collect(Collectors.toList());
                List<String> sequenceCodeS = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getSequenceCode).collect(Collectors.toList());
                List<ESCylinderFillingRecordDto> cylinderFillingRecordInfo = cylinderFillingRecordMapper.getCylinderFillingRecordInfo(appIds, sequenceCodeS);
                cylinderFillingRecord.stream().map(item -> {
                    List<ESCylinderFillingRecordDto> collect = cylinderFillingRecordInfo.stream().filter(e -> item.getAppIdAndSequenceCode().equals(e.getAppIdAndSequenceCode())).collect(Collectors.toList());
                    if (!ObjectUtils.isEmpty(collect)) {
                        item.setUnitName(collect.get(0).getUnitName());
                        item.setFactoryNum(collect.get(0).getFactoryNum());
                        item.setCylinderVariety(collect.get(0).getCylinderVariety());
                        item.setCylinderVarietyName(collect.get(0).getCylinderVarietyName());
                        item.setUnitInnerCode(collect.get(0).getUnitInnerCode());
                        item.setSequenceCode(collect.get(0).getSequenceCode());
                        item.setQrCode(collect.get(0).getQrCode());
                        item.setElectronicLabelCode(collect.get(0).getElectronicLabelCode());
                        item.setAppId(collect.get(0).getAppId());
                        item.setCreditCode(collect.get(0).getCreditCode());
                        item.setRegionCode(collect.get(0).getRegionCode());
                        try {
                            item.setInspectionDateMs(ObjectUtils.isEmpty(item.getFillingStartTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingStartTime()).getTime());
                            item.setInspectionDateAfterMS(ObjectUtils.isEmpty(item.getFillingEndTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingEndTime()).getTime());
                        } catch (ParseException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    return item;
                }).collect(Collectors.toList());
                saveCylinderFillingRecord2ES(cylinderFillingRecord);
            }
        }
    }

    @Override
    public List<ESCylinderFillingRecordDto> getCylinderFillingRecordTest() {
        List<ESCylinderFillingRecordDto> cylinderFillingRecord = cylinderFillingRecordMapper.getCylinderFillingRecord();
        if (!ObjectUtils.isEmpty(cylinderFillingRecord)) {
            List<String> appIds = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getAppId).collect(Collectors.toList());
            List<String> sequenceCodeS = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getSequenceCode).collect(Collectors.toList());
            List<ESCylinderFillingRecordDto> cylinderFillingRecordInfo = cylinderFillingRecordMapper.getCylinderFillingRecordInfo(appIds, sequenceCodeS);
//            List<ESCylinderFillingRecordDto> cylinderFillingRecordInfo = cylinderFillingRecordMapper.getCylinderFillingRecordInfo(appIdAndSequenceCodes);
            cylinderFillingRecord.stream().map(item -> {
                List<ESCylinderFillingRecordDto> collect = cylinderFillingRecordInfo.stream().filter(e -> item.getAppIdAndSequenceCode().equals(e.getAppIdAndSequenceCode())).collect(Collectors.toList());
                if (!ObjectUtils.isEmpty(collect)) {
                    item.setUnitName(collect.get(0).getUnitName());
                    item.setFactoryNum(collect.get(0).getFactoryNum());
                    item.setCylinderVariety(collect.get(0).getCylinderVariety());
                    item.setCylinderVarietyName(collect.get(0).getCylinderVarietyName());
                    item.setUnitInnerCode(collect.get(0).getUnitInnerCode());
                    item.setSequenceCode(collect.get(0).getSequenceCode());
                    item.setQrCode(collect.get(0).getQrCode());
                    item.setElectronicLabelCode(collect.get(0).getElectronicLabelCode());
                    item.setAppId(collect.get(0).getAppId());
                    item.setCreditCode(collect.get(0).getCreditCode());
                    item.setRegionCode(collect.get(0).getRegionCode());
                    try {
                        item.setInspectionDateMs(ObjectUtils.isEmpty(item.getFillingStartTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingStartTime()).getTime());
                        item.setInspectionDateAfterMS(ObjectUtils.isEmpty(item.getFillingEndTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingEndTime()).getTime());
                    } catch (ParseException e) {
                        throw new RuntimeException(e);
                    }
                }
                return item;
            }).collect(Collectors.toList());
            saveCylinderFillingRecord2ES(cylinderFillingRecord);
        }
        return cylinderFillingRecord;
    }


    @Override
    public List<ESCylinderFillingRecordDto> getCylinderFillingRecordAll() {
        List<ESCylinderFillingRecordDto> cylinderFillingRecord = new ArrayList<>();
        Integer count = cylinderFillingRecordMapper.getCylinderFillingRecordTotal();
        Integer times = 0;
        if (count != 0) {
            times = count / 5000;
            int last = count % 5000;
            if (last > 0) {
                times++;
            }
        } else {
            return cylinderFillingRecord;
        }
        for (int i = 0; i <= times; i++) {
            cylinderFillingRecord = cylinderFillingRecordMapper.getCylinderFillingRecord();
            if (!ObjectUtils.isEmpty(cylinderFillingRecord)) {
                List<String> appIds = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getAppId).collect(Collectors.toList());
                List<String> sequenceCodeS = cylinderFillingRecord.stream().map(ESCylinderFillingRecordDto::getSequenceCode).collect(Collectors.toList());
                List<ESCylinderFillingRecordDto> cylinderFillingRecordInfo = cylinderFillingRecordMapper.getCylinderFillingRecordInfo(appIds, sequenceCodeS);
                cylinderFillingRecord.stream().map(item -> {
                    List<ESCylinderFillingRecordDto> collect = cylinderFillingRecordInfo.stream().filter(e -> e.getAppIdAndSequenceCode().equals(item.getAppIdAndSequenceCode())).collect(Collectors.toList());
                    if (!ObjectUtils.isEmpty(collect)) {
                        item.setUnitName(collect.get(0).getUnitName());
                        item.setFactoryNum(collect.get(0).getFactoryNum());
                        item.setCylinderVariety(collect.get(0).getCylinderVariety());
                        item.setCylinderVarietyName(collect.get(0).getCylinderVarietyName());
                        item.setUnitInnerCode(collect.get(0).getUnitInnerCode());
                        item.setSequenceCode(collect.get(0).getSequenceCode());
                        item.setQrCode(collect.get(0).getQrCode());
                        item.setElectronicLabelCode(collect.get(0).getElectronicLabelCode());
                        item.setAppId(collect.get(0).getAppId());
                        item.setCreditCode(collect.get(0).getCreditCode());
                        item.setRegionCode(collect.get(0).getRegionCode());
                        try {
                            item.setInspectionDateMs(ObjectUtils.isEmpty(item.getFillingStartTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingStartTime()).getTime());
                            item.setInspectionDateAfterMS(ObjectUtils.isEmpty(item.getFillingEndTime()) ? 0L : DateUtils.dateParseWithPattern(item.getFillingEndTime()).getTime());
                        } catch (ParseException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    return item;
                }).collect(Collectors.toList());
                saveCylinderFillingRecord2ES(cylinderFillingRecord);
            }
        }
        return cylinderFillingRecord;
    }

    @Override
    public Page<ESCylinderFillingRecordDto> queryByKeys(ESCylinderFillingRecordDto esCylinderFillingRecordDto, int pageNum, int pageSize) {

        Page<ESCylinderFillingRecordDto> result = new Page<ESCylinderFillingRecordDto>(pageNum, pageSize);
        SearchRequest request = new SearchRequest();
        request.indices("cylinder_filling");

        //通用匹配规则，条件构建
        boolean flag = true;

        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();

        //匹配统一信用代码
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getCreditCode())) {
            flag = false;
            BoolQueryBuilder meBuilder = QueryBuilders.boolQuery();
            meBuilder.must(QueryBuilders.matchQuery("creditCode", esCylinderFillingRecordDto.getCreditCode()));
            boolMust.must(meBuilder);
        }

        //匹配行政区划
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getRegionCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("regionCode", "*" + esCylinderFillingRecordDto.getRegionCode() + "*"));
            boolMust.must(query);
        }

        //匹配充装单位
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getFillingUnitName())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("fillingUnitName", "*" + esCylinderFillingRecordDto.getFillingUnitName() + "*"));
            boolMust.must(query);
        }

        //匹配产权单位
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getUnitName())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("unitName", "*" + esCylinderFillingRecordDto.getUnitName() + "*"));
            boolMust.must(query);
        }

        //匹配出厂编号
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getFactoryNum())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("factoryNum", "*" + esCylinderFillingRecordDto.getFactoryNum() + "*"));
            boolMust.must(query);
        }

        //匹配气瓶品种
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getCylinderVariety())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("cylinderVariety", esCylinderFillingRecordDto.getCylinderVariety()));
            boolMust.must(query);
        }

        //匹配二维码编码
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getQrCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("qrCode", "*" + esCylinderFillingRecordDto.getQrCode() + "*"));
            boolMust.must(query);
        }

        //匹配电子标签
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getElectronicLabelCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("electronicLabelCode", "*" + esCylinderFillingRecordDto.getElectronicLabelCode() + "*"));
            boolMust.must(query);
        }

        //匹配气瓶唯一标识
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getSequenceCode())) {
            flag = false;
            BoolQueryBuilder sequenceCodeBuilder = QueryBuilders.boolQuery();
            sequenceCodeBuilder.must(QueryBuilders.matchQuery("sequenceCode", esCylinderFillingRecordDto.getSequenceCode()));
            boolMust.must(sequenceCodeBuilder);
        }

        //匹配单位内部编号
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getUnitInnerCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("unitInnerCode", "*" + esCylinderFillingRecordDto.getUnitInnerCode() + "*"));
            boolMust.must(query);
        }

        SimpleDateFormat sdf = new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN);
        //充装开始时间
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getFillingStartTime())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                String fillingStartTime = esCylinderFillingRecordDto.getFillingStartTime() + " 00:00:00";
                query.must(QueryBuilders.rangeQuery("inspectionDateMs").gte(sdf.parse(fillingStartTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }

        //充装结束时间
        if (!ObjectUtils.isEmpty(esCylinderFillingRecordDto.getFillingEndTime())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                String fillingEndTime = esCylinderFillingRecordDto.getFillingEndTime() + " 23:59:59";
                query.must(QueryBuilders.rangeQuery("inspectionDateAfterMS").lte(sdf.parse(fillingEndTime).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }

        if (flag) { // 搜索全部
            boolMust.must(QueryBuilders.matchAllQuery());
        }

        builder.query(boolMust);
        builder.sort("inspectionDateMs",SortOrder.DESC);
        builder.sort("sequenceNbr",SortOrder.DESC);
        builder.from((pageNum - 1) * pageSize);
        builder.size(pageSize);
        builder.trackTotalHits(true);
        request.source(builder);
        List<ESCylinderFillingRecordDto> list = new LinkedList<>();
        long totle = 0;
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            for (org.elasticsearch.search.SearchHit hit : response.getHits()) {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
                ESCylinderFillingRecordDto esCylinderFillingRecordDto1 = JSONObject.toJavaObject(jsonObject.getJSONObject("sourceAsMap"), ESCylinderFillingRecordDto.class);
                list.add(esCylinderFillingRecordDto1);
            }
            totle = response.getInternalResponse().hits().getTotalHits().value;
            result.setRecords(list);
            result.setTotal(totle);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return result;
    }

    @Override
    public void saveCylinderFillingRecord2ES(List<ESCylinderFillingRecordDto> records) {
        List<String> ids = new ArrayList<>();
        for (ESCylinderFillingRecordDto record : records) {
            CylinderFillingRecord cylinderFillingRecord = new CylinderFillingRecord();
            BeanUtils.copyProperties(record, cylinderFillingRecord);
            ids.add(String.valueOf(record.getSequenceNbr()));
        }
        esCylinderFillingRecordRepository.saveAll(records);
        cylinderFillingRecordMapper.updateCylinderFillingToEsStatus(ids);
    }

    @Override
    public Integer getCylinderFillingRecordTotal() {
        return cylinderFillingRecordMapper.getCylinderFillingRecordTotal();
    }

    @Override
    public ESCylinderFillingRecordDto saveCylinderFillingRecordToES(ESCylinderFillingRecordDto ci) {

        ESCylinderFillingRecordDto recordDto = esCylinderFillingRecordRepository.save(ci);
        if (!ObjectUtils.isEmpty(recordDto)) {
            //同步到es后修改
            CylinderFillingRecord cylinderFillingRecord = new CylinderFillingRecord();
            cylinderFillingRecord.setIsNotEs("1");
            baseMapper.update(cylinderFillingRecord, new QueryWrapper<CylinderFillingRecord>().eq("sequence_nbr", ci.getSequenceNbr()));
        }

        return recordDto;
    }
}