package com.dne.ems.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import com.dne.ems.dto.AqiDistributionDTO;
import com.dne.ems.dto.AqiHeatmapPointDTO;
import com.dne.ems.dto.DashboardStatsDTO;
import com.dne.ems.dto.GridCoverageDTO;
import com.dne.ems.dto.HeatmapPointDTO;
import com.dne.ems.dto.PollutantThresholdDTO;
import com.dne.ems.dto.PollutionStatsDTO;
import com.dne.ems.dto.TaskStatsDTO;
import com.dne.ems.dto.TrendDataPointDTO;
import com.dne.ems.exception.ResourceNotFoundException;
import com.dne.ems.model.AqiRecord;
import com.dne.ems.model.Grid;
import com.dne.ems.model.PollutantThreshold;
import com.dne.ems.model.UserAccount;
import com.dne.ems.model.enums.FeedbackStatus;
import com.dne.ems.model.enums.PollutionType;
import com.dne.ems.model.enums.Role;
import com.dne.ems.model.enums.TaskStatus;
import com.dne.ems.repository.AqiDataRepository;
import com.dne.ems.repository.AqiRecordRepository;
import com.dne.ems.repository.FeedbackRepository;
import com.dne.ems.repository.GridRepository;
import com.dne.ems.repository.PollutantThresholdRepository;
import com.dne.ems.repository.TaskRepository;
import com.dne.ems.repository.UserAccountRepository;
import com.dne.ems.security.CustomUserDetails;
import com.dne.ems.service.DashboardService;

import lombok.RequiredArgsConstructor;

/**
 * 仪表盘服务实现类，提供各类环境监测数据的统计和可视化数据
 *
 * <p>实现了 {@link DashboardService} 接口定义的所有方法，包括：
 * <ul>
 *   <li>环境质量指标统计</li>
 *   <li>AQI数据分布和热力图</li>
 *   <li>污染物阈值管理</li>
 *   <li>任务完成情况统计</li>
 *   <li>各类趋势分析数据</li>
 * </ul>
 *
 * @author DNE开发团队
 * @version 1.0
 * @since 2024
 */
@Service
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {

    private final FeedbackRepository feedbackRepository;
    private final UserAccountRepository userAccountRepository;
    private final AqiDataRepository aqiDataRepository;
    private final AqiRecordRepository aqiRecordRepository;
    private final GridRepository gridRepository;
    private final TaskRepository taskRepository;
    private final PollutantThresholdRepository pollutantThresholdRepository;
    private static final Logger log = LoggerFactory.getLogger(DashboardServiceImpl.class);

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>统计总反馈数量</li>
     *   <li>统计已确认反馈数量</li>
     *   <li>统计AQI记录总数</li>
     *   <li>统计活跃网格员数量</li>
     * </ol>
     */
    @Override
    public DashboardStatsDTO getDashboardStats() {
        long totalFeedbacks = feedbackRepository.count();
        long confirmedFeedbacks = feedbackRepository.countByStatus(FeedbackStatus.CONFIRMED);
        long totalAqiRecords = aqiDataRepository.count();
        long activeGridWorkers = userAccountRepository.countByRole(Role.GRID_WORKER);

        return new DashboardStatsDTO(
                totalFeedbacks,
                confirmedFeedbacks,
                totalAqiRecords,
                activeGridWorkers
        );
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>从AQI记录库查询分布数据</li>
     *   <li>处理空结果情况</li>
     * </ol>
     */
    @Override
    public List<AqiDistributionDTO> getAqiDistribution() {
        List<AqiDistributionDTO> distribution = aqiRecordRepository.getAqiDistribution();
        return distribution != null ? distribution : List.of();
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>获取过去12个月的数据</li>
     *   <li>加载污染物阈值设置</li>
     *   <li>查询超标记录</li>
     *   <li>按月份分组统计</li>
     *   <li>填充空缺月份数据</li>
     * </ol>
     */
    @Override
    public List<TrendDataPointDTO> getMonthlyExceedanceTrend() {
        LocalDateTime startDate = LocalDate.now().minusMonths(11).withDayOfMonth(1).atStartOfDay();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");

        // 添加日志记录开始时间
        log.info("获取月度超标趋势数据，开始时间: {}", startDate.format(formatter));
        
        // 获取污染物阈值设置
        List<PollutantThreshold> thresholds = pollutantThresholdRepository.findAll();
        
        // 如果没有阈值设置，创建默认值
        if (thresholds.isEmpty()) {
            log.info("没有找到污染物阈值设置，使用默认值");
            createDefaultThresholds();
            thresholds = pollutantThresholdRepository.findAll();
        }
        
        // 从阈值设置中提取各污染物的阈值
        Double aqiThreshold = 100.0; // 默认AQI阈值
        Double pm25Threshold = getThresholdValue(thresholds, PollutionType.PM25, 75.0);
        Double o3Threshold = getThresholdValue(thresholds, PollutionType.O3, 160.0);
        Double no2Threshold = getThresholdValue(thresholds, PollutionType.NO2, 100.0);
        Double so2Threshold = getThresholdValue(thresholds, PollutionType.SO2, 150.0);
        
        log.info("使用的阈值设置 - AQI: {}, PM25: {}, O3: {}, NO2: {}, SO2: {}",
                aqiThreshold, pm25Threshold, o3Threshold, no2Threshold, so2Threshold);

        // 使用阈值查询超标记录
        List<AqiRecord> records = aqiRecordRepository.findExceedanceRecordsWithThresholds(
                startDate, aqiThreshold, pm25Threshold, o3Threshold, no2Threshold, so2Threshold);
        
        log.info("查询到 {} 条超标记录", records.size());

        // 按月份分组统计
        Map<String, Long> resultsMap = records.stream()
                .collect(Collectors.groupingBy(
                        record -> record.getRecordTime().format(formatter),
                        Collectors.counting()
                ));
        
        log.info("按月份分组后的数据: {}", resultsMap);

        // 生成最终列表，填充空缺的月份
        List<TrendDataPointDTO> result = Stream.iterate(YearMonth.from(startDate), ym -> ym.plusMonths(1))
                .limit(12)
                .map(ym -> {
                    String yearMonthStr = ym.format(formatter);
                    long count = resultsMap.getOrDefault(yearMonthStr, 0L);
                    TrendDataPointDTO dto = new TrendDataPointDTO(yearMonthStr, count);
                    log.debug("生成趋势数据点: {}", dto);
                    return dto;
                })
                .collect(Collectors.toList());
        
        // 添加日志记录最终结果
        log.info("生成了 {} 个月度趋势数据点", result.size());
        if (!result.isEmpty()) {
            log.info("第一个数据点: {}, 最后一个数据点: {}", 
                    result.get(0), result.get(result.size() - 1));
        }
        
        return result;
    }
    
    /**
     * 从阈值设置列表中获取指定污染物的阈值
     * 
     * @param thresholds 阈值设置列表
     * @param pollutionType 污染物类型
     * @param defaultValue 默认值
     * @return 阈值
     */
    private Double getThresholdValue(List<PollutantThreshold> thresholds, PollutionType pollutionType, Double defaultValue) {
        return thresholds.stream()
                .filter(t -> pollutionType.equals(t.getPollutionType()))
                .findFirst()
                .map(PollutantThreshold::getThreshold)
                .orElse(defaultValue);
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>从网格库查询覆盖率数据</li>
     *   <li>处理空结果情况(生成测试数据)</li>
     *   <li>计算每个城市的覆盖率</li>
     * </ol>
     */
    @Override
    public List<GridCoverageDTO> getGridCoverageByCity() {
        // 1. 获取所有网格员及其分配的网格坐标
        Set<String> coveredCoordinates = userAccountRepository.findByRole(Role.GRID_WORKER).stream()
            .filter(worker -> worker.getGridX() != null && worker.getGridY() != null)
            .map(worker -> worker.getGridX() + "," + worker.getGridY())
            .collect(Collectors.toSet());

        // 2. 获取所有网格并按城市分组
        Map<String, List<Grid>> gridsByCity = gridRepository.findAll().stream()
            .filter(grid -> grid.getCityName() != null && !grid.getCityName().isEmpty())
            .collect(Collectors.groupingBy(Grid::getCityName));
            
        // 如果没有数据，可以返回空列表或测试数据
        if (gridsByCity.isEmpty()) {
            log.info("数据库中未找到任何城市的网格数据，无法计算覆盖率。");
            return Collections.emptyList(); // or return test data if needed
        }

        // 3. 计算每个城市的覆盖率
        List<GridCoverageDTO> result = new ArrayList<>();
        for (Map.Entry<String, List<Grid>> entry : gridsByCity.entrySet()) {
            String cityName = entry.getKey();
            List<Grid> cityGrids = entry.getValue();
            
            long totalGrids = cityGrids.size();
            long coveredGrids = cityGrids.stream()
                .filter(grid -> coveredCoordinates.contains(grid.getGridX() + "," + grid.getGridY()))
                .count();
            
            // 计算覆盖率，避免除以零
            double coverageRate = (totalGrids > 0) 
                ? Math.round(((double) coveredGrids / totalGrids) * 10000) / 100.0 // 保留两位小数
                : 0.0;
            
            result.add(new GridCoverageDTO(cityName, coveredGrids, totalGrids, coverageRate));
        }

        // 按覆盖率降序排序
        result.sort((a, b) -> b.coverageRate().compareTo(a.coverageRate()));
        
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>从反馈库查询热力图数据</li>
     *   <li>处理空结果情况(生成测试数据)</li>
     * </ol>
     */
    @Override
    public List<HeatmapPointDTO> getHeatmapData() {
        log.info("开始获取反馈热力图数据...");
        
        // 尝试从数据库获取热力图数据
        List<HeatmapPointDTO> result = feedbackRepository.getHeatmapData();
        
        // 如果没有数据，生成测试数据
        if (result == null || result.isEmpty()) {
            log.info("没有找到反馈热力图数据，创建测试数据");
            
            // 创建测试数据
            List<HeatmapPointDTO> testData = new ArrayList<>();
            for (int x = 0; x < 5; x++) {
                for (int y = 0; y < 5; y++) {
                    // 随机生成不同强度的热力点
                    long intensity = 1 + (long)(Math.random() * 10);
                    testData.add(new HeatmapPointDTO(x, y, intensity));
                }
            }
            
            log.info("已创建 {} 个测试数据点", testData.size());
            return testData;
        }
        
        log.info("成功获取反馈热力图数据，共 {} 个数据点", result.size());
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>按污染类型统计反馈数量</li>
     * </ol>
     */
    @Override
    public List<PollutionStatsDTO> getPollutionStats() {
        return feedbackRepository.countByPollutionType();
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>统计总任务数量</li>
     *   <li>统计已完成任务数量</li>
     *   <li>计算完成率</li>
     * </ol>
     */
    @Override
    public TaskStatsDTO getTaskCompletionStats() {
        long totalTasks = taskRepository.count();
        long completedTasks = taskRepository.countByStatus(TaskStatus.COMPLETED);
        double completionRate = (totalTasks == 0) ? 0 : (double) completedTasks / totalTasks;
        return new TaskStatsDTO(totalTasks, completedTasks, completionRate);
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>检查是否有设置了网格坐标的记录</li>
     *   <li>处理空结果情况(生成测试数据)</li>
     *   <li>尝试JPQL查询</li>
     *   <li>JPQL失败时尝试原生SQL查询</li>
     *   <li>转换查询结果</li>
     * </ol>
     */
    @Override
    public List<AqiHeatmapPointDTO> getAqiHeatmapData() {
        log.info("开始获取AQI热力图数据...");
        
        // 检查是否有设置了网格坐标的记录
        List<AqiRecord> recordsWithGrid = aqiRecordRepository.findAll().stream()
                .filter(r -> r.getGridX() != null && r.getGridY() != null)
                .toList();
        log.info("设置了网格坐标的记录有 {} 条", recordsWithGrid.size());
        
        // 如果没有设置网格坐标的记录，创建一些测试数据
        if (recordsWithGrid.isEmpty()) {
            log.info("没有找到设置了网格坐标的记录，创建测试数据");
            List<AqiRecord> testRecords = new ArrayList<>();
            
            for (int x = 0; x < 5; x++) {
                for (int y = 0; y < 5; y++) {
                    AqiRecord record = new AqiRecord(
                        "测试城市", 50 + x * 10 + y * 5, 
                        LocalDateTime.now(), 
                        10.0, 20.0, 5.0, 15.0, 0.5, 30.0);
                    record.setGridX(x);
                    record.setGridY(y);
                    testRecords.add(record);
                }
            }
            
            aqiRecordRepository.saveAll(testRecords);
            log.info("已创建 {} 条测试数据", testRecords.size());
        }
        
        // 尝试使用 JPQL 查询
        List<AqiHeatmapPointDTO> result = aqiRecordRepository.getAqiHeatmapData();
        
        if (result == null || result.isEmpty()) {
            log.warn("JPQL查询返回空结果，尝试使用原生SQL查询");
            
            // 尝试使用原生 SQL 查询
            List<Object[]> rawData = aqiRecordRepository.getAqiHeatmapDataRaw();
            
            if (rawData == null || rawData.isEmpty()) {
                log.warn("原生SQL查询也返回空结果");
                return List.of(); // 返回空列表
            }
            
            log.info("原生SQL查询成功，获取到 {} 个数据点", rawData.size());
            
            // 转换原生SQL查询结果
            List<AqiHeatmapPointDTO> convertedResult = rawData.stream()
                .map(row -> {
                    try {
                        Integer gridX = row[0] != null ? ((Number) row[0]).intValue() : null;
                        Integer gridY = row[1] != null ? ((Number) row[1]).intValue() : null;
                        Double avgAqi = row[2] != null ? ((Number) row[2]).doubleValue() : null;
                        log.debug("转换数据点: gridX={}, gridY={}, avgAqi={}", gridX, gridY, avgAqi);
                        return new AqiHeatmapPointDTO(gridX, gridY, avgAqi);
                    } catch (Exception e) {
                        log.error("转换数据点时出错: {}", e.getMessage(), e);
                        return null;
                    }
                })
                .filter(dto -> dto != null)
                .collect(Collectors.toList());
            
            log.info("成功转换 {} 个数据点", convertedResult.size());
            return convertedResult;
        }
        
        log.info("JPQL查询成功，获取到 {} 个数据点", result.size());
        return result;
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>查询所有污染物阈值设置</li>
     *   <li>处理空结果情况(创建默认设置)</li>
     *   <li>转换为DTO格式</li>
     * </ol>
     */
    @Override
    public List<PollutantThresholdDTO> getPollutantThresholds() {
        log.info("获取所有污染物阈值设置");
        List<PollutantThreshold> thresholds = pollutantThresholdRepository.findAll();
        
        if (thresholds.isEmpty()) {
            log.info("没有找到污染物阈值设置，创建默认设置");
            createDefaultThresholds();
            thresholds = pollutantThresholdRepository.findAll();
        }
        
        // 转换为DTO
        return thresholds.stream()
                .map(t -> new PollutantThresholdDTO(
                        t.getPollutionType(),
                        t.getPollutantName(),
                        t.getThreshold(),
                        t.getUnit(),
                        t.getDescription()))
                .collect(Collectors.toList());
    }
    
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>按污染物名称查询阈值设置</li>
     *   <li>转换为DTO格式</li>
     * </ol>
     */
    @Override
    public PollutantThresholdDTO getPollutantThreshold(String pollutantName) {
        log.info("获取污染物 {} 的阈值设置", pollutantName);
        return pollutantThresholdRepository.findByPollutantName(pollutantName)
                .map(t -> new PollutantThresholdDTO(
                        t.getPollutionType(),
                        t.getPollutantName(),
                        t.getThreshold(),
                        t.getUnit(),
                        t.getDescription()))
                .orElseGet(() -> {
                    log.info("未找到污染物 {} 的阈值设置，返回null", pollutantName);
                    return null;
                });
    }
    
    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>检查是否已存在相同名称的阈值设置</li>
     *   <li>更新或创建阈值设置</li>
     *   <li>转换为DTO格式返回</li>
     * </ol>
     */
    @Override
    public PollutantThresholdDTO savePollutantThreshold(PollutantThresholdDTO thresholdDTO) {
        log.info("保存污染物 {} 的阈值设置: {}", thresholdDTO.getPollutantName(), thresholdDTO);
        
        // 检查是否已存在
        PollutantThreshold threshold = pollutantThresholdRepository.findByPollutantName(thresholdDTO.getPollutantName())
                .orElse(new PollutantThreshold());
        
        // 更新属性
        threshold.setPollutionType(thresholdDTO.getPollutionType());
        threshold.setPollutantName(thresholdDTO.getPollutantName());
        threshold.setThreshold(thresholdDTO.getThreshold());
        threshold.setUnit(thresholdDTO.getUnit());
        threshold.setDescription(thresholdDTO.getDescription());
        
        // 保存
        PollutantThreshold saved = pollutantThresholdRepository.save(threshold);
        log.info("污染物阈值设置已保存: {}", saved);
        
        // 转换为DTO返回
        return new PollutantThresholdDTO(
                saved.getPollutionType(),
                saved.getPollutantName(),
                saved.getThreshold(),
                saved.getUnit(),
                saved.getDescription());
    }
    
    /**
     * 创建默认的污染物阈值设置
     */
    private void createDefaultThresholds() {
        log.info("创建默认的污染物阈值设置");
        List<PollutantThreshold> defaults = List.of(
                new PollutantThreshold(PollutionType.PM25, 75.0, "µg/m³", "细颗粒物"),
                new PollutantThreshold(PollutionType.O3, 160.0, "µg/m³", "臭氧"),
                new PollutantThreshold(PollutionType.NO2, 100.0, "µg/m³", "二氧化氮"),
                new PollutantThreshold(PollutionType.SO2, 150.0, "µg/m³", "二氧化硫"),
                new PollutantThreshold(PollutionType.OTHER, 100.0, "unit", "其他污染物")
        );
        
        pollutantThresholdRepository.saveAll(defaults);
        log.info("已创建 {} 个默认污染物阈值设置", defaults.size());
    }

    @SuppressWarnings("unused")
    private UserAccount getCurrentUser() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal == null) {
            throw new ResourceNotFoundException("User not found in security context (principal is null).");
        }
        if (principal instanceof CustomUserDetails userDetails) {
            return userDetails.getUserAccount();
        }
        // This case should ideally not happen in a secured context.
        String username = principal.toString();
        return userAccountRepository.findByEmail(username)
                .orElseThrow(() -> new ResourceNotFoundException("User not found in security context: " + username));
    }

    /**
     * {@inheritDoc}
     *
     * <p>实现细节：
     * <ol>
     *   <li>获取过去12个月的AQI记录</li>
     *   <li>为每种污染物类型计算月度平均值</li>
     *   <li>填充空缺月份数据</li>
     * </ol>
     */
    @Override
    public Map<String, List<TrendDataPointDTO>> getPollutantMonthlyTrends() {
        log.info("获取每种污染物的月度趋势数据");
        
        LocalDateTime startDate = LocalDate.now().minusMonths(11).withDayOfMonth(1).atStartOfDay();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        
        // 获取所有AQI记录
        List<AqiRecord> allRecords = aqiRecordRepository.findByRecordTimeAfter(startDate);
        log.info("查询到 {} 条AQI记录", allRecords.size());
        
        // 准备结果Map
        Map<String, List<TrendDataPointDTO>> result = new HashMap<>();
        
        // 为每种污染物类型准备数据
        for (PollutionType pollutionType : PollutionType.values()) {
            String pollutantName = pollutionType.name();
            log.info("处理污染物: {}", pollutantName);
            
            // 按月份分组统计平均值
            Map<String, List<Double>> monthlyValues = new HashMap<>();
            
            // 根据污染物类型获取对应的值
            for (AqiRecord record : allRecords) {
                String monthKey = record.getRecordTime().format(formatter);
                Double value = getPollutantValue(record, pollutionType);
                
                if (value != null) {
                    monthlyValues.computeIfAbsent(monthKey, k -> new ArrayList<>()).add(value);
                }
            }
            
            // 计算每月平均值
            List<TrendDataPointDTO> trendData = Stream.iterate(YearMonth.from(startDate), ym -> ym.plusMonths(1))
                    .limit(12)
                    .map(ym -> {
                        String yearMonthStr = ym.format(formatter);
                        List<Double> values = monthlyValues.getOrDefault(yearMonthStr, Collections.emptyList());
                        
                        // 计算平均值
                        double avgValue = values.isEmpty() ? 0.0 : 
                                values.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
                        
                        // 四舍五入到两位小数
                        long roundedValue = Math.round(avgValue * 100);
                        
                        return new TrendDataPointDTO(yearMonthStr, roundedValue);
                    })
                    .collect(Collectors.toList());
            
            result.put(pollutantName, trendData);
        }
        
        log.info("生成了 {} 种污染物的趋势数据", result.size());
        return result;
    }
    
    /**
     * 根据污染物类型获取对应的值
     * 
     * @param record AQI记录
     * @param pollutionType 污染物类型
     * @return 污染物值
     */
    private Double getPollutantValue(AqiRecord record, PollutionType pollutionType) {
        return switch (pollutionType) {
            case PM25 -> record.getPm25();
            case O3 -> record.getO3();
            case NO2 -> record.getNo2();
            case SO2 -> record.getSo2();
            case OTHER -> null;
        };
    }
}