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.MeterYearDataVo;
import com.zksy.infrared.service.InfraredReadingMeterService;
import com.zksy.infrared.mapper.InfraredReadingMeterMapper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
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);

    @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));
    }
}
