package com.dora.service.impl;

import com.dora.dto.UvStatisticsDto;
import com.dora.service.UvStatisticsService;
import com.dora.util.IpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * UV统计服务实现类
 * 使用Redis存储UV数据，自动7天过期
 * 
 * @author dora
 * @date 2024/12/01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UvStatisticsServiceImpl implements UvStatisticsService {

    private final RedisTemplate<String, String> redisTemplate;

    // Redis Key前缀
    private static final String UV_KEY_PREFIX = "uv:daily:";           // 每日UV统计
    private static final String PV_KEY_PREFIX = "pv:daily:";           // 每日PV统计
    private static final String IP_SET_KEY_PREFIX = "uv:ip_set:";      // 每日IP集合
    private static final String VISITOR_KEY_PREFIX = "uv:visitor:";    // 访客详情

    // 数据过期时间（7天）
    private static final long EXPIRE_DAYS = 7;
    private static final long EXPIRE_SECONDS = EXPIRE_DAYS * 24 * 60 * 60;

    // 日期格式
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    public boolean recordVisit(String ip, String userAgent) {
        if (!StringUtils.hasText(ip) || !IpUtil.isValidIpFormat(ip)) {
            log.warn("无效的IP地址: {}", ip);
            return false;
        }

        try {
            String today = LocalDate.now().format(DATE_FORMATTER);
            String uvKey = UV_KEY_PREFIX + today;
            String pvKey = PV_KEY_PREFIX + today;
            String ipSetKey = IP_SET_KEY_PREFIX + today;
            // 1. 增加PV计数（每次访问都计数）
            redisTemplate.opsForValue().increment(pvKey);
            redisTemplate.expire(pvKey, EXPIRE_SECONDS, TimeUnit.SECONDS);

            // 2. 检查是否为新访客（今日首次访问）
            Long addResult = redisTemplate.opsForSet().add(ipSetKey, ip);
            Boolean isNewVisitor = addResult != null && addResult > 0;
            if (Boolean.TRUE.equals(isNewVisitor)) {
                // 新访客，增加UV计数
                redisTemplate.opsForValue().increment(uvKey);
                redisTemplate.expire(uvKey, EXPIRE_SECONDS, TimeUnit.SECONDS);
                redisTemplate.expire(ipSetKey, EXPIRE_SECONDS, TimeUnit.SECONDS);

                log.info("记录新访客 - IP: {}, 日期: {}", IpUtil.maskIp(ip), today);
            }

            // 3. 更新访客详情信息
            updateVisitorDetail(ip, userAgent, isNewVisitor);

            return Boolean.TRUE.equals(isNewVisitor);

        } catch (Exception e) {
            log.error("记录访问失败 - IP: {}", ip, e);
            return false;
        }
    }

    @Override
    public UvStatisticsDto getUvByDate(LocalDate date) {
        try {
            String dateStr = date.format(DATE_FORMATTER);
            String uvKey = UV_KEY_PREFIX + dateStr;
            String pvKey = PV_KEY_PREFIX + dateStr;
            String ipSetKey = IP_SET_KEY_PREFIX + dateStr;

            // 获取UV和PV数据
            String uvStr = redisTemplate.opsForValue().get(uvKey);
            String pvStr = redisTemplate.opsForValue().get(pvKey);
            
            Long uv = StringUtils.hasText(uvStr) ? Long.valueOf(uvStr) : 0L;
            Long pv = StringUtils.hasText(pvStr) ? Long.valueOf(pvStr) : 0L;

            // 获取IP列表（脱敏）
            Set<String> ipSet = redisTemplate.opsForSet().members(ipSetKey);
            List<String> maskedIpList = new ArrayList<>();
            if (ipSet != null) {
                maskedIpList = ipSet.stream()
                        .map(IpUtil::maskIp)
                        .toList();
            }

            UvStatisticsDto result = new UvStatisticsDto();
            result.setDate(date);
            result.setUv(uv);
            result.setPv(pv);
            result.setNewVisitors(uv); // 简化处理，新访客数等于UV
            result.setIpList(maskedIpList);

            return result;

        } catch (Exception e) {
            log.error("获取UV数据失败 - 日期: {}", date, e);
            return new UvStatisticsDto(date, 0L, 0L, 0L, new ArrayList<>());
        }
    }

    @Override
    public List<UvStatisticsDto> getRecentSevenDaysUv() {
        List<UvStatisticsDto> result = new ArrayList<>();
        
        for (int i = 6; i >= 0; i--) {
            LocalDate date = LocalDate.now().minusDays(i);
            UvStatisticsDto uvData = getUvByDate(date);
            result.add(uvData);
        }

        return result;
    }

    @Override
    public UvStatisticsDto getTodayUv() {
        return getUvByDate(LocalDate.now());
    }

    @Override
    public UvStatisticsDto.UvSummary getUvSummary() {
        try {
            LocalDate today = LocalDate.now();
            LocalDate yesterday = today.minusDays(1);

            // 获取今日和昨日数据
            UvStatisticsDto todayData = getUvByDate(today);
            UvStatisticsDto yesterdayData = getUvByDate(yesterday);

            // 获取最近7天数据
            List<UvStatisticsDto> weeklyData = getRecentSevenDaysUv();

            // 计算汇总数据
            Long weeklyUv = weeklyData.stream().mapToLong(UvStatisticsDto::getUv).sum();
            Long weeklyPv = weeklyData.stream().mapToLong(UvStatisticsDto::getPv).sum();
            Double weeklyAvgUv = weeklyUv / 7.0;

            // 计算增长率
            Double uvGrowthRate = calculateGrowthRate(todayData.getUv(), yesterdayData.getUv());
            Double pvGrowthRate = calculateGrowthRate(todayData.getPv(), yesterdayData.getPv());

            UvStatisticsDto.UvSummary summary = new UvStatisticsDto.UvSummary();
            summary.setTodayUv(todayData.getUv());
            summary.setYesterdayUv(yesterdayData.getUv());
            summary.setWeeklyUv(weeklyUv);
            summary.setWeeklyAvgUv(weeklyAvgUv);
            summary.setTodayPv(todayData.getPv());
            summary.setYesterdayPv(yesterdayData.getPv());
            summary.setWeeklyPv(weeklyPv);
            summary.setUvGrowthRate(uvGrowthRate);
            summary.setPvGrowthRate(pvGrowthRate);
            summary.setWeeklyTrend(weeklyData);

            return summary;

        } catch (Exception e) {
            log.error("获取UV汇总数据失败", e);
            return new UvStatisticsDto.UvSummary();
        }
    }

    @Override
    public void cleanExpiredUvData() {
        try {
            LocalDate expireDate = LocalDate.now().minusDays(EXPIRE_DAYS);
            String dateStr = expireDate.format(DATE_FORMATTER);

            // 删除过期的数据
            String uvKey = UV_KEY_PREFIX + dateStr;
            String pvKey = PV_KEY_PREFIX + dateStr;
            String ipSetKey = IP_SET_KEY_PREFIX + dateStr;

            redisTemplate.delete(uvKey);
            redisTemplate.delete(pvKey);
            redisTemplate.delete(ipSetKey);

            log.info("清理过期UV数据完成 - 日期: {}", dateStr);

        } catch (Exception e) {
            log.error("清理过期UV数据失败", e);
        }
    }

    /**
     * 更新访客详情信息
     */
    private void updateVisitorDetail(String ip, String userAgent, Boolean isNewVisitor) {
        try {
            String visitorKey = VISITOR_KEY_PREFIX + ip;
            String currentTime = String.valueOf(System.currentTimeMillis());

            Map<String, String> visitorInfo = new HashMap<>();
            visitorInfo.put("lastVisitTime", currentTime);
            visitorInfo.put("userAgent", userAgent != null ? userAgent : "");
            visitorInfo.put("location", IpUtil.getIpLocation(ip));

            if (Boolean.TRUE.equals(isNewVisitor)) {
                visitorInfo.put("firstVisitTime", currentTime);
                visitorInfo.put("visitCount", "1");
                visitorInfo.put("isNewVisitor", "true");
            } else {
                // 增加访问次数
                redisTemplate.opsForHash().increment(visitorKey, "visitCount", 1);
            }

            redisTemplate.opsForHash().putAll(visitorKey, visitorInfo);
            redisTemplate.expire(visitorKey, EXPIRE_SECONDS, TimeUnit.SECONDS);

        } catch (Exception e) {
            log.error("更新访客详情失败 - IP: {}", ip, e);
        }
    }

    /**
     * 计算增长率
     */
    private Double calculateGrowthRate(Long current, Long previous) {
        if (previous == null || previous == 0) {
            return current > 0 ? 100.0 : 0.0;
        }
        return ((current - previous) * 100.0) / previous;
    }
}
