package com.kt.event.analytics.service;

import com.kt.event.analytics.dto.response.*;
import com.kt.event.analytics.entity.ChannelStats;
import com.kt.event.analytics.entity.EventStats;
import com.kt.event.analytics.repository.ChannelStatsRepository;
import com.kt.event.analytics.repository.EventStatsRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * User Analytics Service
 *
 * 매장(사용자) 전체 이벤트의 통합 성과 대시보드를 제공하는 서비스
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserAnalyticsService {

    private final EventStatsRepository eventStatsRepository;
    private final ChannelStatsRepository channelStatsRepository;
    private final ROICalculator roiCalculator;
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    private static final String CACHE_KEY_PREFIX = "analytics:user:dashboard:";
    private static final long CACHE_TTL = 1800; // 30분 (여러 이벤트 통합이므로 짧게)

    /**
     * 사용자 전체 대시보드 데이터 조회
     *
     * @param userId    사용자 ID
     * @param startDate 조회 시작 날짜 (선택)
     * @param endDate   조회 종료 날짜 (선택)
     * @param refresh   캐시 갱신 여부
     * @return 사용자 통합 대시보드 응답
     */
    public UserAnalyticsDashboardResponse getUserDashboardData(String userId, LocalDateTime startDate, LocalDateTime endDate, boolean refresh) {
        log.info("사용자 전체 대시보드 데이터 조회 시작: userId={}, refresh={}", userId, refresh);

        String cacheKey = CACHE_KEY_PREFIX + userId;

        // 1. Redis 캐시 조회 (refresh가 false일 때만)
        if (!refresh) {
            String cachedData = redisTemplate.opsForValue().get(cacheKey);
            if (cachedData != null) {
                try {
                    log.info("✅ 캐시 HIT: {}", cacheKey);
                    return objectMapper.readValue(cachedData, UserAnalyticsDashboardResponse.class);
                } catch (JsonProcessingException e) {
                    log.warn("캐시 데이터 역직렬화 실패: {}", e.getMessage());
                }
            }
        }

        // 2. 캐시 MISS: 데이터 조회 및 통합
        log.info("캐시 MISS 또는 refresh=true: PostgreSQL 조회");

        // 2-1. 사용자의 모든 이벤트 조회
        List<EventStats> allEvents = eventStatsRepository.findAllByUserId(userId);
        if (allEvents.isEmpty()) {
            log.warn("사용자에 이벤트가 없음: userId={}", userId);
            return buildEmptyResponse(userId, startDate, endDate);
        }

        log.debug("사용자 이벤트 조회 완료: userId={}, 이벤트 수={}", userId, allEvents.size());

        // 2-2. 모든 이벤트의 채널 통계 조회
        List<String> eventIds = allEvents.stream()
                .map(EventStats::getEventId)
                .collect(Collectors.toList());
        List<ChannelStats> allChannelStats = channelStatsRepository.findByEventIdIn(eventIds);

        // 3. 통합 대시보드 데이터 구성
        UserAnalyticsDashboardResponse response = buildUserDashboardData(userId, allEvents, allChannelStats, startDate, endDate);

        // 4. Redis 캐싱 (30분 TTL)
        try {
            String jsonData = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(cacheKey, jsonData, CACHE_TTL, TimeUnit.SECONDS);
            log.info("✅ Redis 캐시 저장 완료: {} (TTL: 30분)", cacheKey);
        } catch (Exception e) {
            log.warn("캐시 저장 실패 (무시하고 계속 진행): {}", e.getMessage());
        }

        return response;
    }

    /**
     * 빈 응답 생성 (이벤트가 없는 경우)
     */
    private UserAnalyticsDashboardResponse buildEmptyResponse(String userId, LocalDateTime startDate, LocalDateTime endDate) {
        return UserAnalyticsDashboardResponse.builder()
                .userId(userId)
                .period(buildPeriodInfo(startDate, endDate))
                .totalEvents(0)
                .activeEvents(0)
                .overallSummary(buildEmptyAnalyticsSummary())
                .channelPerformance(new ArrayList<>())
                .overallRoi(buildEmptyRoiSummary())
                .eventPerformances(new ArrayList<>())
                .lastUpdatedAt(LocalDateTime.now())
                .dataSource("empty")
                .build();
    }

    /**
     * 사용자 통합 대시보드 데이터 구성
     */
    private UserAnalyticsDashboardResponse buildUserDashboardData(String userId, List<EventStats> allEvents,
                                                                   List<ChannelStats> allChannelStats,
                                                                   LocalDateTime startDate, LocalDateTime endDate) {
        // 기간 정보
        PeriodInfo period = buildPeriodInfo(startDate, endDate);

        // 전체 이벤트 수 및 활성 이벤트 수
        int totalEvents = allEvents.size();
        long activeEvents = allEvents.stream()
                .filter(e -> "ACTIVE".equalsIgnoreCase(e.getStatus()) || "RUNNING".equalsIgnoreCase(e.getStatus()))
                .count();

        // 전체 성과 요약 (모든 이벤트 통합)
        AnalyticsSummary overallSummary = buildOverallSummary(allEvents, allChannelStats);

        // 채널별 성과 요약 (모든 이벤트 통합)
        List<ChannelSummary> channelPerformance = buildAggregatedChannelPerformance(allChannelStats, allEvents);

        // 전체 ROI 요약
        RoiSummary overallRoi = calculateOverallRoi(allEvents);

        // 이벤트별 성과 목록
        List<UserAnalyticsDashboardResponse.EventPerformanceSummary> eventPerformances = buildEventPerformances(allEvents);

        return UserAnalyticsDashboardResponse.builder()
                .userId(userId)
                .period(period)
                .totalEvents(totalEvents)
                .activeEvents((int) activeEvents)
                .overallSummary(overallSummary)
                .channelPerformance(channelPerformance)
                .overallRoi(overallRoi)
                .eventPerformances(eventPerformances)
                .lastUpdatedAt(LocalDateTime.now())
                .dataSource("cached")
                .build();
    }

    /**
     * 전체 성과 요약 계산 (모든 이벤트 통합)
     */
    private AnalyticsSummary buildOverallSummary(List<EventStats> allEvents, List<ChannelStats> allChannelStats) {
        int totalParticipants = allEvents.stream()
                .mapToInt(EventStats::getTotalParticipants)
                .sum();

        int totalViews = allEvents.stream()
                .mapToInt(EventStats::getTotalViews)
                .sum();

        BigDecimal totalInvestment = allEvents.stream()
                .map(EventStats::getTotalInvestment)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalExpectedRevenue = allEvents.stream()
                .map(EventStats::getExpectedRevenue)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 평균 참여율 계산
        double avgEngagementRate = totalViews > 0 ? (double) totalParticipants / totalViews * 100 : 0.0;

        // 평균 전환율 계산 (채널 통계 기반)
        int totalConversions = allChannelStats.stream()
                .mapToInt(ChannelStats::getConversions)
                .sum();
        double avgConversionRate = totalParticipants > 0 ? (double) totalConversions / totalParticipants * 100 : 0.0;

        return AnalyticsSummary.builder()
                .participants(totalParticipants)
                .participantsDelta(0) // TODO: 이전 기간 데이터와 비교하여 계산
                .totalViews(totalViews)
                .engagementRate(Math.round(avgEngagementRate * 10) / 10.0)
                .conversionRate(Math.round(avgConversionRate * 10) / 10.0)
                .build();
    }

    /**
     * 채널별 성과 통합 (모든 이벤트의 채널 데이터 집계)
     */
    private List<ChannelSummary> buildAggregatedChannelPerformance(List<ChannelStats> allChannelStats, List<EventStats> allEvents) {
        if (allChannelStats.isEmpty()) {
            return new ArrayList<>();
        }

        BigDecimal totalInvestment = allEvents.stream()
                .map(EventStats::getTotalInvestment)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 채널명별로 그룹화하여 집계
        Map<String, List<ChannelStats>> channelGroups = allChannelStats.stream()
                .collect(Collectors.groupingBy(ChannelStats::getChannelName));

        return channelGroups.entrySet().stream()
                .map(entry -> {
                    String channelName = entry.getKey();
                    List<ChannelStats> channelList = entry.getValue();

                    int participants = channelList.stream().mapToInt(ChannelStats::getParticipants).sum();
                    int views = channelList.stream().mapToInt(ChannelStats::getViews).sum();
                    double engagementRate = views > 0 ? (double) participants / views * 100 : 0.0;

                    BigDecimal channelCost = channelList.stream()
                            .map(ChannelStats::getDistributionCost)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

                    double channelRoi = channelCost.compareTo(BigDecimal.ZERO) > 0
                            ? (participants - channelCost.doubleValue()) / channelCost.doubleValue() * 100
                            : 0.0;

                    return ChannelSummary.builder()
                            .channel(channelName)
                            .participants(participants)
                            .views(views)
                            .engagementRate(Math.round(engagementRate * 10) / 10.0)
                            .roi(Math.round(channelRoi * 10) / 10.0)
                            .build();
                })
                .sorted(Comparator.comparingInt(ChannelSummary::getParticipants).reversed())
                .collect(Collectors.toList());
    }

    /**
     * 전체 ROI 계산
     */
    private RoiSummary calculateOverallRoi(List<EventStats> allEvents) {
        BigDecimal totalInvestment = allEvents.stream()
                .map(EventStats::getTotalInvestment)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalExpectedRevenue = allEvents.stream()
                .map(EventStats::getExpectedRevenue)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalProfit = totalExpectedRevenue.subtract(totalInvestment);

        Double roi = totalInvestment.compareTo(BigDecimal.ZERO) > 0
                ? totalProfit.divide(totalInvestment, 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100))
                .doubleValue()
                : 0.0;

        return RoiSummary.builder()
                .totalCost(totalInvestment)
                .expectedRevenue(totalExpectedRevenue)
                .netProfit(totalProfit)
                .roi(Math.round(roi * 10) / 10.0)
                .build();
    }

    /**
     * 이벤트별 성과 목록 생성
     */
    private List<UserAnalyticsDashboardResponse.EventPerformanceSummary> buildEventPerformances(List<EventStats> allEvents) {
        return allEvents.stream()
                .map(event -> {
                    Double roi = event.getTotalInvestment().compareTo(BigDecimal.ZERO) > 0
                            ? event.getExpectedRevenue().subtract(event.getTotalInvestment())
                            .divide(event.getTotalInvestment(), 4, RoundingMode.HALF_UP)
                            .multiply(BigDecimal.valueOf(100))
                            .doubleValue()
                            : 0.0;

                    return UserAnalyticsDashboardResponse.EventPerformanceSummary.builder()
                            .eventId(event.getEventId())
                            .eventTitle(event.getEventTitle())
                            .participants(event.getTotalParticipants())
                            .views(event.getTotalViews())
                            .roi(Math.round(roi * 10) / 10.0)
                            .status(event.getStatus())
                            .build();
                })
                .sorted(Comparator.comparingInt(UserAnalyticsDashboardResponse.EventPerformanceSummary::getParticipants).reversed())
                .collect(Collectors.toList());
    }

    /**
     * 기간 정보 구성
     */
    private PeriodInfo buildPeriodInfo(LocalDateTime startDate, LocalDateTime endDate) {
        LocalDateTime start = startDate != null ? startDate : LocalDateTime.now().minusDays(30);
        LocalDateTime end = endDate != null ? endDate : LocalDateTime.now();
        long durationDays = ChronoUnit.DAYS.between(start, end);

        return PeriodInfo.builder()
                .startDate(start)
                .endDate(end)
                .durationDays((int) durationDays)
                .build();
    }

    /**
     * 빈 성과 요약
     */
    private AnalyticsSummary buildEmptyAnalyticsSummary() {
        return AnalyticsSummary.builder()
                .participants(0)
                .participantsDelta(0)
                .totalViews(0)
                .engagementRate(0.0)
                .conversionRate(0.0)
                .build();
    }

    /**
     * 빈 ROI 요약
     */
    private RoiSummary buildEmptyRoiSummary() {
        return RoiSummary.builder()
                .totalCost(BigDecimal.ZERO)
                .expectedRevenue(BigDecimal.ZERO)
                .netProfit(BigDecimal.ZERO)
                .roi(0.0)
                .build();
    }
}
