package com.zksy.infrared.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zksy.common.exception.CommonException;
import com.zksy.infrared.domain.InfraredReadingMeter;
import com.zksy.infrared.domain.vo.MeterDayDataVo;
import com.zksy.infrared.domain.vo.MeterMonthDataVo;
import com.zksy.infrared.domain.vo.MeterQuarterDataVo;
import com.zksy.infrared.domain.vo.MeterYearDataVo;
import com.zksy.infrared.service.InfraredReadingMeterService;
import com.zksy.infrared.mapper.InfraredReadingMeterMapper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @description 针对表【infrared_reading_meter(红外读电表数据)】的数据库操作Service实现
 * @createDate 2025-06-06 10:27:47
 */
@Service
public class InfraredReadingMeterServiceImpl extends ServiceImpl<InfraredReadingMeterMapper, InfraredReadingMeter>
        implements InfraredReadingMeterService {

    // 工具方法：生成默认月数据列表
    private static final List<BigDecimal> DEFAULT_MONTH_LIST = Collections.nCopies(12, BigDecimal.ZERO);
    // 工具方法：生成默认日数据列表
    private static final List<BigDecimal> DEFAULT_DAY_LIST = Collections.nCopies(31, BigDecimal.ZERO);
    // 工具方法：生成默认季数据列表
    private static final List<BigDecimal> DEFAULT_QUARTER_LIST = Collections.nCopies(3, new BigDecimal("0.00"));

    @Override
    public MeterYearDataVo selectByYear(Integer year, String meterNumber) {
        if (year == null || year < 0 || year > 9999) {
            throw new CommonException("年份无效", 400);
        }

        LocalDate firstDayOfYear = LocalDate.of(year, 1, 1);
        LocalDate lastDayOfYear = LocalDate.of(year, 12, 31);

        LocalDateTime startOfDay = firstDayOfYear.atStartOfDay();
        LocalDateTime endOfDay = lastDayOfYear.atTime(LocalTime.MAX);

        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InfraredReadingMeter::getElectricNumber, meterNumber)
                .between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .orderByAsc(InfraredReadingMeter::getCreateTime);

        List<InfraredReadingMeter> list = this.list(wrapper);

        if (list == null || list.size() < 2) {
            return new MeterYearDataVo(BigDecimal.ZERO, new ArrayList<>(DEFAULT_MONTH_LIST));
        }

        BigDecimal firstValue = BigDecimal.valueOf(list.get(0).getElectricEnergy());
        BigDecimal lastValue = BigDecimal.valueOf(list.get(list.size() - 1).getElectricEnergy());

        Map<Integer, List<InfraredReadingMeter>> groupedByMonth = list.stream()
                .collect(Collectors.groupingBy(item -> {
                    LocalDate date = item.getCreateTime().toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDate();
                    return date.getMonthValue();
                }));

        List<Integer> months = new ArrayList<>(groupedByMonth.keySet());
        Collections.sort(months);

        List<BigDecimal> monthDataList = new ArrayList<>(DEFAULT_MONTH_LIST);

        for (int i = 0; i < months.size(); i++) {
            Integer currentMonth = months.get(i);
            List<InfraredReadingMeter> meters = groupedByMonth.get(currentMonth);

            if (!meters.isEmpty()) {
                BigDecimal start = BigDecimal.valueOf(meters.get(0).getElectricEnergy());
                BigDecimal end;

                if (i < months.size() - 1) {
                    Integer nextMonth = months.get(i + 1);
                    List<InfraredReadingMeter> nextMeters = groupedByMonth.get(nextMonth);
                    end = !nextMeters.isEmpty()
                            ? BigDecimal.valueOf(nextMeters.get(0).getElectricEnergy())
                            : BigDecimal.valueOf(meters.get(meters.size() - 1).getElectricEnergy());
                } else {
                    end = BigDecimal.valueOf(meters.get(meters.size() - 1).getElectricEnergy());
                }

                monthDataList.set(currentMonth - 1, end.subtract(start));
            }
        }

        return new MeterYearDataVo(lastValue.subtract(firstValue), monthDataList);
    }

    @Override
    public MeterMonthDataVo selectByYearselectByYearAndMonthWithDayData(Integer year, Integer month, String meterNumber) {
        if (year == null || month == null || year < 0 || year > 9999 || month < 1 || month > 12) {
            throw new CommonException("年份或月份无效", 400);
        }

        LocalDate firstDayOfMonth = LocalDate.of(year, month, 1);
        LocalDate lastDayOfMonth = firstDayOfMonth.withDayOfMonth(firstDayOfMonth.lengthOfMonth());

        LocalDateTime startOfDay = firstDayOfMonth.atStartOfDay();
        LocalDateTime endOfDay = lastDayOfMonth.atTime(LocalTime.MAX);

        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InfraredReadingMeter::getElectricNumber, meterNumber)
                .between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .orderByAsc(InfraredReadingMeter::getCreateTime);

        List<InfraredReadingMeter> list = this.list(wrapper);

        if (list == null || list.size() < 2) {
            return new MeterMonthDataVo(BigDecimal.ZERO, new ArrayList<>(DEFAULT_DAY_LIST));
        }

        BigDecimal firstValue = BigDecimal.valueOf(list.get(0).getElectricEnergy());
        BigDecimal lastValue = BigDecimal.valueOf(list.get(list.size() - 1).getElectricEnergy());

        Map<Integer, List<InfraredReadingMeter>> groupedByDay = list.stream()
                .collect(Collectors.groupingBy(item -> {
                    LocalDate date = item.getCreateTime().toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDate();
                    return date.getDayOfMonth();
                }));

        List<Integer> days = new ArrayList<>(groupedByDay.keySet());
        Collections.sort(days);

        List<BigDecimal> dayDataList = new ArrayList<>(DEFAULT_DAY_LIST);

        for (int i = 0; i < days.size(); i++) {
            Integer currentDay = days.get(i);
            List<InfraredReadingMeter> currentMeters = groupedByDay.get(currentDay);

            if (!currentMeters.isEmpty()) {
                BigDecimal start = BigDecimal.valueOf(currentMeters.get(0).getElectricEnergy());
                BigDecimal end;

                if (i < days.size() - 1) {
                    Integer nextDay = days.get(i + 1);
                    List<InfraredReadingMeter> nextMeters = groupedByDay.get(nextDay);
                    end = !nextMeters.isEmpty()
                            ? BigDecimal.valueOf(nextMeters.get(0).getElectricEnergy())
                            : BigDecimal.valueOf(currentMeters.get(currentMeters.size() - 1).getElectricEnergy());
                } else {
                    end = BigDecimal.valueOf(currentMeters.get(currentMeters.size() - 1).getElectricEnergy());
                }

                dayDataList.set(currentDay - 1, end.subtract(start));
            }
        }

        return new MeterMonthDataVo(lastValue.subtract(firstValue), dayDataList);
    }

    @Override
    public MeterDayDataVo selectByNewestData(String meterNumber) {
        if (meterNumber == null || meterNumber.isEmpty()) {
            throw new CommonException("电表编号不能为空", 400);
        }

        LocalDate today = LocalDate.now();
        LocalDateTime startOfDay = today.atStartOfDay();
        LocalDateTime endOfDay = today.atTime(LocalTime.MAX);

        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InfraredReadingMeter::getElectricNumber, meterNumber)
                .between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .orderByAsc(InfraredReadingMeter::getCreateTime);

        List<InfraredReadingMeter> list = this.list(wrapper);

        if (list == null || list.size() < 2) {
            return new MeterDayDataVo(BigDecimal.ZERO);
        }

        BigDecimal firstValue = BigDecimal.valueOf(list.get(0).getElectricEnergy());
        BigDecimal lastValue = BigDecimal.valueOf(list.get(list.size() - 1).getElectricEnergy());

        return new MeterDayDataVo(lastValue.subtract(firstValue));
    }
    @Override
    public MeterYearDataVo selectByCurrentYearData() {
        log.warn("按照年统计电表");
        //首先获取当前年份
        int currentYear = LocalDate.now().getYear();

        LocalDate firstDayOfYear = LocalDate.of(currentYear, 1, 1);
        LocalDate lastDayOfYear = LocalDate.of(currentYear, 12, 31);
        //获取当前年份的开始日期和结束日期
        LocalDateTime startOfDay = firstDayOfYear.atStartOfDay();
        LocalDateTime endOfDay = lastDayOfYear.atTime(LocalTime.MAX);

        //构建查询条件，查询在时间范围内的数据，按照升序排序
        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<InfraredReadingMeter>()
                .between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .orderByAsc(InfraredReadingMeter::getCreateTime);
        //根据查询条件返回结果
        List<InfraredReadingMeter> list = this.list(wrapper);
        //根据电表编号和月份对结果集进行分组
        Map<Integer, Map<String, List<InfraredReadingMeter>>> groupedByMonthAndMeter =
                list.stream().collect(Collectors.groupingBy(
                        item -> item.getCreateTime()
                                .toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDate()
                                .getMonthValue(),
                        Collectors.groupingBy(InfraredReadingMeter::getElectricNumber)
                ));
        //每月用电量
        List<BigDecimal> monthDataList = new ArrayList<>(Collections.nCopies(12, BigDecimal.ZERO));
        for(int month=1;month<=12;month++){
            //获取当前月份的数据
            Map<String, List<InfraredReadingMeter>> meterData=groupedByMonthAndMeter.getOrDefault(month,Collections.emptyMap());

            BigDecimal monthTotal=BigDecimal.ZERO;
            //将每个电表每月的数据累加
            for(List<InfraredReadingMeter> meterList:meterData.values()){
                if(meterList.size()>=2){
                    BigDecimal start = BigDecimal.valueOf(meterList.get(0).getElectricEnergy());
                    BigDecimal end = BigDecimal.valueOf(meterList.get(meterList.size()-1).getElectricEnergy());
                    monthTotal=monthTotal.add(end.subtract(start));
                }
            }
            monthTotal = monthTotal.setScale(2, RoundingMode.HALF_UP); //精度
            monthDataList.set(month-1,monthTotal);
        }
        //计算年用电量
        BigDecimal totalElectricity = monthDataList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        //年总量处理
        totalElectricity = totalElectricity.setScale(2, RoundingMode.HALF_UP);
        return new MeterYearDataVo(totalElectricity,monthDataList);

    }

    @Override
    public MeterMonthDataVo selectByCurrentYearselectByCurrentYearAndCurrentMonthWithDayData() {
        final List<BigDecimal> defaultDayList  = Collections.nCopies(31, new BigDecimal("0.00"));
        log.warn("根据当前年月统计电表数据");
        //首先获取当前的日期
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        int month = now.getMonthValue();

        LocalDate firstDayOfMonth = LocalDate.of(year, month, 1);
        LocalDate lastDayOfMonth = firstDayOfMonth.withDayOfMonth(firstDayOfMonth.lengthOfMonth());

        LocalDateTime startOfDay = firstDayOfMonth.atStartOfDay();
        LocalDateTime endOfDay = lastDayOfMonth.atTime(LocalTime.MAX);

        //构造查询条件
        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .orderByAsc(InfraredReadingMeter::getCreateTime);
        //根据查询条件获取查询结果
        List<InfraredReadingMeter> list = this.list(wrapper);

        if(list==null||list.size()<2){
            return new MeterMonthDataVo(new BigDecimal("0.00"),defaultDayList);
        }
        //对查询结果按照日期和电表编号分组
        Map<Integer,Map<String,List<InfraredReadingMeter>>> groupedByDayAndMeter=list.stream()
                .collect(Collectors.groupingBy(item->item.getCreateTime()
                                .toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDate()
                                .getDayOfMonth(),
                        Collectors.groupingBy(InfraredReadingMeter::getElectricNumber)));
        //构建返回的结果集
        List<BigDecimal> dayDataList=new ArrayList<>(Collections.nCopies(firstDayOfMonth.lengthOfMonth(),BigDecimal.ZERO));
        for(int day=1;day<=firstDayOfMonth.lengthOfMonth();day++){
            //获取到天数的各个电表的用电量
            Map<String, List<InfraredReadingMeter>> meterData  = groupedByDayAndMeter.getOrDefault(day, Collections.emptyMap());

            BigDecimal dayTotal=BigDecimal.ZERO;
            //遍历结果集，即各个电表的各天数据
            for(List<InfraredReadingMeter> meterList:meterData.values()){
                if(meterList.size()>=2){
                    BigDecimal start = BigDecimal.valueOf(meterList.get(0).getElectricEnergy());
                    BigDecimal end = BigDecimal.valueOf(meterList.get(meterList.size() - 1).getElectricEnergy());
                    dayTotal=dayTotal.add(end.subtract(start));
                }
            }
            dayTotal = dayTotal.setScale(2, RoundingMode.HALF_UP); // 添加这行
            dayDataList.set(day-1,dayTotal);
        }
        // 按电表分组，计算每个电表当月最后一天最后一条数据减去当月第一天第一条数据，再累加
        Map<String, List<InfraredReadingMeter>> groupedByMeter = list.stream()
                .collect(Collectors.groupingBy(InfraredReadingMeter::getElectricNumber));

        BigDecimal totalElectricity = BigDecimal.ZERO;

        for (List<InfraredReadingMeter> meterList : groupedByMeter.values()) {
            // 找到第一天第一条数据
            InfraredReadingMeter firstRecord = meterList.stream()
                    .filter(r -> r.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(firstDayOfMonth))
                    .findFirst()
                    .orElse(null);

            // 找到最后一天最后一条数据
            InfraredReadingMeter lastRecord = meterList.stream()
                    .filter(r -> r.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(lastDayOfMonth))
                    .reduce((first, second) -> second)  // 获取最后一条
                    .orElse(null);

            if (firstRecord != null && lastRecord != null) {
                BigDecimal startEnergy = BigDecimal.valueOf(firstRecord.getElectricEnergy());
                BigDecimal endEnergy = BigDecimal.valueOf(lastRecord.getElectricEnergy());
                totalElectricity = totalElectricity.add(endEnergy.subtract(startEnergy));
            }
        }
        // 月总量处理
        totalElectricity = totalElectricity.setScale(2, RoundingMode.HALF_UP);
        return new MeterMonthDataVo(totalElectricity,dayDataList);
    }

    @Override
    public MeterQuarterDataVo selectByCurrentQuarterData() {
        log.warn("根据当前季度统计电表数据");
        //首先获取当前的日期
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        int month = now.getMonthValue();
        //计算当前季度
        int currentQuarter = (month - 1) / 3 + 1;
        int startMonth = (currentQuarter - 1) * 3 + 1;
        int endMonth = startMonth + 2;

        LocalDate startDate = LocalDate.of(year, startMonth, 1);
        LocalDate endDate = LocalDate.of(year, endMonth, YearMonth.of(year, endMonth).lengthOfMonth());

        LocalDateTime startDateTime = startDate.atStartOfDay();
        LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX);

        LambdaQueryWrapper<InfraredReadingMeter> wrapper = new LambdaQueryWrapper<>();
        //构建查询条件
        wrapper.between(InfraredReadingMeter::getCreateTime,startDateTime,endDateTime)
                .orderByAsc(InfraredReadingMeter::getCreateTime);
        //获取查询结果
        List<InfraredReadingMeter> list = this.list(wrapper);

        if(list==null||list.size()<2){
            return new MeterQuarterDataVo(new BigDecimal("0.00"),DEFAULT_QUARTER_LIST);
        }
        //将结果集根据年份和电表编号进行分组
        Map<Integer, Map<String, List<InfraredReadingMeter>>> groupedByMonthAndMeter =
                list.stream().collect(Collectors.groupingBy(
                        item -> item.getCreateTime().toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDate()
                                .getMonthValue(),
                        Collectors.groupingBy(InfraredReadingMeter::getElectricNumber)
                ));
        List<BigDecimal> monthDataList=new ArrayList<>(Collections.nCopies(3, new BigDecimal("0.00")));
        BigDecimal totalQuarterElectricity = BigDecimal.ZERO;
        for(int m=startMonth;m<=endMonth;m++){
            Map<String, List<InfraredReadingMeter>> meterData = groupedByMonthAndMeter.getOrDefault(m, Collections.emptyMap());
            BigDecimal monthTotal = BigDecimal.ZERO;
            for(List<InfraredReadingMeter> meterList:meterData.values()){
                if(meterList.size()>=2){
                    BigDecimal start = BigDecimal.valueOf(meterList.get(0).getElectricEnergy());
                    BigDecimal end = BigDecimal.valueOf(meterList.get(meterList.size() - 1).getElectricEnergy());
                    monthTotal=monthTotal.add(end.subtract(start));
                }
            }
            monthTotal = monthTotal.setScale(2, RoundingMode.HALF_UP);
            monthDataList.set(m-startMonth,monthTotal);
            totalQuarterElectricity = totalQuarterElectricity.add(monthTotal);
        }

        totalQuarterElectricity = totalQuarterElectricity.setScale(2, RoundingMode.HALF_UP);

        return new MeterQuarterDataVo(totalQuarterElectricity,monthDataList);
    }
}
