package com.centreproject.system.service.guestExperience.impl;


import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.centreproject.common.constant.GuestConstants;
import com.centreproject.common.constant.KeyConstants;
import com.centreproject.common.utils.DateUtils;
import com.centreproject.common.websocket.MyWebSocketHandler;
import com.centreproject.system.domain.kpi.guestExperience.KpiParkGuestExperience;
import com.centreproject.system.domain.kpi.park.ParkTicketType;
import com.centreproject.system.mapper.kpi.KpiParkGuestExperienceMapper;
import com.centreproject.system.service.guestExperience.IKpiParkGuestExperienceService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *
 * @author lenovo
 */
@Service
public class KpiParkGuestExperienceServiceImpl extends ServiceImpl<KpiParkGuestExperienceMapper, KpiParkGuestExperience>
        implements IKpiParkGuestExperienceService {

    private Logger logger = LoggerFactory.getLogger(KpiParkGuestExperienceServiceImpl.class);
    @Autowired
    private KpiParkGuestExperienceMapper mapper;

    @Autowired
    private RedisTemplate<String, List<KpiParkGuestExperience>> redisTemplate;
    // WebSocket消息发送器
    @Autowired
    private MyWebSocketHandler myWebSocketHandler;

    /**
     * Happiness_Meters last 60 days
     * @param parkCode park_code
     * @return Happiness_Meters last 60 days kpi data
     */
    @Override
    public List<KpiParkGuestExperience> getHappinessMeters(String parkCode) {
        String redisKey = KeyConstants.HAPPINESS_METERS + DateUtils.getRiDate() + parkCode+ GuestConstants.LATEST_60_DAYS;

        // 1. 查缓存
        List<KpiParkGuestExperience> cached =  redisTemplate.opsForValue().get(redisKey);
        if (CollectionUtils.isNotEmpty(cached)) {
            return cached;
        }

        // 2. 根据类型处理数据
        List<KpiParkGuestExperience> results= mapper.getHappinessMetersLast60Days(parkCode);


        // 3. 写缓存（仅60天数据需要缓存）
        if (CollectionUtils.isNotEmpty(results)) {
            redisTemplate.opsForValue().set(redisKey, results, 1, TimeUnit.HOURS);
        }
        return results;
    }

    /**
     * Queue_Time last 60 days
     * @param parkCode park_code
     * @return Queue_Time last 60 days kpi data
     */
    @Override
    public List<KpiParkGuestExperience> getQueueTime(String parkCode) {
        String redisKey = KeyConstants.QUEUE_TIME + DateUtils.getRiDate() + parkCode + GuestConstants.LATEST_60_DAYS;

        // 1. 查缓存
        List<KpiParkGuestExperience> cached =  redisTemplate.opsForValue().get(redisKey);
        if (CollectionUtils.isNotEmpty(cached)) {
            return cached;
        }

        // 2. 根据类型处理数据
        List<KpiParkGuestExperience> results= mapper.getQueueTimeLast60Days(parkCode);


        // 3. 写缓存（仅60天数据需要缓存）
        if (CollectionUtils.isNotEmpty(results)) {
            redisTemplate.opsForValue().set(redisKey, results, 1, TimeUnit.HOURS);
        }
        return results;
    }

/******************************  定时模拟数据推送  *********************************/

    /**
     * 模拟定时任务往websocket发送消息 可能调度频率会不同，所以分开推送
     * 定时推送最新数据
     * Happiness_Meters daily data
     */
    @Scheduled(fixedRate = 30000) // 每30秒执行一次
    public void fetchAndPushDataByHappiness() {
        try {
            // 1. 从数仓获取最新数据（示例）
            //    取当天的数据，再分组推送
            List<KpiParkGuestExperience> latestData = mapper.getHappinessMetersDailyData();
            // 取前一天的数据，再分组推送
            List<KpiParkGuestExperience> preData = mapper.getPreHappinessMetersDailyData();
            Map<String, List<KpiParkGuestExperience>> map =
                    preData.stream().collect(Collectors.groupingBy(KpiParkGuestExperience::getParkCode));
            if(CollectionUtils.isNotEmpty(latestData)) {
                // 2. 按园区(park_code)分组并排序
                Map<String, List<KpiParkGuestExperience>> groupedData = latestData.stream()
                        .collect(Collectors.groupingBy(
                                KpiParkGuestExperience::getParkCode,
                                Collectors.collectingAndThen(
                                        Collectors.toList(),
                                        list -> list.stream()
                                                .sorted(Comparator.comparing(KpiParkGuestExperience::getHappinessMeters).reversed())
                                                .collect(Collectors.toList())
                                )
                        ));

                // 3. 按园区分组推送
                groupedData.forEach((parkCode, parkData) -> {
                    List<KpiParkGuestExperience> preParkData = map.get(parkCode);
                    BigDecimal currentHappiness = parkData.stream()
                            .map(KpiParkGuestExperience::getHappinessMeters)
                            .filter(Objects::nonNull)
                            .collect(Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    list -> list.isEmpty() ? BigDecimal.ZERO :
                                            list.stream()
                                                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                                                    .divide(new BigDecimal(list.size()), 2, RoundingMode.HALF_UP)
                            ));
                    BigDecimal preHappiness=BigDecimal.ZERO;
                    if(CollectionUtils.isNotEmpty(preParkData)) {
                        preHappiness= preParkData.stream()
                                .map(KpiParkGuestExperience::getHappinessMeters)
                                .filter(Objects::nonNull)
                                .collect(Collectors.collectingAndThen(
                                        Collectors.toList(),
                                        list -> list.isEmpty() ? BigDecimal.ZERO :
                                                list.stream()
                                                        .reduce(BigDecimal.ZERO, BigDecimal::add)
                                                        .divide(new BigDecimal(list.size()), 2, RoundingMode.HALF_UP)
                                ));
                    }
                    // 构建每个园区的独立消息
                    Map<String, Object> message = new HashMap<>();
                    message.put("module", KeyConstants.PARK_HAPPINESS_METERS+parkCode);
                    message.put("data", parkData);
                    message.put("value",currentHappiness);
                    message.put("preValue",currentHappiness.subtract(preHappiness));
                    message.put("timestamp", System.currentTimeMillis());
                    // 转换为JSON并推送
                    String jsonData = JSON.toJSONString(message);
                    myWebSocketHandler.broadcast(jsonData);
                    logger.info("模块{}园区[{}]数据推送成功，项目数量: {}",KeyConstants.PARK_HAPPINESS_METERS, parkCode, parkData.size());
                });
            }

        } catch (Exception e) {
            logger.error("数据获取或推送异常", e);
        }
    }

    /**
     * Queue_Time daily data
     */
    @Scheduled(fixedRate = 30000) // 每30秒执行一次
    public void fetchAndPushDataByQueueTime() {
        try {
            // 1. 从数仓获取最新数据（示例）
            //    取当天的数据，再分组推送
            List<KpiParkGuestExperience> latestData = mapper.getQueueTimeDailyData();
            if(CollectionUtils.isNotEmpty(latestData)) {
                // 2. 按园区(park_code)分组并排序
                Map<String, List<KpiParkGuestExperience>> groupedData = latestData.stream()
                        .collect(Collectors.groupingBy(
                                KpiParkGuestExperience::getParkCode,
                                Collectors.collectingAndThen(
                                        Collectors.toList(),
                                        list -> list.stream()
                                                .sorted(Comparator.comparing(KpiParkGuestExperience::getQueueTime).reversed())
                                                .collect(Collectors.toList())
                                )
                        ));

                // 3. 按园区分组推送
                groupedData.forEach((parkCode, parkData) -> {
                    // 构建每个园区的独立消息
                    // 平均排队时间
                    BigDecimal avgQueueTime = parkData.stream()
                            .map(KpiParkGuestExperience::getQueueTime)
                            .filter(Objects::nonNull)
                            .collect(Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    list -> list.isEmpty() ? BigDecimal.ZERO :
                                            list.stream()
                                                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                                                    .divide(new BigDecimal(list.size()), 2, RoundingMode.HALF_UP)
                            ));
                    // 构建每个园区的独立消息
                    Map<String, Object> message = new HashMap<>();
                    message.put("module", KeyConstants.PARK_QUEUE_TIME+parkCode);
                    message.put("value",avgQueueTime);
                    message.put("data", parkData);
                    message.put("timestamp", System.currentTimeMillis());
                    // 转换为JSON并推送
                    String jsonData = JSON.toJSONString(message);
                    myWebSocketHandler.broadcast(jsonData);
                    logger.info("模块{}园区[{}]数据推送成功，项目数量: {}",KeyConstants.PARK_QUEUE_TIME, parkCode, parkData.size());
                });
            }

        } catch (Exception e) {
            logger.error("数据获取或推送异常", e);
        }
    }
}
