package com.kt.event.analytics.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.kt.event.analytics.messaging.event.DistributionCompletedEvent;
import com.kt.event.analytics.messaging.event.EventCreatedEvent;
import com.kt.event.analytics.messaging.event.ParticipantRegisteredEvent;
import com.kt.event.analytics.repository.ChannelStatsRepository;
import com.kt.event.analytics.repository.EventStatsRepository;
import com.kt.event.analytics.repository.TimelineDataRepository;
import jakarta.annotation.PreDestroy;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

/**
 * 샘플 데이터 로더 (Kafka Producer 방식)
 *
 * ⚠️ MVP 전용: 다른 마이크로서비스(Event, Participant, Distribution)가
 *             없는 환경에서 해당 서비스들의 역할을 시뮬레이션합니다.
 *
 * ⚠️ 실제 운영: Analytics Service는 순수 Consumer 역할만 수행해야 하며,
 *             이 클래스는 비활성화되어야 합니다.
 *             → SAMPLE_DATA_ENABLED=false 설정
 *
 * - 서비스 시작 시: Kafka 이벤트 발행하여 샘플 데이터 자동 생성
 * - 서비스 종료 시: PostgreSQL 전체 데이터 삭제
 *
 * 활성화 조건: spring.sample-data.enabled=true (기본값: true)
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "spring.sample-data.enabled", havingValue = "true", matchIfMissing = true)
@RequiredArgsConstructor
public class SampleDataLoader implements ApplicationRunner {

    private final KafkaTemplate<String, String> kafkaTemplate;
    private final ObjectMapper objectMapper;
    private final EventStatsRepository eventStatsRepository;
    private final ChannelStatsRepository channelStatsRepository;
    private final TimelineDataRepository timelineDataRepository;
    private final EntityManager entityManager;
    private final RedisTemplate<String, String> redisTemplate;

    private final Random random = new Random();

    // Kafka Topic Names (MVP용 샘플 토픽)
    private static final String EVENT_CREATED_TOPIC = "sample.event.created";
    private static final String PARTICIPANT_REGISTERED_TOPIC = "sample.participant.registered";
    private static final String DISTRIBUTION_COMPLETED_TOPIC = "sample.distribution.completed";

    @Override
    @Transactional
    public void run(ApplicationArguments args) {
        log.info("========================================");
        log.info("🚀 서비스 시작: Kafka 이벤트 발행하여 샘플 데이터 생성");
        log.info("========================================");

        // 항상 기존 데이터 삭제 후 새로 생성
        long existingCount = eventStatsRepository.count();
        if (existingCount > 0) {
            log.info("기존 데이터 {} 건 삭제 중...", existingCount);
            timelineDataRepository.deleteAll();
            channelStatsRepository.deleteAll();
            eventStatsRepository.deleteAll();

            // 삭제 커밋 보장
            entityManager.flush();
            entityManager.clear();

            log.info("✅ 기존 데이터 삭제 완료");
        }

        // Redis 멱등성 키 삭제 (새로운 이벤트 처리를 위해)
        log.info("Redis 멱등성 키 삭제 중...");
        redisTemplate.delete("processed_events");
        redisTemplate.delete("distribution_completed");
        redisTemplate.delete("processed_participants");
        log.info("✅ Redis 멱등성 키 삭제 완료");

        try {
            // 1. EventCreated 이벤트 발행 (3개 이벤트)
            publishEventCreatedEvents();
            log.info("⏳ EventStats 생성 대기 중... (5초)");
            Thread.sleep(5000);  // EventCreatedConsumer가 EventStats 생성할 시간

            // 2. DistributionCompleted 이벤트 발행 (각 이벤트당 4개 채널)
            publishDistributionCompletedEvents();
            log.info("⏳ ChannelStats 생성 대기 중... (3초)");
            Thread.sleep(3000);  // DistributionCompletedConsumer가 ChannelStats 생성할 시간

            // 3. ParticipantRegistered 이벤트 발행 (각 이벤트당 다수 참여자)
            publishParticipantRegisteredEvents();

            log.info("========================================");
            log.info("🎉 Kafka 이벤트 발행 완료! (Consumer가 처리 중...)");
            log.info("========================================");
            log.info("발행된 이벤트:");
            log.info("  - EventCreated: 3건");
            log.info("  - DistributionCompleted: 3건 (각 이벤트당 4개 채널 배열)");
            log.info("  - ParticipantRegistered: 180건 (MVP 테스트용)");
            log.info("========================================");

            // Consumer 처리 대기 (5초)
            log.info("⏳ 참여자 수 업데이트 대기 중... (5초)");
            Thread.sleep(5000);

            // 4. TimelineData 생성 (시간대별 데이터)
            createTimelineData();
            log.info("✅ TimelineData 생성 완료");

        } catch (Exception e) {
            log.error("샘플 데이터 적재 중 오류 발생", e);
        }
    }

    /**
     * 서비스 종료 시 전체 데이터 삭제
     */
    @PreDestroy
    @Transactional
    public void onShutdown() {
        log.info("========================================");
        log.info("🛑 서비스 종료: PostgreSQL 전체 데이터 삭제");
        log.info("========================================");

        try {
            long timelineCount = timelineDataRepository.count();
            long channelCount = channelStatsRepository.count();
            long eventCount = eventStatsRepository.count();

            log.info("삭제 대상: 이벤트={}, 채널={}, 타임라인={}",
                    eventCount, channelCount, timelineCount);

            timelineDataRepository.deleteAll();
            channelStatsRepository.deleteAll();
            eventStatsRepository.deleteAll();

            // 삭제 커밋 보장
            entityManager.flush();
            entityManager.clear();

            log.info("✅ 모든 샘플 데이터 삭제 완료!");
            log.info("========================================");

        } catch (Exception e) {
            log.error("샘플 데이터 삭제 중 오류 발생", e);
        }
    }

    /**
     * EventCreated 이벤트 발행
     */
    private void publishEventCreatedEvents() throws Exception {
        // 이벤트 1: 신년맞이 할인 이벤트 (진행중, 높은 성과)
        EventCreatedEvent event1 = EventCreatedEvent.builder()
            .eventId("evt_2025012301")
            .eventTitle("신년맞이 20% 할인 이벤트")
            .storeId("store_001")
            .totalInvestment(new BigDecimal("5000000"))
            .status("ACTIVE")
            .build();
        publishEvent(EVENT_CREATED_TOPIC, event1);

        // 이벤트 2: 설날 특가 이벤트 (진행중, 중간 성과)
        EventCreatedEvent event2 = EventCreatedEvent.builder()
            .eventId("evt_2025020101")
            .eventTitle("설날 특가 선물세트 이벤트")
            .storeId("store_001")
            .totalInvestment(new BigDecimal("3500000"))
            .status("ACTIVE")
            .build();
        publishEvent(EVENT_CREATED_TOPIC, event2);

        // 이벤트 3: 겨울 신메뉴 런칭 이벤트 (종료, 저조한 성과)
        EventCreatedEvent event3 = EventCreatedEvent.builder()
            .eventId("evt_2025011501")
            .eventTitle("겨울 신메뉴 런칭 이벤트")
            .storeId("store_001")
            .totalInvestment(new BigDecimal("2000000"))
            .status("COMPLETED")
            .build();
        publishEvent(EVENT_CREATED_TOPIC, event3);

        log.info("✅ EventCreated 이벤트 3건 발행 완료");
    }

    /**
     * DistributionCompleted 이벤트 발행 (설계서 기준 - 이벤트당 1번 발행, 여러 채널 배열)
     */
    private void publishDistributionCompletedEvents() throws Exception {
        String[] eventIds = {"evt_2025012301", "evt_2025020101", "evt_2025011501"};
        int[][] expectedViews = {
            {5000, 10000, 3000, 2000},  // 이벤트1: 우리동네TV, 지니TV, 링고비즈, SNS
            {3500, 7000, 2000, 1500},   // 이벤트2
            {1500, 3000, 1000, 500}     // 이벤트3
        };

        for (int i = 0; i < eventIds.length; i++) {
            String eventId = eventIds[i];

            // 4개 채널을 배열로 구성
            List<DistributionCompletedEvent.ChannelDistribution> channels = new ArrayList<>();

            // 1. 우리동네TV (TV)
            channels.add(DistributionCompletedEvent.ChannelDistribution.builder()
                .channel("우리동네TV")
                .channelType("TV")
                .status("SUCCESS")
                .expectedViews(expectedViews[i][0])
                .build());

            // 2. 지니TV (TV)
            channels.add(DistributionCompletedEvent.ChannelDistribution.builder()
                .channel("지니TV")
                .channelType("TV")
                .status("SUCCESS")
                .expectedViews(expectedViews[i][1])
                .build());

            // 3. 링고비즈 (CALL)
            channels.add(DistributionCompletedEvent.ChannelDistribution.builder()
                .channel("링고비즈")
                .channelType("CALL")
                .status("SUCCESS")
                .expectedViews(expectedViews[i][2])
                .build());

            // 4. SNS (SNS)
            channels.add(DistributionCompletedEvent.ChannelDistribution.builder()
                .channel("SNS")
                .channelType("SNS")
                .status("SUCCESS")
                .expectedViews(expectedViews[i][3])
                .build());

            // 이벤트 발행 (채널 배열 포함)
            DistributionCompletedEvent event = DistributionCompletedEvent.builder()
                .eventId(eventId)
                .distributedChannels(channels)
                .completedAt(java.time.LocalDateTime.now())
                .build();

            publishEvent(DISTRIBUTION_COMPLETED_TOPIC, event);
        }

        log.info("✅ DistributionCompleted 이벤트 3건 발행 완료 (3 이벤트 × 4 채널 배열)");
    }

    /**
     * ParticipantRegistered 이벤트 발행
     */
    private void publishParticipantRegisteredEvents() throws Exception {
        String[] eventIds = {"evt_2025012301", "evt_2025020101", "evt_2025011501"};
        int[] totalParticipants = {100, 50, 30};  // MVP 테스트용 샘플 데이터 (총 180명)
        String[] channels = {"우리동네TV", "지니TV", "링고비즈", "SNS"};

        int totalPublished = 0;

        for (int i = 0; i < eventIds.length; i++) {
            String eventId = eventIds[i];
            int participants = totalParticipants[i];

            // 각 이벤트에 대해 참여자 수만큼 ParticipantRegistered 이벤트 발행
            for (int j = 0; j < participants; j++) {
                String participantId = UUID.randomUUID().toString();
                String channel = channels[j % channels.length];  // 채널 순환 배정

                ParticipantRegisteredEvent event = ParticipantRegisteredEvent.builder()
                    .eventId(eventId)
                    .participantId(participantId)
                    .channel(channel)
                    .build();

                publishEvent(PARTICIPANT_REGISTERED_TOPIC, event);
                totalPublished++;

                // 동시성 충돌 방지: 10개마다 100ms 대기
                if ((j + 1) % 10 == 0) {
                    Thread.sleep(100);
                }
            }
        }

        log.info("✅ ParticipantRegistered 이벤트 {}건 발행 완료", totalPublished);
    }

    /**
     * TimelineData 생성 (시간대별 샘플 데이터)
     *
     * - 각 이벤트마다 30일 치 daily 데이터 생성
     * - 참여자 수, 조회수, 참여행동, 전환수, 누적 참여자 수
     */
    private void createTimelineData() {
        log.info("📊 TimelineData 생성 시작...");

        String[] eventIds = {"evt_2025012301", "evt_2025020101", "evt_2025011501"};

        // 각 이벤트별 기준 참여자 수 (이벤트 성과에 따라 다름)
        int[] baseParticipants = {20, 12, 5};  // 이벤트1(높음), 이벤트2(중간), 이벤트3(낮음)

        for (int eventIndex = 0; eventIndex < eventIds.length; eventIndex++) {
            String eventId = eventIds[eventIndex];
            int baseParticipant = baseParticipants[eventIndex];
            int cumulativeParticipants = 0;

            // 30일 치 데이터 생성 (2024-09-24부터)
            java.time.LocalDateTime startDate = java.time.LocalDateTime.of(2024, 9, 24, 0, 0);

            for (int day = 0; day < 30; day++) {
                java.time.LocalDateTime timestamp = startDate.plusDays(day);

                // 랜덤한 참여자 수 생성 (기준값 ± 50%)
                int dailyParticipants = baseParticipant + random.nextInt(baseParticipant + 1);
                cumulativeParticipants += dailyParticipants;

                // 조회수는 참여자의 3~5배
                int dailyViews = dailyParticipants * (3 + random.nextInt(3));

                // 참여행동은 참여자의 1~2배
                int dailyEngagement = dailyParticipants * (1 + random.nextInt(2));

                // 전환수는 참여자의 50~80%
                int dailyConversions = (int) (dailyParticipants * (0.5 + random.nextDouble() * 0.3));

                // TimelineData 생성
                com.kt.event.analytics.entity.TimelineData timelineData =
                    com.kt.event.analytics.entity.TimelineData.builder()
                        .eventId(eventId)
                        .timestamp(timestamp)
                        .participants(dailyParticipants)
                        .views(dailyViews)
                        .engagement(dailyEngagement)
                        .conversions(dailyConversions)
                        .cumulativeParticipants(cumulativeParticipants)
                        .build();

                timelineDataRepository.save(timelineData);
            }

            log.info("✅ TimelineData 생성 완료: eventId={}, 30일 데이터", eventId);
        }

        log.info("✅ 전체 TimelineData 생성 완료: 3개 이벤트 × 30일 = 90건");
    }

    /**
     * Kafka 이벤트 발행 공통 메서드
     */
    private void publishEvent(String topic, Object event) throws Exception {
        String jsonMessage = objectMapper.writeValueAsString(event);
        kafkaTemplate.send(topic, jsonMessage);
    }
}
