package com.ruoyi.ems.service.impl;

import com.ruoyi.ems.domain.ElecConsumeForecast;
import com.ruoyi.ems.mapper.ElecConsumeForecastMapper;
import com.ruoyi.ems.service.IElecConsumeForecastService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 电力消耗预测Service业务层处理
 *
 * @author ruoyi
 * @date 2024-08-12
 */
@Service
public class ElecConsumeForecastServiceImpl implements IElecConsumeForecastService {

    @Autowired
    private ElecConsumeForecastMapper forecastMapper;

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 查询电力消耗预测
     *
     * @param id 电力消耗预测主键
     * @return 电力消耗预测
     */
    @Override
    public ElecConsumeForecast selectForecastById(Long id) {
        return forecastMapper.selectForecastById(id);
    }

    /**
     * 查询电力消耗预测列表（支持层级汇总）
     * 注意：预测数据只查询明天及之后的数据
     *
     * @param forecast 电力消耗预测
     * @return 电力消耗预测
     */
    @Override
    public List<ElecConsumeForecast> selectForecastList(ElecConsumeForecast forecast) {
        // 确保预测数据从明天开始
        adjustForecastDateRange(forecast);
        return forecastMapper.selectForecastList(forecast);
    }

    /**
     * 新增电力消耗预测
     *
     * @param forecast 电力消耗预测
     * @return 结果
     */
    @Override
    public int insertForecast(ElecConsumeForecast forecast) {
        return forecastMapper.insertForecast(forecast);
    }

    /**
     * 修改电力消耗预测
     *
     * @param forecast 电力消耗预测
     * @return 结果
     */
    @Override
    public int updateForecast(ElecConsumeForecast forecast) {
        return forecastMapper.updateForecast(forecast);
    }

    /**
     * 批量删除电力消耗预测
     *
     * @param ids 需要删除的电力消耗预测主键
     * @return 结果
     */
    @Override
    public int deleteForecastByIds(Long[] ids) {
        return forecastMapper.deleteForecastByIds(ids);
    }

    /**
     * 删除电力消耗预测信息
     *
     * @param id 电力消耗预测主键
     * @return 结果
     */
    @Override
    public int deleteForecastById(Long id) {
        return forecastMapper.deleteForecastById(id);
    }

    /**
     * 查询每日汇总趋势数据（支持层级汇总）
     * 注意：只返回明天及之后的预测数据
     */
    @Override
    public List<ElecConsumeForecast> calcForecastDateRange(ElecConsumeForecast forecast) {
        // 确保预测数据从明天开始
        adjustForecastDateRange(forecast);
        return forecastMapper.calcForecastDateRange(forecast);
    }

    /**
     * 获取预测汇总统计（支持层级汇总）
     * 包含环比趋势计算
     * 注意：只统计明天及之后的预测数据
     */
    @Override
    public Map<String, Object> selectForecastSummary(ElecConsumeForecast forecast) {
        // 确保预测数据从明天开始
        adjustForecastDateRange(forecast);

        // 查询当前周期汇总数据
        Map<String, Object> result = forecastMapper.selectForecastSummary(forecast);

        if (result == null) {
            result = new HashMap<>();
            result.put("totalElec", 0.0);
            result.put("avgElec", 0.0);
            result.put("itemCount", 0);
            result.put("dayCount", 0);
            result.put("dateRange", "-");
            result.put("trend", null);
            return result;
        }

        // 处理日期范围显示（显示实际的预测周期）
        String startDate = forecast.getStartRecTime();
        String endDate = forecast.getEndRecTime();
        if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate)) {
            result.put("dateRange", startDate + " ~ " + endDate);

            // 计算实际天数
            LocalDate start = LocalDate.parse(startDate, DATE_FORMATTER);
            LocalDate end = LocalDate.parse(endDate, DATE_FORMATTER);
            long days = ChronoUnit.DAYS.between(start, end) + 1;
            result.put("dayCount", days);
        }
        else {
            Object minDate = result.get("startDate");
            Object maxDate = result.get("endDate");
            if (minDate != null && maxDate != null) {
                result.put("dateRange", minDate.toString() + " ~ " + maxDate.toString());
            }
            else {
                result.put("dateRange", "-");
            }
        }

        // 重新计算日均值（基于实际预测天数）
        Object totalElecObj = result.get("totalElec");
        Object dayCountObj = result.get("dayCount");

        if (totalElecObj != null && dayCountObj != null) {
            double totalElec = ((Number) totalElecObj).doubleValue();
            long dayCount = ((Number) dayCountObj).longValue();
            if (dayCount > 0) {
                double avgElec = totalElec / dayCount;
                result.put("avgElec", Math.round(avgElec * 100) / 100.0);
            }
        }

        // 计算环比趋势（与上一周期对比）
        Double trend = calculateTrend(forecast, result);
        result.put("trend", trend);

        return result;
    }

    /**
     * 调整预测日期范围，确保从明天开始
     * 预测数据不应包含今天及之前的历史数据
     *
     * @param forecast 查询条件
     */
    private void adjustForecastDateRange(ElecConsumeForecast forecast) {
        // 计算明天的日期
        LocalDate tomorrow = LocalDate.now().plusDays(1);
        String tomorrowStr = tomorrow.format(DATE_FORMATTER);

        String startRecTime = forecast.getStartRecTime();

        // 如果没有设置开始日期，默认从明天开始
        if (StringUtils.isEmpty(startRecTime)) {
            forecast.setStartRecTime(tomorrowStr);
        }
        else {
            // 如果设置的开始日期早于明天，强制调整为明天
            try {
                LocalDate startDate = LocalDate.parse(startRecTime, DATE_FORMATTER);
                if (startDate.isBefore(tomorrow)) {
                    forecast.setStartRecTime(tomorrowStr);
                }
            }
            catch (Exception e) {
                // 日期解析失败，使用明天
                forecast.setStartRecTime(tomorrowStr);
            }
        }

        // 如果没有设置结束日期，可以设置一个默认值（比如未来7天）
        // 或者保持为空，让SQL查询所有未来数据
        if (StringUtils.isEmpty(forecast.getEndRecTime())) {
            // 默认查询未来7天
            LocalDate defaultEnd = tomorrow.plusDays(6);
            forecast.setEndRecTime(defaultEnd.format(DATE_FORMATTER));
        }
        else {
            // 确保结束日期不早于开始日期
            try {
                LocalDate endDate = LocalDate.parse(forecast.getEndRecTime(), DATE_FORMATTER);
                LocalDate startDate = LocalDate.parse(forecast.getStartRecTime(), DATE_FORMATTER);
                if (endDate.isBefore(startDate)) {
                    forecast.setEndRecTime(forecast.getStartRecTime());
                }
            }
            catch (Exception e) {
                // 忽略解析错误
            }
        }
    }

    /**
     * 计算环比趋势
     * 根据当前查询周期，计算上一个同等长度周期的数据，进行对比
     * 注意：上一周期也需要是预测数据（即上一周期的"明天"数据）
     *
     * @param forecast      当前查询条件
     * @param currentResult 当前周期的汇总结果
     * @return 环比趋势百分比，正数表示上涨，负数表示下降
     */
    private Double calculateTrend(ElecConsumeForecast forecast, Map<String, Object> currentResult) {
        try {
            String startRecTime = forecast.getStartRecTime();
            String endRecTime = forecast.getEndRecTime();

            // 如果没有指定时间范围，无法计算环比
            if (StringUtils.isEmpty(startRecTime) || StringUtils.isEmpty(endRecTime)) {
                return null;
            }

            // 解析日期
            LocalDate startDate = LocalDate.parse(startRecTime, DATE_FORMATTER);
            LocalDate endDate = LocalDate.parse(endRecTime, DATE_FORMATTER);

            // 计算当前周期天数
            long dayDiff = ChronoUnit.DAYS.between(startDate, endDate);

            // 计算上一周期的时间范围
            LocalDate prevEndDate = startDate.minusDays(1);
            LocalDate prevStartDate = prevEndDate.minusDays(dayDiff);

            // 构建上一周期的查询条件
            ElecConsumeForecast prevForecast = new ElecConsumeForecast();
            BeanUtils.copyProperties(forecast, prevForecast);
            prevForecast.setStartRecTime(prevStartDate.format(DATE_FORMATTER));
            prevForecast.setEndRecTime(prevEndDate.format(DATE_FORMATTER));

            // 查询上一周期的汇总数据（不调整日期范围，因为这是历史对比）
            Map<String, Object> prevResult = forecastMapper.selectForecastSummary(prevForecast);

            if (prevResult == null) {
                return null;
            }

            // 获取两个周期的总用电量
            Object prevTotalObj = prevResult.get("totalElec");
            Object currTotalObj = currentResult.get("totalElec");

            if (prevTotalObj == null || currTotalObj == null) {
                return null;
            }

            double prevTotal = ((Number) prevTotalObj).doubleValue();
            double currTotal = ((Number) currTotalObj).doubleValue();

            // 避免除零错误
            if (prevTotal == 0) {
                return null;
            }

            // 计算环比变化百分比
            double trend = ((currTotal - prevTotal) / prevTotal) * 100;

            // 保留一位小数
            return Math.round(trend * 10) / 10.0;
        }
        catch (Exception e) {
            // 日期解析或计算出错，返回null
            return null;
        }
    }
}