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.repository.ChannelStatsRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.util.*;
import java.util.stream.Collectors;

/**
 * 채널별 분석 Service
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ChannelAnalyticsService {

    private final ChannelStatsRepository channelStatsRepository;
    private final ExternalChannelService externalChannelService;

    /**
     * 채널별 성과 분석
     */
    public ChannelAnalyticsResponse getChannelAnalytics(String eventId, List<String> channels, String sortBy, String order) {
        log.info("채널별 성과 분석 조회: eventId={}", eventId);

        List<ChannelStats> channelStatsList = channelStatsRepository.findByEventId(eventId);

        // 외부 API 호출하여 최신 데이터 반영
        externalChannelService.updateChannelStatsFromExternalAPIs(eventId, channelStatsList);

        // 필터링 (특정 채널만 조회)
        if (channels != null && !channels.isEmpty()) {
            channelStatsList = channelStatsList.stream()
                .filter(stats -> channels.contains(stats.getChannelName()))
                .collect(Collectors.toList());
        }

        // 채널별 상세 분석 구성
        List<ChannelAnalytics> channelAnalytics = buildChannelAnalytics(channelStatsList);

        // 정렬
        channelAnalytics = sortChannelAnalytics(channelAnalytics, sortBy, order);

        // 채널 간 비교 분석
        ChannelComparison comparison = buildChannelComparison(channelAnalytics);

        return ChannelAnalyticsResponse.builder()
            .eventId(eventId)
            .channels(channelAnalytics)
            .comparison(comparison)
            .lastUpdatedAt(LocalDateTime.now())
            .build();
    }

    /**
     * 채널별 상세 분석 구성
     */
    private List<ChannelAnalytics> buildChannelAnalytics(List<ChannelStats> channelStatsList) {
        return channelStatsList.stream()
            .map(this::buildChannelAnalytics)
            .collect(Collectors.toList());
    }

    private ChannelAnalytics buildChannelAnalytics(ChannelStats stats) {
        ChannelMetrics metrics = buildChannelMetrics(stats);
        ChannelPerformance performance = buildChannelPerformance(stats);
        ChannelCosts costs = buildChannelCosts(stats);

        return ChannelAnalytics.builder()
            .channelName(stats.getChannelName())
            .channelType(stats.getChannelType())
            .metrics(metrics)
            .performance(performance)
            .costs(costs)
            .externalApiStatus("success")
            .build();
    }

    /**
     * 채널 지표 구성
     */
    private ChannelMetrics buildChannelMetrics(ChannelStats stats) {
        SocialInteractionStats socialStats = null;
        if (stats.getLikes() > 0 || stats.getComments() > 0 || stats.getShares() > 0) {
            socialStats = SocialInteractionStats.builder()
                .likes(stats.getLikes())
                .comments(stats.getComments())
                .shares(stats.getShares())
                .build();
        }

        VoiceCallStats voiceStats = null;
        if (stats.getTotalCalls() > 0) {
            double completionRate = stats.getTotalCalls() > 0 ?
                (stats.getCompletedCalls() * 100.0 / stats.getTotalCalls()) : 0.0;

            voiceStats = VoiceCallStats.builder()
                .totalCalls(stats.getTotalCalls())
                .completedCalls(stats.getCompletedCalls())
                .averageDuration(stats.getAverageDuration())
                .completionRate(Math.round(completionRate * 10.0) / 10.0)
                .build();
        }

        return ChannelMetrics.builder()
            .impressions(stats.getImpressions())
            .views(stats.getViews())
            .clicks(stats.getClicks())
            .participants(stats.getParticipants())
            .conversions(stats.getConversions())
            .socialInteractions(socialStats)
            .voiceCallStats(voiceStats)
            .build();
    }

    /**
     * 채널 성과 지표 구성
     */
    private ChannelPerformance buildChannelPerformance(ChannelStats stats) {
        double ctr = stats.getImpressions() > 0 ? (stats.getClicks() * 100.0 / stats.getImpressions()) : 0.0;
        double engagementRate = stats.getViews() > 0 ? (stats.getParticipants() * 100.0 / stats.getViews()) : 0.0;
        double conversionRate = stats.getViews() > 0 ? (stats.getConversions() * 100.0 / stats.getViews()) : 0.0;

        return ChannelPerformance.builder()
            .clickThroughRate(Math.round(ctr * 10.0) / 10.0)
            .engagementRate(Math.round(engagementRate * 10.0) / 10.0)
            .conversionRate(Math.round(conversionRate * 10.0) / 10.0)
            .averageEngagementTime(165)
            .bounceRate(35.8)
            .build();
    }

    /**
     * 채널 비용 구성
     */
    private ChannelCosts buildChannelCosts(ChannelStats stats) {
        double cpv = stats.getViews() > 0 ?
            stats.getDistributionCost().divide(BigDecimal.valueOf(stats.getViews()), 2, RoundingMode.HALF_UP).doubleValue() : 0.0;
        double cpc = stats.getClicks() > 0 ?
            stats.getDistributionCost().divide(BigDecimal.valueOf(stats.getClicks()), 2, RoundingMode.HALF_UP).doubleValue() : 0.0;
        double cpa = stats.getParticipants() > 0 ?
            stats.getDistributionCost().divide(BigDecimal.valueOf(stats.getParticipants()), 2, RoundingMode.HALF_UP).doubleValue() : 0.0;

        double roi = stats.getDistributionCost().compareTo(BigDecimal.ZERO) > 0 ?
            (stats.getParticipants() * 100.0 / stats.getDistributionCost().doubleValue()) : 0.0;

        return ChannelCosts.builder()
            .distributionCost(stats.getDistributionCost())
            .costPerView(Math.round(cpv * 100.0) / 100.0)
            .costPerClick(Math.round(cpc * 100.0) / 100.0)
            .costPerAcquisition(Math.round(cpa * 100.0) / 100.0)
            .roi(Math.round(roi * 10.0) / 10.0)
            .build();
    }

    /**
     * 채널 정렬
     */
    private List<ChannelAnalytics> sortChannelAnalytics(List<ChannelAnalytics> channelAnalytics, String sortBy, String order) {
        Comparator<ChannelAnalytics> comparator = switch (sortBy != null ? sortBy : "roi") {
            case "views" -> Comparator.comparing(c -> c.getMetrics().getViews());
            case "participants" -> Comparator.comparing(c -> c.getMetrics().getParticipants());
            case "engagement_rate" -> Comparator.comparing(c -> c.getPerformance().getEngagementRate());
            case "conversion_rate" -> Comparator.comparing(c -> c.getPerformance().getConversionRate());
            default -> Comparator.comparing(c -> c.getCosts().getRoi());
        };

        if ("asc".equals(order)) {
            channelAnalytics.sort(comparator);
        } else {
            channelAnalytics.sort(comparator.reversed());
        }

        return channelAnalytics;
    }

    /**
     * 채널 간 비교 분석 구성
     */
    private ChannelComparison buildChannelComparison(List<ChannelAnalytics> channelAnalytics) {
        if (channelAnalytics.isEmpty()) {
            return null;
        }

        // 최고 성과 채널 찾기
        String bestByViews = channelAnalytics.stream()
            .max(Comparator.comparing(c -> c.getMetrics().getViews()))
            .map(ChannelAnalytics::getChannelName)
            .orElse(null);

        String bestByEngagement = channelAnalytics.stream()
            .max(Comparator.comparing(c -> c.getPerformance().getEngagementRate()))
            .map(ChannelAnalytics::getChannelName)
            .orElse(null);

        String bestByRoi = channelAnalytics.stream()
            .max(Comparator.comparing(c -> c.getCosts().getRoi()))
            .map(ChannelAnalytics::getChannelName)
            .orElse(null);

        Map<String, String> bestPerforming = new HashMap<>();
        bestPerforming.put("byViews", bestByViews);
        bestPerforming.put("byEngagement", bestByEngagement);
        bestPerforming.put("byRoi", bestByRoi);

        // 평균 지표 계산
        double avgEngagementRate = channelAnalytics.stream()
            .mapToDouble(c -> c.getPerformance().getEngagementRate())
            .average()
            .orElse(0.0);

        double avgConversionRate = channelAnalytics.stream()
            .mapToDouble(c -> c.getPerformance().getConversionRate())
            .average()
            .orElse(0.0);

        double avgRoi = channelAnalytics.stream()
            .mapToDouble(c -> c.getCosts().getRoi())
            .average()
            .orElse(0.0);

        Map<String, Double> averageMetrics = new HashMap<>();
        averageMetrics.put("engagementRate", Math.round(avgEngagementRate * 10.0) / 10.0);
        averageMetrics.put("conversionRate", Math.round(avgConversionRate * 10.0) / 10.0);
        averageMetrics.put("roi", Math.round(avgRoi * 10.0) / 10.0);

        return ChannelComparison.builder()
            .bestPerforming(bestPerforming)
            .averageMetrics(averageMetrics)
            .build();
    }
}
