package com.campus.activity.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.campus.activity.common.ResultCode;
import com.campus.activity.entity.Activity;
import com.campus.activity.entity.CheckIn;
import com.campus.activity.entity.Registration;
import com.campus.activity.entity.Review;
import com.campus.activity.exception.BusinessException;
import com.campus.activity.mapper.ActivityMapper;
import com.campus.activity.mapper.CheckInMapper;
import com.campus.activity.mapper.RegistrationMapper;
import com.campus.activity.mapper.ReviewMapper;
import com.campus.activity.service.StatisticsService;
import com.campus.activity.util.CsvUtil;
import com.campus.activity.vo.ActivityStatisticsVO;
import com.campus.activity.vo.OverviewStatisticsVO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class StatisticsServiceImpl implements StatisticsService {

    private final RegistrationMapper registrationMapper;
    private final CheckInMapper checkInMapper;
    private final ReviewMapper reviewMapper;
    private final ActivityMapper activityMapper;
    private final CsvUtil csvUtil;

    @Override
    public ActivityStatisticsVO getActivityStatistics(Long activityId) {
        Activity activity = activityMapper.selectById(activityId);
        if (activity == null || activity.getDeleted() == 1) {
            throw new BusinessException(ResultCode.ACTIVITY_NOT_FOUND);
        }

        Long registeredCount = registrationMapper.selectCount(
                new LambdaQueryWrapper<Registration>()
                        .eq(Registration::getActivityId, activityId)
                        .in(Registration::getStatus, 1, 2)
        );

        List<CheckIn> checkIns = checkInMapper.selectList(
                new LambdaQueryWrapper<CheckIn>()
                        .eq(CheckIn::getActivityId, activityId)
        );

        Long checkedInCount = (long) checkIns.size();

        Long reviewCount = reviewMapper.selectCount(
                new LambdaQueryWrapper<Review>()
                        .eq(Review::getActivityId, activityId)
        );

        List<Review> reviews = reviewMapper.selectList(
                new LambdaQueryWrapper<Review>()
                        .eq(Review::getActivityId, activityId)
        );

        Double averageRating = reviews.isEmpty() ? 0.0 :
                reviews.stream().mapToInt(Review::getRating).average().orElse(0.0);

        Map<Integer, Long> ratingDistribution = reviews.stream()
                .collect(Collectors.groupingBy(Review::getRating, Collectors.counting()));

        for (int i = 1; i <= 5; i++) {
            ratingDistribution.putIfAbsent(i, 0L);
        }

        Double checkInRate = registeredCount > 0 ?
                (double) checkedInCount / registeredCount : 0.0;

        return ActivityStatisticsVO.builder()
                .activityId(activityId)
                .activityTitle(activity.getTitle())
                .registeredCount(registeredCount)
                .checkedInCount(checkedInCount)
                .checkInRate(Math.round(checkInRate * 100.0) / 100.0)
                .reviewCount(reviewCount)
                .averageRating(Math.round(averageRating * 10.0) / 10.0)
                .ratingDistribution(ratingDistribution)
                .build();
    }

    @Override
    public ResponseEntity<byte[]> exportActivityData(Long activityId, String format) {
        Activity activity = activityMapper.selectById(activityId);
        if (activity == null || activity.getDeleted() == 1) {
            throw new BusinessException(ResultCode.ACTIVITY_NOT_FOUND);
        }

        byte[] csvBytes = csvUtil.exportActivityData(activity);

        String filename = "activity_" + activityId + "_statistics.csv";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
                .contentType(MediaType.parseMediaType("text/csv; charset=UTF-8"))
                .contentLength(csvBytes.length)
                .body(csvBytes);
    }

    @Override
    public OverviewStatisticsVO getOverviewStatistics() {
        Long totalActivities = activityMapper.selectCount(
                new LambdaQueryWrapper<Activity>()
                        .eq(Activity::getDeleted, 0)
        );

        Long totalRegistrations = registrationMapper.selectCount(
                new LambdaQueryWrapper<Registration>()
                        .eq(Registration::getStatus, 1)
        );

        Long totalCheckIns = checkInMapper.selectCount(null);

        Long totalReviews = reviewMapper.selectCount(null);

        List<Review> allReviews = reviewMapper.selectList(null);
        Double averageRating = allReviews.isEmpty() ? 0.0 :
                allReviews.stream().mapToInt(Review::getRating).average().orElse(0.0);

        List<OverviewStatisticsVO.MonthlyStats> monthlyStats = calculateMonthlyStats();

        return OverviewStatisticsVO.builder()
                .totalActivities(totalActivities)
                .totalRegistrations(totalRegistrations)
                .totalCheckIns(totalCheckIns)
                .totalReviews(totalReviews)
                .averageRating(Math.round(averageRating * 10.0) / 10.0)
                .monthlyStats(monthlyStats)
                .build();
    }

    private List<OverviewStatisticsVO.MonthlyStats> calculateMonthlyStats() {
        List<OverviewStatisticsVO.MonthlyStats> stats = new ArrayList<>();

        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");

        for (int i = 5; i >= 0; i--) {
            LocalDateTime monthStart = now.minusMonths(i).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
            LocalDateTime monthEnd = monthStart.plusMonths(1).minusSeconds(1);
            String monthKey = monthStart.format(formatter);

            Long activityCount = activityMapper.selectCount(
                    new LambdaQueryWrapper<Activity>()
                            .eq(Activity::getDeleted, 0)
                            .between(Activity::getCreatedAt, monthStart, monthEnd)
            );

            Long registrationCount = registrationMapper.selectCount(
                    new LambdaQueryWrapper<Registration>()
                            .eq(Registration::getStatus, 1)
                            .between(Registration::getCreatedAt, monthStart, monthEnd)
            );

            stats.add(OverviewStatisticsVO.MonthlyStats.builder()
                    .month(monthKey)
                    .activityCount(activityCount)
                    .registrationCount(registrationCount)
                    .build());
        }

        return stats;
    }
}