package com.kt.event.analytics.service;

import com.kt.event.analytics.dto.response.*;
import com.kt.event.analytics.entity.EventStats;
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.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * User ROI Analytics Service
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserRoiAnalyticsService {

    private final EventStatsRepository eventStatsRepository;
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    private static final String CACHE_KEY_PREFIX = "analytics:user:roi:";
    private static final long CACHE_TTL = 1800;

    public UserRoiAnalyticsResponse getUserRoiAnalytics(String userId, boolean includeProjection,
                                                         LocalDateTime startDate, LocalDateTime endDate, boolean refresh) {
        log.info("사용자 ROI 분석 조회 시작: userId={}, refresh={}", userId, refresh);

        String cacheKey = CACHE_KEY_PREFIX + userId;

        if (!refresh) {
            String cachedData = redisTemplate.opsForValue().get(cacheKey);
            if (cachedData != null) {
                try {
                    return objectMapper.readValue(cachedData, UserRoiAnalyticsResponse.class);
                } catch (JsonProcessingException e) {
                    log.warn("캐시 역직렬화 실패: {}", e.getMessage());
                }
            }
        }

        List<EventStats> allEvents = eventStatsRepository.findAllByUserId(userId);
        if (allEvents.isEmpty()) {
            return buildEmptyResponse(userId, startDate, endDate);
        }

        UserRoiAnalyticsResponse response = buildRoiResponse(userId, allEvents, includeProjection, startDate, endDate);

        try {
            String jsonData = objectMapper.writeValueAsString(response);
            redisTemplate.opsForValue().set(cacheKey, jsonData, CACHE_TTL, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.warn("캐시 저장 실패: {}", e.getMessage());
        }

        return response;
    }

    private UserRoiAnalyticsResponse buildEmptyResponse(String userId, LocalDateTime startDate, LocalDateTime endDate) {
        return UserRoiAnalyticsResponse.builder()
                .userId(userId)
                .period(buildPeriodInfo(startDate, endDate))
                .totalEvents(0)
                .overallInvestment(InvestmentDetails.builder().total(BigDecimal.ZERO).build())
                .overallRevenue(RevenueDetails.builder().total(BigDecimal.ZERO).build())
                .overallRoi(RoiCalculation.builder()
                        .netProfit(BigDecimal.ZERO)
                        .roiPercentage(0.0)
                        .build())
                .eventRois(new ArrayList<>())
                .lastUpdatedAt(LocalDateTime.now())
                .dataSource("empty")
                .build();
    }

    private UserRoiAnalyticsResponse buildRoiResponse(String userId, List<EventStats> allEvents, boolean includeProjection,
                                                       LocalDateTime startDate, LocalDateTime endDate) {
        BigDecimal totalInvestment = allEvents.stream().map(EventStats::getTotalInvestment).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalRevenue = allEvents.stream().map(EventStats::getExpectedRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalProfit = totalRevenue.subtract(totalInvestment);

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

        InvestmentDetails investment = InvestmentDetails.builder()
                .total(totalInvestment)
                .contentCreation(totalInvestment.multiply(BigDecimal.valueOf(0.6)))
                .operation(totalInvestment.multiply(BigDecimal.valueOf(0.2)))
                .distribution(totalInvestment.multiply(BigDecimal.valueOf(0.2)))
                .build();

        RevenueDetails revenue = RevenueDetails.builder()
                .total(totalRevenue)
                .directSales(totalRevenue.multiply(BigDecimal.valueOf(0.7)))
                .expectedSales(totalRevenue.multiply(BigDecimal.valueOf(0.3)))
                .build();

        RoiCalculation roiCalc = RoiCalculation.builder()
                .netProfit(totalProfit)
                .roiPercentage(Math.round(roiPercentage * 10) / 10.0)
                .build();

        int totalParticipants = allEvents.stream().mapToInt(EventStats::getTotalParticipants).sum();
        CostEfficiency efficiency = CostEfficiency.builder()
                .costPerParticipant(totalParticipants > 0 ? totalInvestment.doubleValue() / totalParticipants : 0.0)
                .revenuePerParticipant(totalParticipants > 0 ? totalRevenue.doubleValue() / totalParticipants : 0.0)
                .build();

        RevenueProjection projection = includeProjection ? RevenueProjection.builder()
                .currentRevenue(totalRevenue)
                .projectedFinalRevenue(totalRevenue.multiply(BigDecimal.valueOf(1.2)))
                .confidenceLevel(85.0)
                .basedOn("Historical trend analysis")
                .build() : null;

        List<UserRoiAnalyticsResponse.EventRoiSummary> eventRois = allEvents.stream()
                .map(event -> {
                    Double eventRoi = 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 UserRoiAnalyticsResponse.EventRoiSummary.builder()
                            .eventId(event.getEventId())
                            .eventTitle(event.getEventTitle())
                            .totalInvestment(event.getTotalInvestment().doubleValue())
                            .expectedRevenue(event.getExpectedRevenue().doubleValue())
                            .roi(Math.round(eventRoi * 10) / 10.0)
                            .status(event.getStatus())
                            .build();
                })
                .sorted(Comparator.comparingDouble(UserRoiAnalyticsResponse.EventRoiSummary::getRoi).reversed())
                .collect(Collectors.toList());

        return UserRoiAnalyticsResponse.builder()
                .userId(userId)
                .period(buildPeriodInfo(startDate, endDate))
                .totalEvents(allEvents.size())
                .overallInvestment(investment)
                .overallRevenue(revenue)
                .overallRoi(roiCalc)
                .costEfficiency(efficiency)
                .projection(projection)
                .eventRois(eventRois)
                .lastUpdatedAt(LocalDateTime.now())
                .dataSource("cached")
                .build();
    }

    private PeriodInfo buildPeriodInfo(LocalDateTime startDate, LocalDateTime endDate) {
        LocalDateTime start = startDate != null ? startDate : LocalDateTime.now().minusDays(30);
        LocalDateTime end = endDate != null ? endDate : LocalDateTime.now();
        return PeriodInfo.builder()
                .startDate(start)
                .endDate(end)
                .durationDays((int) ChronoUnit.DAYS.between(start, end))
                .build();
    }
}
