package com.zcxk.rmcp.core.dao.mongo;

import com.zcxk.core.common.pojo.PageResult;
import com.zcxk.core.common.pojo.UserCondition;
import com.zcxk.core.mongo.base.BaseDao;
import com.zcxk.core.mongo.utils.StringUtil;
import com.zcxk.core.oauth2.util.UserUtil;
import com.zcxk.core.utils.BigDecimalUtils;
import com.zcxk.core.utils.DateUtil;
import com.zcxk.core.utils.ZoniotIntegerUtils;
import com.zcxk.core.utils.ZoniotStringUtils;
import com.zcxk.rmcp.api.dto.meterreadrecord.MeterReadRecordDto;
import com.zcxk.rmcp.api.dto.meterreadrecord.MeterReadRecordUpdateDto;
import com.zcxk.rmcp.api.enums.ReadStatusEnum;
import com.zcxk.rmcp.api.vo.MeterReadRecordAggregationVo;
import com.zcxk.rmcp.api.vo.MeterReadRecordVo;
import com.zcxk.rmcp.core.entity.Device;
import com.zcxk.rmcp.core.mongo.MeterReadRecord;
import io.swagger.models.auth.In;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
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.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author Andy
 * @version V1.0
 * @description: 抄表记录
 * @date 2021/7/22
 **/
@Repository
public class MeterReadRecordDao extends BaseDao<MeterReadRecord, String> implements BaseMongoDao {

    /**
    * @author Andy
    * @description 根据日期和ID获取当前日期的抄表数据
    * @date 15:54 2021/7/23
    * @param id, readDate
    * @return com.zcxk.rmcp.api.vo.MeterReadRecordVo
    **/
    public MeterReadRecordVo findMeterReadRecord(String id, String readDate){
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("id").is(id)),
                Aggregation.unwind("data"),
                Aggregation.match(Criteria.where("data.readDate").is(Integer.parseInt(readDate.replaceAll("-",""))))
        ).withOptions(aggregationOptions);
        AggregationResults<MeterReadRecordVo> durationData =
                mongoTemplate.aggregate(agg, MeterReadRecord.class, MeterReadRecordVo.class);
        List<MeterReadRecordVo> dataList = durationData.getMappedResults();
        if (CollectionUtils.isEmpty(dataList)) {
            return null;
        }
        return dataList.get(0);
    }

    /**
    * @author Andy
    * @description 保存抄表记录
    * @date 16:32 2021/7/22
    * @param readRecord
    * @return void
    **/
    public void saveMeterReadRecord(MeterReadRecord readRecord){
        super.save(readRecord);
    }


    /**
    * @author Andy
    * @description 更新读数
    * @date 15:27 2021/7/23
    * @param dto
    * @return long
    **/
    public long updateReadData(MeterReadRecordUpdateDto dto){
        MeterReadRecordVo meterReadRecordVo = findMeterReadRecord(dto.getId(), dto.getReadDate());
        if (meterReadRecordVo == null) {
            return 0;
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(dto.getId()));
        query.addCriteria(Criteria.where("data.readDate").is(Integer.parseInt(dto.getReadDate().replaceAll("-",""))));
        Update update = new Update();
        update.set("data.$.readData",dto.getReadData());
        update.set("data.$.readStatus", ReadStatusEnum.READ.getCode());
        update.set("data.$.readTime", LocalDateTime.now());
        update.set("data.$.lastCost", BigDecimalUtils.subtract(meterReadRecordVo.getData().getLastValid(), new BigDecimal(dto.getReadData())));
        update.set("data.$.createBy", UserUtil.getCurrentUser().getUsername());
        return upsert(query,update).getModifiedCount();
    }

    /**
    * @author Andy
    * @description 查询集合
    * @date 10:36 2021/7/23
    * @param dto, query
    * @return java.util.List<com.zcxk.rmcp.api.vo.MeterReadRecordVo>
    **/
    public List<MeterReadRecordVo> listMeterReadRecord (MeterReadRecordDto dto, UserCondition query){
        // 查询条件
        List<AggregationOperation> commonOperations =  queryCondition(dto, query);
        // 排序
        commonOperations.add(Aggregation.sort(Sort.Direction.DESC,"data.readTime"));
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        AggregationResults<MeterReadRecordVo> durationData =
                mongoTemplate.aggregate(Aggregation.newAggregation(commonOperations).withOptions(aggregationOptions),
                        MeterReadRecord.class, MeterReadRecordVo.class);
        return durationData.getMappedResults();
    }

    /**
    * @author Andy
    * @description 分页查询
    * @date 18:04 2021/7/22
    * @param dto, query
    * @return void
    **/
    public PageResult<MeterReadRecordVo> pageMeterReadRecord(MeterReadRecordDto dto, UserCondition query, Pageable pageable) {
        // 查询条件
        List<AggregationOperation> commonOperations =  queryCondition(dto, query);

        int total = getAggregationCount(commonOperations);
        if (total == 0) {
            return PageResult.builder(new ArrayList<>(), pageable.getPageNumber() + 1, pageable.getPageSize(), total);
        }
        // 排序
        commonOperations.add(Aggregation.sort(Sort.Direction.DESC,"data.readTime"));
        // 分页条件
        commonOperations.add(Aggregation.skip((long)(dto.getPageNum() > 1 ?(dto.getPageNum()-1) * dto.getPageSize() : 0)));

        commonOperations.add(Aggregation.limit(dto.getPageSize()));
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();

        AggregationResults<MeterReadRecordVo> durationData =
                mongoTemplate.aggregate(Aggregation.newAggregation(commonOperations).withOptions(aggregationOptions),
                        MeterReadRecord.class, MeterReadRecordVo.class);
        return PageResult.builder(durationData.getMappedResults(), pageable.getPageNumber() + 1, pageable.getPageSize(), total);
    }

    /**
     * @author Andy
     * @description 聚合小区每天用水量
     * @date 9:27 2021/7/28
     * @param year ：年, statDay：具体日期
     * @return void
     **/
    public List<MeterReadRecordAggregationVo> aggregationCommunityWaterConsumption(int year, int statDay, int pageNum, int pageSize){
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        List<AggregationOperation> queryCondition = groupCondition(year, statDay, statDay,
                Aggregation.group("tenantId", "companyOrgId", "deptOrgId",  "communityId", "categoryId", "data.readDate")
                        .sum("$data.lastCost").as("waterConsumption"));
        // 分页条件
        queryCondition.add(Aggregation.skip((long)(pageNum > 1 ?(pageNum-1) * pageSize : 0)));
        queryCondition.add(Aggregation.limit(pageSize));
        AggregationResults<MeterReadRecordAggregationVo> durationData =
                mongoTemplate.aggregate(Aggregation.newAggregation(queryCondition).withOptions(aggregationOptions),
                        MeterReadRecord.class, MeterReadRecordAggregationVo.class);
        return durationData.getMappedResults();
    }

    /**
     *
     * @description 查询结算期的用水量
     * @author hym
     * @updateTime 2021/7/28 10:25
     * @throws
     */
    public List<MeterReadRecord> querySettlementWaterConsumption(Device device, Date clearingStartDate, Date clearingEndDate) {
        Criteria criteria = new Criteria();
        criteria.and("deviceId").is(device.getId());
        Criteria criteria1 = Criteria.where("data.readDate").lte(Integer.valueOf(
                DateUtil.formatDate(clearingEndDate,"yyyyMMdd")));
        Criteria criteria2 = Criteria.where("data.readDate").gte(Integer.valueOf(
                DateUtil.formatDate(clearingStartDate,"yyyyMMdd")));
        Criteria dayCritera = new Criteria();
        dayCritera.orOperator(criteria1,criteria2);
        List<AggregationOperation> countQuery =aggrQueryCondition(criteria,"data",
                dayCritera,null);

        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        AggregationResults<MeterReadRecord> aggregate = mongoTemplate.aggregate(Aggregation.newAggregation(countQuery).withOptions(aggregationOptions)
                , MeterReadRecord.class, MeterReadRecord.class);
        return aggregate.getMappedResults();
    }

    /**
    * @author Andy
    * @description 根据设备ID和读表日期小于endDate，查询设备数据
    * @date 17:25 2021/8/2
    * @param id, endDate
    * @return java.util.List<com.zcxk.rmcp.core.mongo.MeterReadRecord>
    **/
    public List<MeterReadRecordVo> queryDeviceMeterReadRecordWithCondtion(Long id, int endDate) {
        return listSingleDeviceTimeData(id, null, endDate);
    }

    /**
    * @author Andy
    * @description 根据设备ID和读表日期大于startDate且小于endDate，查询设备记录
    * @date 17:26 2021/8/2
    * @param id, startDate, endDate
    * @return java.util.List<com.zcxk.rmcp.core.mongo.MeterReadRecord>
    **/
    public List<MeterReadRecordVo> queryDeviceMeterReadRecordWithCondtion(Long id, Integer startDate, Integer endDate) {
        return listSingleDeviceTimeData(id, startDate, endDate);
    }

    /**
    * @author Andy
    * @description 根据设备ID和读表日期大于startDate且小于endDate， 查询设备数据
    * @date 17:26 2021/8/2
    * @param deviceId, startDate, endDate
    * @return java.util.List<com.zcxk.rmcp.core.mongo.MeterReadRecord>
    **/
    public List<MeterReadRecordVo> listSingleDeviceTimeData(Long deviceId, Integer startDate, Integer endDate){
        Criteria criteria = new Criteria();
        criteria.and("deviceId").is(deviceId);
        Criteria dayCritera = new Criteria();
        if (startDate != null && endDate != null) {
            dayCritera.and("data.readDate").gte(startDate).lte(endDate);
        } else if (startDate != null) {
            dayCritera.and("data.readDate").gte(startDate);
        } else if (endDate != null) {
            dayCritera.and("data.readDate").lte(endDate);
        }
        List<AggregationOperation> countQuery = aggrQueryCondition(criteria,"data",
                dayCritera,null);
        countQuery.add(Aggregation.sort(Sort.Direction.DESC,"data.readTime"));
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();
        AggregationResults<MeterReadRecordVo> aggregate = mongoTemplate.aggregate(Aggregation.newAggregation(countQuery).withOptions(aggregationOptions)
                , MeterReadRecord.class, MeterReadRecordVo.class);
        return aggregate.getMappedResults();
    }


    /**
     * @author Andy
     * @description 查询条件
     * @date 10:37 2021/7/23
     * @param dto, query
     * @return java.util.List<org.springframework.data.mongodb.core.aggregation.AggregationOperation>
     **/
    private List<AggregationOperation> queryCondition(MeterReadRecordDto dto, UserCondition userCondition){
        LocalDate localDate;
        List<AggregationOperation> commonOperations = new ArrayList<>();
        Criteria criteria = new Criteria();
        if (ZoniotStringUtils.isEmpty(dto.getDate())) {
            localDate = DateUtil.parseLocalDate(new Date());
        } else {
            localDate = DateUtil.parseLocalDate(dto.getDate());
        }
        criteria.and("year").is(localDate.getYear());
        criteria.and("categoryId").is(dto.getCategoryId());
        if (StringUtil.isNotEmpty(userCondition.getTenantId())){
            criteria.and("tenantId").is(userCondition.getTenantId());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getConcentratorId())) {
            criteria.and("concentratorId").is(dto.getConcentratorId());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getChannelNumberId())) {
            criteria.and("channelNumberId").is(dto.getChannelNumberId());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getCollectorCode())) {
            criteria.and("collectorCode").is(dto.getCollectorCode());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getConcentratorId())) {
            criteria.and("concentratorId").is(dto.getConcentratorId());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getDeviceMeterNo())) {
            criteria.and("readStatus").is(dto.getReadStatus());
        }
        if (ZoniotStringUtils.isNotEmpty(dto.getDeviceMeterNo())) {
            Criteria criteria1 = Criteria.where("meterNo").is(dto.getDeviceMeterNo());
            Criteria criteria2 = Criteria.where("deviceNo").is(dto.getDeviceMeterNo());
            criteria.orOperator(criteria1,criteria2);
        }
        // 设置用户权限过滤
        criteriaUserCondition(userCondition, criteria);
        // 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(Integer.parseInt(localDate.toString().replaceAll("-",""))));
        commonOperations.add(match2);
        return commonOperations;
    }


    /**
     * @author Andy
     * @description 分组查询
     * @date 18:36 2021/7/27
     * @param year, beginDate, endDate, groupOperation
     * @return java.util.List<org.springframework.data.mongodb.core.aggregation.AggregationOperation>
     **/
    private List<AggregationOperation> groupCondition(int year, int readDateBeginDate, int readDateEndDate, GroupOperation groupOperation){
        Criteria unwindCriteria;
        Criteria criteria = new Criteria();
        criteria.and("year").is(year);
        if (readDateBeginDate == readDateEndDate) {
            unwindCriteria = Criteria.where("data.readDate").is(readDateBeginDate);
        } else {
            unwindCriteria = Criteria.where("data.readDate").gte(readDateBeginDate).lte(readDateEndDate);
        }
        return aggrQueryCondition(criteria, "data", unwindCriteria, groupOperation);
    }

    /**
     * @author Andy
     * @description 获取总条数
     * @date 10:34 2021/7/23
     * @param commonOperations
     * @return int
     **/
    private int getAggregationCount(List<AggregationOperation> commonOperations)  {
        List<AggregationOperation> countQuery = new ArrayList<>(commonOperations);
        countQuery.add(Aggregation.count().as("count"));
        countQuery.add(Aggregation.project("count"));
        AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(true).build();

        AggregationResults<Map> aggregate = mongoTemplate.aggregate(Aggregation.newAggregation(countQuery).
                withOptions(aggregationOptions), MeterReadRecord.class, Map.class);
        List<Map> mappedResults = aggregate.getMappedResults();
        return CollectionUtils.isEmpty(mappedResults) ? 0 : ZoniotIntegerUtils.parseInt(mappedResults.get(0).get("count"));
    }
}
