package com.zcxk.xxljob.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.PageUtil;
import com.alibaba.fastjson.JSON;
import com.xxl.job.core.log.XxlJobLogger;
import com.zcxk.core.common.enums.StatusEnum;
import com.zcxk.core.common.util.SnowflakeIdWorker;
import com.zcxk.core.utils.DateUtil;
import com.zcxk.rmcp.api.dto.meterreadrecord.MeterReadRecordDetailDto;
import com.zcxk.rmcp.api.dto.readmeter.WaterMeterDto;
import com.zcxk.rmcp.api.enums.ReadStatusEnum;
import com.zcxk.rmcp.core.dao.DeviceMapper;
import com.zcxk.rmcp.core.dao.MeterReadRecordMapper;
import com.zcxk.rmcp.core.entity.Device;
import com.zcxk.rmcp.core.mongo.MeterReadRecord;
import com.zcxk.xxljob.common.Constants;
import com.zcxk.xxljob.service.MeterReadRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author linqingwei
 * @date 2021-08-11 11:36
 */
@Slf4j
@Service
public class MeterReadRecordServiceImpl implements MeterReadRecordService {

    @Resource
    private DeviceMapper deviceMapper;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SnowflakeIdWorker idWorker;
    @Resource
    private MeterReadRecordMapper meterReadRecordMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void executeCreateMeterUnReadRecord(Map<String, Object> map, int index, int total) {
        log.info("begin executeCreateMeterUnReadRecord map = {} index={} total={}",JSON.toJSONString(map),index,total);
        int readDay = MapUtil.getInt(map, "readDay");
        //计算分页
        int deviceTotal = deviceMapper.countWaterMeter();
        int pageSize = PageUtil.totalPage(deviceTotal, total);
        int startIndex = index * pageSize;

        List<WaterMeterDto> waterMeterList = deviceMapper.findWaterMeterListWithPage(startIndex, pageSize);

        XxlJobLogger.log("waterMeterList size = {}",waterMeterList.size());
        batchCreateMeterUnReadRecord(waterMeterList,readDay);
        log.info("end executeCreateMeterUnReadRecord");
    }

    private void batchCreateMeterUnReadRecord(List<WaterMeterDto> waterMeterList, int readDay) {
        if(waterMeterList.isEmpty())return;

        //更新抄表记录
        updateReadingData(waterMeterList,readDay);

        //保存未抄记录
        saveUnReadingData(readDay);
    }

    /**
     * @description 更新抄表数据
     * @param waterMeterList
     * @return
     * @author linqingwei
     **/
    private void updateReadingData(List<WaterMeterDto> waterMeterList,int readDay) {



        Map<String,WaterMeterDto> lastMap = redisTemplate.opsForHash().entries(Constants.PREFIX_DEVICE_CACHE_FLAG);

        List<MeterReadRecord> meterReadRecordList = new ArrayList<>();

        for (WaterMeterDto waterMeter : waterMeterList) {
            WaterMeterDto last = lastMap.get(waterMeter.getId().toString());
            if (last != null) {
                //判断当前和最后缓存设备信息，不同则更新
                if(CompareUtil.compare(waterMeter, last) == -1){
                    log.info("begin updateReadingData compare waterMeter = {} last = {} {}",JSON.toJSONString(waterMeter),JSON.toJSONString(last),readDay);
                    Query query = new Query();
                    query.addCriteria(Criteria.where("deviceId").is(waterMeter.getId()));
                    Update update = new Update();
                    if(waterMeter.getTenantId()!=null)update.set("tenantId",waterMeter.getTenantId());
                    if(waterMeter.getCategoryId()!=null)update.set("categoryId",waterMeter.getCategoryId());
                    if(waterMeter.getCompanyOrgId()!=null)update.set("companyOrgId",waterMeter.getCompanyOrgId());
                    if(waterMeter.getCompanyName()!=null)update.set("companyOrgName",waterMeter.getCompanyName());
                    if(waterMeter.getDeptOrgId()!=null)update.set("deptOrgId",waterMeter.getDeptOrgId());
                    if(waterMeter.getDeptName()!=null)update.set("deptOrgName",waterMeter.getDeptName());
                    if(waterMeter.getCommunityId()!=null)update.set("communityId",waterMeter.getCommunityId());
                    if(waterMeter.getCommunityName()!=null)update.set("communityName",waterMeter.getCommunityName());
                    if(waterMeter.getDeviceNo()!=null)update.set("deviceNo",waterMeter.getDeviceNo());
                    if(waterMeter.getAddress()!=null)update.set("location",waterMeter.getAddress());
                    if(waterMeter.getProductName()!=null)update.set("deviceModel",waterMeter.getManufacturerName()+"/"+waterMeter.getProductName()+"/"+waterMeter.getProductModel());
                    if(waterMeter.getMeterNo()!=null)update.set("meterNo",waterMeter.getMeterNo());
                    if(waterMeter.getFileNo()!=null)update.set("fileNo",waterMeter.getFileNo());
                    long updateResult = mongoTemplate.updateMulti(query,update, Constants.METER_READ_RECORD_TABLE).getModifiedCount();
                }

            }else {
                //无缓存则插入抄表记录
                meterReadRecordList.add(buildMeterReadRecord(waterMeter));
            }


        }
        XxlJobLogger.log("meterReadRecordList size = {},lastMap size = {}",meterReadRecordList.size() , lastMap.size());
        if(!meterReadRecordList.isEmpty()){
            mongoTemplate.insert(meterReadRecordList,Constants.METER_READ_RECORD_TABLE);
        }

        //保存最新信息到缓存中
        Map<String,WaterMeterDto> map = new HashMap<>();
        for (WaterMeterDto waterMeter : waterMeterList) {
            map.put(waterMeter.getId().toString(),waterMeter);
        }
        redisTemplate.opsForHash().putAll(Constants.PREFIX_DEVICE_CACHE_FLAG,map);


    }

    private void saveUnReadingData(int readDay) {
        log.info("begin saveReadingData ,readDay = {}" , readDay);
        MeterReadRecord.MeterReadInfo meterReadInfo =  buildMeterReadInfo(readDay);


        //推送数据
        Query pushQuery = new Query();
        //pushQuery.addCriteria(Criteria.where("deviceId").is(waterMeter.getId()));
        Update push = new Update();
        push.push("data",meterReadInfo);
        long pushResult = mongoTemplate.updateMulti(pushQuery,push, Constants.METER_READ_RECORD_TABLE).getModifiedCount();
        log.info("end saveReadingData pushResult = {}",pushResult);
    }

    private MeterReadRecord.MeterReadInfo buildMeterReadInfo(int readDay) {


        MeterReadRecord.MeterReadInfo readInfo = new MeterReadRecord.MeterReadInfo();
        readInfo.setReadDate(readDay);
        //readInfo.setReadTime(meterData.getReceiveDate());
        readInfo.setReadStatus(ReadStatusEnum.UN_READ.getCode());
        //readInfo.setValveStatus(meterData.getValveStatus());
        //readInfo.setReadData(meterData.getReadData());
        //readInfo.setLastValid(lastMeterData.getReadData());
        readInfo.setLastCost(0d);//用水量
        return readInfo;
    }

    private MeterReadRecord buildMeterReadRecord(WaterMeterDto waterMeter) {

        MeterReadRecord readRecord = new MeterReadRecord();
        readRecord.setId(idWorker.nextId());
        readRecord.setYear(DateUtil.getYear(new Date()));
        readRecord.setTenantId(waterMeter.getTenantId());
        readRecord.setCategoryId(waterMeter.getCategoryId());
        readRecord.setCompanyOrgId(waterMeter.getCompanyOrgId());
        readRecord.setCompanyOrgName(waterMeter.getCompanyName());
        readRecord.setDeptOrgId(waterMeter.getDeptOrgId());
        readRecord.setDeptOrgName(waterMeter.getDeptName());
        readRecord.setCommunityId(waterMeter.getCommunityId());
        readRecord.setCommunityName(waterMeter.getCommunityName());
        //readRecord.setConcentratorId(0);
        //readRecord.setConcentratorCode("");
        //readRecord.setCollectorId(0);
        //readRecord.setCollectorCode("");
        //readRecord.setChannelNumber("");
        readRecord.setDeviceId(waterMeter.getId());
        readRecord.setDeviceNo(waterMeter.getDeviceNo());
        readRecord.setLocation(waterMeter.getDeviceNo());
        readRecord.setProductId(waterMeter.getProductId());
        readRecord.setDeviceModel(waterMeter.getManufacturerName()+"/"+waterMeter.getProductName()+"/"+waterMeter.getProductModel());
        readRecord.setMeterNo(waterMeter.getMeterNo());
        readRecord.setFileNo(waterMeter.getFileNo());
        readRecord.setCreateTime(new Date());
        readRecord.setCreatorName(Constants.SYS_FLAG);
        readRecord.setUpdateTime(new Date());


        return readRecord;
    }


    @Override
    public void executeSyncMeterReadRecord(Map<String, Object> map) {
        log.info("begin executeSyncMeterReadRecord map = {}",JSON.toJSONString(map));
        int readDay = MapUtil.getInt(map, "readDay");
        List<MeterReadRecordDetailDto> list = findMeterReadRecord(readDay);
        log.info("begin executeSyncMeterReadRecord list size = {}",list.size());
        List<com.zcxk.rmcp.core.entity.MeterReadRecord> readRecordList = new ArrayList<>();
        if(!list.isEmpty()){
            for (MeterReadRecordDetailDto detail : list) {
                com.zcxk.rmcp.core.entity.MeterReadRecord readRecord = new com.zcxk.rmcp.core.entity.MeterReadRecord();
                readRecord.setId(idWorker.nextId());
                readRecord.setReadDate(detail.getData().getReadDate());
                readRecord.setTenantId(detail.getTenantId());
                readRecord.setCategoryId(detail.getCategoryId());
                readRecord.setProductId(detail.getProductId());
                readRecord.setDeviceId(detail.getDeviceId());
                readRecord.setCommunityId(detail.getCommunityId());
                readRecord.setCompanyOrgId(detail.getCompanyOrgId());
                readRecord.setDeptOrgId(detail.getDeptOrgId());
                readRecord.setConcentratorId(detail.getConcentratorId());
                readRecord.setCollectorId(detail.getCollectorId());
                if(detail.getData().getReadTime()!=null)readRecord.setReadTime(LocalDateTimeUtil.of(detail.getData().getReadTime()));
                readRecord.setReadStatus(detail.getData().getReadStatus());
                if(detail.getData().getReadData()!=null)readRecord.setReadData(detail.getData().getReadData().toString());
                if(detail.getData().getLastValid()!=null)readRecord.setLastValid(detail.getData().getLastValid().toString());
                if(detail.getData().getLastCost()!=null)readRecord.setLastCost(BigDecimal.valueOf(detail.getData().getLastCost()));
                readRecord.setStatus(StatusEnum.OK.getCode());
                readRecord.setCreateDate(LocalDateTime.now());
                readRecord.setUpdateDate(LocalDateTime.now());
                readRecord.setCreateBy(Constants.SYS_FLAG);
                readRecord.setUpdateBy(Constants.SYS_FLAG);
                readRecordList.add(readRecord);
            }

            if(!readRecordList.isEmpty()){
                //分批添加
                List<List<com.zcxk.rmcp.core.entity.MeterReadRecord>> tempList =  ListUtil.split(readRecordList, 5000);
                for (List<com.zcxk.rmcp.core.entity.MeterReadRecord> meterReadRecords : tempList) {
                    meterReadRecordMapper.batchInsert(meterReadRecords);
                }
            }
        }
        log.info("end executeSyncMeterReadRecord");
    }


    private List<MeterReadRecordDetailDto> findMeterReadRecord(Integer readDate){
        List<AggregationOperation> commonOperations = new ArrayList<>();
        // 1. 指定查询主文档
        /*Criteria criteria = new Criteria();
        criteria.and("status").is(1);
        MatchOperation match = Aggregation.match(criteria);
        commonOperations.add(match);*/
        // 2.拆开
        UnwindOperation unwind = Aggregation.unwind("data");
        commonOperations.add(unwind);
        // 3.子文档查询
        MatchOperation match2 = Aggregation.match(
                Criteria.where("data.readDate").is(readDate));
        commonOperations.add(match2);

        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();

        AggregationResults<MeterReadRecordDetailDto> durationData =
                mongoTemplate.aggregate(Aggregation.newAggregation(commonOperations).withOptions(aggregationOptions),
                        MeterReadRecord.class, MeterReadRecordDetailDto.class);

        return durationData.getMappedResults();
    }

    @Override
    public void todayReadRateJobHandler(Map<String, Object> map) {
        log.info("begin yesterdayReadRateJobHandler map = {}",JSON.toJSONString(map));
        Date readDay = cn.hutool.core.date.DateUtil.parse(MapUtil.getStr(map, "readDay"),"yyyyMMdd");
        int result = meterReadRecordMapper.todayReadRateJobHandler(readDay);
        log.info("end todayReadRateJobHandler result = {}",result);
    }

    @Override
    public void yesterdayReadRateJobHandler(Map<String, Object> map) {
        log.info("begin yesterdayReadRateJobHandler map = {}",JSON.toJSONString(map));
        Integer readDay = MapUtil.getInt(map, "readDay");
        log.info("begin yesterdayReadRateJobHandler readDay = {}",readDay);
        int result = meterReadRecordMapper.yesterdayReadRate(readDay);
        log.info("end yesterdayReadRateJobHandler result = {}",result);
    }

    @Override
    public void recent7daysReadRateJobHandler(Map<String, Object> map) {
        log.info("begin recent7daysReadRateJobHandler map = {}",JSON.toJSONString(map));
        Integer readDay = MapUtil.getInt(map, "readDay");
        int result = meterReadRecordMapper.recent7daysReadRate(readDay);
        log.info("end recent7daysReadRateJobHandler result = {}",result);
    }

    @Override
    public void lastMonthReadRateJobHandler(Map<String, Object> map) {
        log.info("begin lastMonthReadRateJobHandler map = {}",JSON.toJSONString(map));
        Integer readDay = MapUtil.getInt(map, "readDay");
        int result = meterReadRecordMapper.lastMonthReadRate(readDay);
        log.info("end lastMonthReadRateJobHandler result = {}",result);
    }
}
