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.CompanyElectric;
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.CompanyElectricService;
import com.zksy.infrared.service.InfraredReadingMeterService;
import com.zksy.infrared.mapper.InfraredReadingMeterMapper;
import org.springframework.beans.factory.annotation.Autowired;
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"));
    @Autowired
    private CompanyElectricService companyElectricService;

    @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()
                        // TODO 过滤掉电表号为空、创建时间为空、总电能为空的行
                        .filter(item -> item.getElectricNumber() != null
                                && !item.getElectricNumber().isEmpty()
                                && item.getCreateTime() != null
                                && item.getElectricEnergy() != null)
                        .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"));


        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()
                // TODO 过滤掉电表号为空、创建时间为空、总电能为空的行
                .filter(item -> item.getElectricNumber() != null
                        && !item.getElectricNumber().isEmpty()
                        && item.getCreateTime() != null
                        && item.getElectricEnergy() != null)
                .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()
                // TODO 过滤掉电表号为空、创建时间为空、总电能为空的行
                .filter(item -> item.getElectricNumber() != null
                        && !item.getElectricNumber().isEmpty()
                        && item.getCreateTime() != null
                        && item.getElectricEnergy() != null)
                .collect(Collectors.groupingBy(InfraredReadingMeter::getElectricNumber));

        BigDecimal totalElectricity = BigDecimal.ZERO;

        for (List<InfraredReadingMeter> meterList : groupedByMeter.values()) {

            // 找到当月最早一条数据（优化逻辑）
            InfraredReadingMeter firstRecord = meterList.stream()
                    .min(Comparator.comparing(InfraredReadingMeter::getCreateTime))
                    .orElse(null);

            // 找到当月最晚一条数据（优化逻辑）
            InfraredReadingMeter lastRecord = meterList.stream()
                    .max(Comparator.comparing(InfraredReadingMeter::getCreateTime))
                    .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() {

        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()
                        // TODO 过滤掉电表号为空、创建时间为空、总电能为空的行
                        .filter(item -> item.getElectricNumber() != null
                                && !item.getElectricNumber().isEmpty()
                                && item.getCreateTime() != null
                                && item.getElectricEnergy() != null)
                        .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);
    }
    @Override
    public List<Map<String, Object>> getDailyAndTotalElectricityAsMap() {
        // 确定当日时间范围
        LocalDate today = LocalDate.now();
        Date startOfDay = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date endOfDay = Date.from(today.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());

        // 获取所有有效的电表号（非空且有总电量记录）
        LambdaQueryWrapper<InfraredReadingMeter> allMeterQuery = new LambdaQueryWrapper<>();
        allMeterQuery.isNotNull(InfraredReadingMeter::getElectricNumber)
                .isNotNull(InfraredReadingMeter::getElectricEnergy)
                .groupBy(InfraredReadingMeter::getElectricNumber); // 去重获取所有电表号
        List<String> allValidMeterNumbers = list(allMeterQuery).stream()
                .map(InfraredReadingMeter::getElectricNumber)
                .filter(Objects::nonNull)
                .filter(num -> !num.trim().isEmpty())
                .collect(Collectors.toList());

        // 查询当日所有有效记录（用于计算当日用电量）
        LambdaQueryWrapper<InfraredReadingMeter> todayQuery = new LambdaQueryWrapper<>();
        todayQuery.between(InfraredReadingMeter::getCreateTime, startOfDay, endOfDay)
                .isNotNull(InfraredReadingMeter::getElectricNumber)
                .isNotNull(InfraredReadingMeter::getElectricEnergy)
                .orderByAsc(InfraredReadingMeter::getCreateTime);
        List<InfraredReadingMeter> todayRecords = list(todayQuery);

        // 按电表号分组存储当日记录
        Map<String, List<InfraredReadingMeter>> todayRecordsByMeter = todayRecords.stream()
                .collect(Collectors.groupingBy(InfraredReadingMeter::getElectricNumber));

        // 查询所有电表的最新总电量记录
        LambdaQueryWrapper<InfraredReadingMeter> latestQuery = new LambdaQueryWrapper<>();
        latestQuery.isNotNull(InfraredReadingMeter::getElectricNumber)
                .isNotNull(InfraredReadingMeter::getElectricEnergy);
        List<InfraredReadingMeter> allRecords = list(latestQuery);

        // 按电表号分组，取最新记录（创建时间最晚）
        Map<String, InfraredReadingMeter> latestRecordsByMeter = allRecords.stream()
                .collect(Collectors.groupingBy(InfraredReadingMeter::getElectricNumber,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparing(InfraredReadingMeter::getCreateTime)),
                                Optional::get)));

        // 组装结果（保留两位小数）
        List<Map<String, Object>> result = new ArrayList<>();
        for (String electricNumber : allValidMeterNumbers) {
            Map<String, Object> meterMap = new HashMap<>(4);
            meterMap.put("electricNumber", electricNumber);

            LambdaQueryWrapper<CompanyElectric> companyElectricQuery = new LambdaQueryWrapper<>();
            companyElectricQuery.eq(CompanyElectric::getElectricNumber, electricNumber);
            CompanyElectric companyElectric = companyElectricService.getOne(companyElectricQuery);
            meterMap.put("companyName", companyElectric == null ? "" : companyElectric.getCompanyName());

            // 获取该电表的最新总电量（保留两位小数）
            InfraredReadingMeter latestRecord = latestRecordsByMeter.get(electricNumber);
            if (latestRecord == null || latestRecord.getElectricEnergy() == null) {
                continue; // 排除总电量为空的电表
            }
            BigDecimal totalElectricity = BigDecimal.valueOf(latestRecord.getElectricEnergy())
                    .setScale(2, RoundingMode.HALF_UP); // 四舍五入保留两位小数
            meterMap.put("totalElectricity", totalElectricity);

            // 计算当日用电量（无数据则为0，保留两位小数）
            List<InfraredReadingMeter> todayMeterRecords = todayRecordsByMeter.getOrDefault(electricNumber, Collections.emptyList());
            BigDecimal dailyElectricity;
            if (todayMeterRecords.size() >= 2) {
                InfraredReadingMeter first = todayMeterRecords.get(0);
                InfraredReadingMeter last = todayMeterRecords.get(todayMeterRecords.size() - 1);
                if (first.getElectricEnergy() != null && last.getElectricEnergy() != null) {
                    double diff = last.getElectricEnergy() - first.getElectricEnergy();
                    dailyElectricity = BigDecimal.valueOf(diff)
                            .setScale(2, RoundingMode.HALF_UP);
                } else {
                    dailyElectricity = BigDecimal.ZERO.setScale(2); // 数据异常时设为0.00
                }
            } else {
                dailyElectricity = BigDecimal.ZERO.setScale(2); // 当日无数据或记录不足，设为0.00
            }
            meterMap.put("dailyElectricity", dailyElectricity);

            result.add(meterMap);
        }

        return result;
    }
}
