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

import com.kt.event.common.dto.PageResponse;
import com.kt.event.participation.application.dto.ParticipationRequest;
import com.kt.event.participation.application.dto.ParticipationResponse;
import com.kt.event.participation.domain.participant.Participant;
import com.kt.event.participation.domain.participant.ParticipantRepository;
import com.kt.event.participation.exception.ParticipationException.*;
import static com.kt.event.participation.exception.ParticipationException.EventNotFoundException;
import static com.kt.event.participation.exception.ParticipationException.ParticipantNotFoundException;
import com.kt.event.participation.infrastructure.kafka.KafkaProducerService;
import com.kt.event.participation.infrastructure.kafka.event.ParticipantRegisteredEvent;
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.util.Optional;

/**
 * 이벤트 참여 서비스
 *
 * @author Digital Garage Team
 * @since 2025-01-24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ParticipationService {

    private final ParticipantRepository participantRepository;
    private final KafkaProducerService kafkaProducerService;

    /**
     * 이벤트 참여
     *
     * @param eventId 이벤트 ID
     * @param request 참여 요청
     * @return 참여 응답
     */
    @Transactional
    public ParticipationResponse participate(String eventId, ParticipationRequest request) {
        log.info("이벤트 참여 시작 - eventId: {}, phoneNumber: {}", eventId, request.getPhoneNumber());

        // 중복 참여 체크 - 상세 디버깅
        log.info("중복 참여 체크 시작 - eventId: '{}', phoneNumber: '{}'", eventId, request.getPhoneNumber());

        boolean isDuplicate = participantRepository.existsByEventIdAndPhoneNumber(eventId, request.getPhoneNumber());
        log.info("중복 참여 체크 결과 - isDuplicate: {}", isDuplicate);

        if (isDuplicate) {
            log.warn("중복 참여 감지! eventId: '{}', phoneNumber: '{}'", eventId, request.getPhoneNumber());
            throw new DuplicateParticipationException();
        }

        log.info("중복 참여 체크 통과 - 참여 진행");

        // 참여자 ID 생성 - 날짜별 최대 순번 기반
        String dateTime;
        if (eventId != null && eventId.length() >= 16 && eventId.startsWith("evt_")) {
            dateTime = eventId.substring(4, 12); // "20250124"
        } else {
            dateTime = java.time.LocalDate.now().format(
                java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
        }

        String datePrefix = "prt_" + dateTime + "_";
        Integer maxSequence = participantRepository.findMaxSequenceByDatePrefix(datePrefix);
        String participantId = String.format("prt_%s_%03d", dateTime, maxSequence + 1);

        // 참여자 저장
        Participant participant = Participant.builder()
            .participantId(participantId)
            .eventId(eventId)
            .name(request.getName())
            .phoneNumber(request.getPhoneNumber())
            .email(request.getEmail())
            .channel(request.getChannel())
            .storeVisited(request.getStoreVisited())
            .bonusEntries(Participant.calculateBonusEntries(request.getStoreVisited()))
            .agreeMarketing(request.getAgreeMarketing())
            .agreePrivacy(request.getAgreePrivacy())
            .isWinner(false)
            .build();

        participant = participantRepository.save(participant);
        log.info("참여자 저장 완료 - participantId: {}", participantId);

        // Kafka 이벤트 발행
        kafkaProducerService.publishParticipantRegistered(
            ParticipantRegisteredEvent.from(participant)
        );

        return ParticipationResponse.from(participant);
    }

    /**
     * 참여자 목록 조회
     *
     * @param eventId 이벤트 ID
     * @param storeVisited 매장 방문 여부 필터 (nullable)
     * @param pageable 페이징 정보
     * @return 참여자 목록
     */
    @Transactional(readOnly = true)
    public PageResponse<ParticipationResponse> getParticipants(
        String eventId, Boolean storeVisited, Pageable pageable) {

        Page<Participant> participantPage;
        if (storeVisited != null) {
            participantPage = participantRepository
                .findByEventIdAndStoreVisitedOrderByCreatedAtDesc(eventId, storeVisited, pageable);
        } else {
            participantPage = participantRepository
                .findByEventIdOrderByCreatedAtDesc(eventId, pageable);
        }

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

    /**
     * 참여자 상세 조회
     *
     * @param eventId 이벤트 ID
     * @param participantId 참여자 ID
     * @return 참여자 정보
     */
    @Transactional(readOnly = true)
    public ParticipationResponse getParticipant(String eventId, String participantId) {
        // 참여자 조회
        Optional<Participant> participantOpt = participantRepository
            .findByEventIdAndParticipantId(eventId, participantId);

        // 참여자가 없으면 이벤트 존재 여부 확인
        if (participantOpt.isEmpty()) {
            long participantCount = participantRepository.countByEventId(eventId);
            if (participantCount == 0) {
                // 이벤트에 참여자가 한 명도 없음 = 이벤트가 존재하지 않음
                throw new EventNotFoundException();
            }
            // 이벤트는 존재하지만 해당 참여자가 없음
            throw new ParticipantNotFoundException();
        }

        return ParticipationResponse.from(participantOpt.get());
    }
}
