package com.kt.event.analytics.service;

import com.kt.event.analytics.dto.response.*;
import com.kt.event.analytics.entity.TimelineData;
import com.kt.event.analytics.repository.TimelineDataRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 시간대별 분석 Service
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TimelineAnalyticsService {

    private final TimelineDataRepository timelineDataRepository;

    /**
     * 시간대별 참여 추이 조회
     */
    public TimelineAnalyticsResponse getTimelineAnalytics(String eventId, String interval,
                                                           LocalDateTime startDate, LocalDateTime endDate,
                                                           List<String> metrics) {
        log.info("시간대별 참여 추이 조회: eventId={}, interval={}", eventId, interval);

        // 시간대별 데이터 조회
        List<TimelineData> timelineDataList;
        if (startDate != null && endDate != null) {
            timelineDataList = timelineDataRepository.findByEventIdAndTimestampBetween(eventId, startDate, endDate);
        } else {
            timelineDataList = timelineDataRepository.findByEventIdOrderByTimestampAsc(eventId);
        }

        // 시간대별 데이터 포인트 구성
        List<TimelineDataPoint> dataPoints = buildTimelineDataPoints(timelineDataList);

        // 추세 분석
        TrendAnalysis trends = buildTrendAnalysis(dataPoints);

        // 피크 타임 분석
        List<PeakTimeInfo> peakTimes = buildPeakTimes(dataPoints);

        return TimelineAnalyticsResponse.builder()
            .eventId(eventId)
            .interval(interval != null ? interval : "daily")
            .dataPoints(dataPoints)
            .trends(trends)
            .peakTimes(peakTimes)
            .lastUpdatedAt(LocalDateTime.now())
            .build();
    }

    /**
     * 시간대별 데이터 포인트 구성
     */
    private List<TimelineDataPoint> buildTimelineDataPoints(List<TimelineData> timelineDataList) {
        return timelineDataList.stream()
            .map(data -> TimelineDataPoint.builder()
                .timestamp(data.getTimestamp())
                .participants(data.getParticipants())
                .views(data.getViews())
                .engagement(data.getEngagement())
                .conversions(data.getConversions())
                .cumulativeParticipants(data.getCumulativeParticipants())
                .build())
            .collect(Collectors.toList());
    }

    /**
     * 추세 분석 구성
     */
    private TrendAnalysis buildTrendAnalysis(List<TimelineDataPoint> dataPoints) {
        if (dataPoints.isEmpty()) {
            return null;
        }

        // 전체 추세 계산
        String overallTrend = calculateOverallTrend(dataPoints);

        // 증가율 계산
        double growthRate = calculateGrowthRate(dataPoints);

        // 예상 참여자 수
        int projectedParticipants = calculateProjectedParticipants(dataPoints);

        // 피크 기간 계산
        String peakPeriod = calculatePeakPeriod(dataPoints);

        return TrendAnalysis.builder()
            .overallTrend(overallTrend)
            .growthRate(Math.round(growthRate * 10.0) / 10.0)
            .projectedParticipants(projectedParticipants)
            .peakPeriod(peakPeriod)
            .build();
    }

    /**
     * 전체 추세 계산
     */
    private String calculateOverallTrend(List<TimelineDataPoint> dataPoints) {
        if (dataPoints.size() < 2) {
            return "stable";
        }

        int firstHalfParticipants = dataPoints.stream()
            .limit(dataPoints.size() / 2)
            .mapToInt(TimelineDataPoint::getParticipants)
            .sum();

        int secondHalfParticipants = dataPoints.stream()
            .skip(dataPoints.size() / 2)
            .mapToInt(TimelineDataPoint::getParticipants)
            .sum();

        if (secondHalfParticipants > firstHalfParticipants * 1.1) {
            return "increasing";
        } else if (secondHalfParticipants < firstHalfParticipants * 0.9) {
            return "decreasing";
        } else {
            return "stable";
        }
    }

    /**
     * 증가율 계산
     */
    private double calculateGrowthRate(List<TimelineDataPoint> dataPoints) {
        if (dataPoints.size() < 2) {
            return 0.0;
        }

        int firstParticipants = dataPoints.get(0).getParticipants();
        int lastParticipants = dataPoints.get(dataPoints.size() - 1).getParticipants();

        if (firstParticipants == 0) {
            return 0.0;
        }

        return ((lastParticipants - firstParticipants) * 100.0 / firstParticipants);
    }

    /**
     * 예상 참여자 수 계산
     */
    private int calculateProjectedParticipants(List<TimelineDataPoint> dataPoints) {
        if (dataPoints.isEmpty()) {
            return 0;
        }

        return dataPoints.get(dataPoints.size() - 1).getCumulativeParticipants();
    }

    /**
     * 피크 기간 계산
     */
    private String calculatePeakPeriod(List<TimelineDataPoint> dataPoints) {
        TimelineDataPoint peakPoint = dataPoints.stream()
            .max(Comparator.comparing(TimelineDataPoint::getParticipants))
            .orElse(null);

        if (peakPoint == null) {
            return "";
        }

        return peakPoint.getTimestamp().toLocalDate().toString();
    }

    /**
     * 피크 타임 구성
     */
    private List<PeakTimeInfo> buildPeakTimes(List<TimelineDataPoint> dataPoints) {
        List<PeakTimeInfo> peakTimes = new ArrayList<>();

        // 참여자 수 피크
        dataPoints.stream()
            .max(Comparator.comparing(TimelineDataPoint::getParticipants))
            .ifPresent(point -> peakTimes.add(PeakTimeInfo.builder()
                .timestamp(point.getTimestamp())
                .metric("participants")
                .value(point.getParticipants())
                .description("최대 참여자 수")
                .build()));

        // 조회수 피크
        dataPoints.stream()
            .max(Comparator.comparing(TimelineDataPoint::getViews))
            .ifPresent(point -> peakTimes.add(PeakTimeInfo.builder()
                .timestamp(point.getTimestamp())
                .metric("views")
                .value(point.getViews())
                .description("최대 조회수")
                .build()));

        return peakTimes;
    }
}
