package com.kt.event.participation.application.service;

import com.kt.event.common.dto.PageResponse;
import com.kt.event.participation.application.dto.DrawWinnersRequest;
import com.kt.event.participation.application.dto.DrawWinnersResponse;
import com.kt.event.participation.application.dto.DrawWinnersResponse.WinnerSummary;
import com.kt.event.participation.application.dto.ParticipationResponse;
import com.kt.event.participation.domain.draw.DrawLog;
import com.kt.event.participation.domain.draw.DrawLogRepository;
import com.kt.event.participation.domain.participant.Participant;
import com.kt.event.participation.domain.participant.ParticipantRepository;
import com.kt.event.participation.exception.ParticipationException.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 당첨자 추첨 서비스
 *
 * @author Digital Garage Team
 * @since 2025-01-24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WinnerDrawService {

    private final ParticipantRepository participantRepository;
    private final DrawLogRepository drawLogRepository;

    /**
     * 당첨자 추첨
     *
     * @param eventId 이벤트 ID
     * @param request 추첨 요청
     * @return 추첨 결과
     */
    @Transactional
    public DrawWinnersResponse drawWinners(String eventId, DrawWinnersRequest request) {
        log.info("당첨자 추첨 시작 - eventId: {}, winnerCount: {}", eventId, request.getWinnerCount());

        // 이미 추첨이 완료되었는지 확인
        if (drawLogRepository.existsByEventId(eventId)) {
            throw new AlreadyDrawnException();
        }

        // 참여자 목록 조회
        List<Participant> participants = participantRepository.findByEventIdAndIsWinnerFalse(eventId);
        long participantCount = participants.size();

        // 참여자 수 검증
        if (participantCount < request.getWinnerCount()) {
            throw new InsufficientParticipantsException(participantCount, request.getWinnerCount());
        }

        // 가중치 적용 추첨 풀 생성
        List<Participant> drawPool = createDrawPool(participants, request.getApplyStoreVisitBonus());

        // 추첨 실행
        Collections.shuffle(drawPool);
        List<Participant> winners = drawPool.stream()
            .distinct()
            .limit(request.getWinnerCount())
            .collect(Collectors.toList());

        // 당첨자 업데이트
        LocalDateTime now = LocalDateTime.now();
        for (int i = 0; i < winners.size(); i++) {
            winners.get(i).markAsWinner(i + 1);
        }
        participantRepository.saveAll(winners);

        // 추첨 로그 저장
        DrawLog drawLog = DrawLog.builder()
            .eventId(eventId)
            .totalParticipants((int) participantCount)
            .winnerCount(request.getWinnerCount())
            .applyStoreVisitBonus(request.getApplyStoreVisitBonus())
            .algorithm("WEIGHTED_RANDOM")
            .drawnAt(now)
            .drawnBy("SYSTEM")
            .build();
        drawLogRepository.save(drawLog);

        log.info("당첨자 추첨 완료 - eventId: {}, winners: {}", eventId, winners.size());

        // 응답 생성
        List<WinnerSummary> winnerSummaries = winners.stream()
            .map(w -> WinnerSummary.builder()
                .participantId(w.getParticipantId())
                .name(w.getName())
                .phoneNumber(w.getPhoneNumber())
                .rank(w.getWinnerRank())
                .build())
            .collect(Collectors.toList());

        return DrawWinnersResponse.builder()
            .eventId(eventId)
            .totalParticipants((int) participantCount)
            .winnerCount(winners.size())
            .drawnAt(now)
            .winners(winnerSummaries)
            .build();
    }

    /**
     * 당첨자 목록 조회
     *
     * @param eventId 이벤트 ID
     * @param pageable 페이징 정보
     * @return 당첨자 목록
     */
    @Transactional(readOnly = true)
    public PageResponse<ParticipationResponse> getWinners(String eventId, Pageable pageable) {
        // 추첨 완료 확인
        if (!drawLogRepository.existsByEventId(eventId)) {
            throw new NoWinnersYetException();
        }

        Page<Participant> winnerPage = participantRepository
            .findByEventIdAndIsWinnerTrueOrderByWinnerRankAsc(eventId, pageable);

        Page<ParticipationResponse> responsePage = winnerPage.map(ParticipationResponse::from);
        return PageResponse.of(responsePage);
    }

    /**
     * 추첨 풀 생성 (매장 방문 보너스 적용)
     *
     * @param participants 참여자 목록
     * @param applyBonus 보너스 적용 여부
     * @return 추첨 풀
     */
    private List<Participant> createDrawPool(List<Participant> participants, Boolean applyBonus) {
        if (!applyBonus) {
            return new ArrayList<>(participants);
        }

        List<Participant> pool = new ArrayList<>();
        for (Participant participant : participants) {
            // 보너스 응모권 수만큼 추첨 풀에 추가
            int entries = participant.getBonusEntries();
            for (int i = 0; i < entries; i++) {
                pool.add(participant);
            }
        }
        return pool;
    }
}
