package com.mosquito.project.job;

import com.mosquito.project.domain.Activity;
import com.mosquito.project.domain.DailyActivityStats;
import com.mosquito.project.service.ActivityService;
import com.mosquito.project.persistence.entity.DailyActivityStatsEntity;
import com.mosquito.project.persistence.repository.DailyActivityStatsRepository;
import com.mosquito.project.persistence.repository.LinkClickRepository;
import com.mosquito.project.persistence.repository.UserInviteRepository;
import com.mosquito.project.persistence.repository.UserRewardRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class StatisticsAggregationJob {

    private static final Logger log = LoggerFactory.getLogger(StatisticsAggregationJob.class);

    private final ActivityService activityService;
    private final DailyActivityStatsRepository dailyStatsRepository;
    private final LinkClickRepository linkClickRepository;
    private final UserInviteRepository userInviteRepository;
    private final UserRewardRepository userRewardRepository;
    private final Map<Long, DailyActivityStats> dailyStats = new ConcurrentHashMap<>();

    public StatisticsAggregationJob(ActivityService activityService,
                                    DailyActivityStatsRepository dailyStatsRepository,
                                    LinkClickRepository linkClickRepository,
                                    UserInviteRepository userInviteRepository,
                                    UserRewardRepository userRewardRepository) {
        this.activityService = activityService;
        this.dailyStatsRepository = dailyStatsRepository;
        this.linkClickRepository = linkClickRepository;
        this.userInviteRepository = userInviteRepository;
        this.userRewardRepository = userRewardRepository;
    }

    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
    public void aggregateDailyStats() {
        log.info("开始执行每日活动数据聚合任务");
        List<Activity> activities = activityService.getAllActivities();
        LocalDate yesterday = LocalDate.now().minusDays(1);

        for (Activity activity : activities) {
            // 从真实事件表聚合数据
            DailyActivityStats stats = aggregateStatsForActivity(activity, yesterday);
            // 持久化到统计表
            upsertDailyStats(stats);
            log.info("为活动ID {} 聚合了真实数据: {} 次浏览, {} 次分享, {} 次新注册, {} 次转化",
                activity.getId(), stats.getViews(), stats.getShares(), stats.getNewRegistrations(), stats.getConversions());
        }
        log.info("每日活动数据聚合任务执行完成");
    }

    @Scheduled(cron = "0 0 * * * ?") // 每小时执行一次，检查过期活动
    public void processExpiredActivities() {
        log.info("开始执行活动过期检查任务");
        int processedCount = activityService.processExpiredActivities();
        log.info("活动过期检查任务执行完成，共处理 {} 个过期活动", processedCount);
    }

    /**
     * 从真实事件表聚合统计数据
     * 不再使用随机数模拟
     */
    public DailyActivityStats aggregateStatsForActivity(Activity activity, LocalDate date) {
        DailyActivityStats stats = new DailyActivityStats();
        stats.setActivityId(activity.getId());
        stats.setStatDate(date);

        // 转换日期范围
        OffsetDateTime startOfDay = date.atStartOfDay().atOffset(ZoneOffset.UTC);
        OffsetDateTime endOfDay = date.atTime(LocalTime.MAX).atOffset(ZoneOffset.UTC);

        // 从 link_clicks 表聚合：PV/UV/分享数
        // PV: 总浏览次数（按日期范围）
        long views = linkClickRepository.countViewsByActivityIdAndDateRange(
            activity.getId(), startOfDay, endOfDay);
        stats.setViews((int) views);

        // UV: 按IP去重统计（独立访客数）
        long uniqueVisitors = linkClickRepository.countUniqueVisitorsByActivityIdAndDateRange(
            activity.getId(), startOfDay, endOfDay);
        stats.setUniqueVisitors((int) uniqueVisitors);

        // 分享数: 统计有inviterUserId的记录（有效归因的分享点击）
        long shares = linkClickRepository.countSharesByActivityIdAndDateRange(
            activity.getId(), startOfDay, endOfDay);
        stats.setShares((int) shares);

        // 新注册: 从 user_invites 表统计当天创建的邀请记录
        List<?> invites = userInviteRepository.findByActivityId(activity.getId());
        int newRegistrations = (int) invites.stream()
            .filter(i -> {
                try {
                    Object createdAt = i.getClass().getMethod("getCreatedAt").invoke(i);
                    if (createdAt instanceof OffsetDateTime) {
                        OffsetDateTime dt = (OffsetDateTime) createdAt;
                        return dt.toLocalDate().equals(date);
                    }
                } catch (Exception e) {
                    // ignore
                }
                return false;
            })
            .count();
        stats.setNewRegistrations(newRegistrations);

        // 转化: 从 user_rewards 表统计当天发放的奖励
        List<?> rewards = userRewardRepository.findByActivityId(activity.getId());
        int conversions = (int) rewards.stream()
            .filter(r -> {
                try {
                    Object createdAt = r.getClass().getMethod("getCreatedAt").invoke(r);
                    if (createdAt instanceof OffsetDateTime) {
                        OffsetDateTime dt = (OffsetDateTime) createdAt;
                        return dt.toLocalDate().equals(date);
                    }
                } catch (Exception e) {
                    // ignore
                }
                return false;
            })
            .count();
        stats.setConversions(conversions);

        dailyStats.put(activity.getId(), stats);
        // 持久化
        upsertDailyStats(stats);
        return stats;
    }

    private void upsertDailyStats(DailyActivityStats stats) {
        DailyActivityStatsEntity entity = dailyStatsRepository
            .findByActivityIdAndStatDate(stats.getActivityId(), stats.getStatDate())
            .orElseGet(DailyActivityStatsEntity::new);
        entity.setActivityId(stats.getActivityId());
        entity.setStatDate(stats.getStatDate());
        entity.setViews(stats.getViews());
        entity.setUniqueVisitors(stats.getUniqueVisitors());
        entity.setShares(stats.getShares());
        entity.setNewRegistrations(stats.getNewRegistrations());
        entity.setConversions(stats.getConversions());
        dailyStatsRepository.save(entity);
    }
}
