package com.kt.event.eventservice.infrastructure.kafka;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.kt.event.eventservice.application.dto.kafka.ImageGenerationJobMessage;
import com.kt.event.eventservice.application.service.NotificationService;
import com.kt.event.eventservice.domain.entity.Event;
import com.kt.event.eventservice.domain.entity.GeneratedImage;
import com.kt.event.eventservice.domain.entity.Job;
import com.kt.event.eventservice.domain.repository.EventRepository;
import com.kt.event.eventservice.domain.repository.JobRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 이미지 생성 작업 메시지 구독 Consumer
 *
 * image-generation-job 토픽의 메시지를 구독하여 처리합니다.
 *
 * @author Event Service Team
 * @version 1.0.0
 * @since 2025-10-29
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ImageJobKafkaConsumer {

    private final ObjectMapper objectMapper;
    private final JobRepository jobRepository;
    private final EventRepository eventRepository;
    private final NotificationService notificationService;
    private final ImageJobKafkaProducer imageJobKafkaProducer;

    /**
     * 이미지 생성 작업 메시지 수신 처리
     *
     * @param payload 메시지 페이로드 (JSON)
     * @param partition 파티션 번호
     * @param offset 오프셋
     * @param acknowledgment 수동 커밋용 Acknowledgment
     */
    @KafkaListener(
        topics = "${app.kafka.topics.image-generation-job}",
        groupId = "${spring.kafka.consumer.group-id}",
        containerFactory = "kafkaListenerContainerFactory"
    )
    public void consumeImageGenerationJob(
        @Payload String payload,
        @Header(KafkaHeaders.RECEIVED_PARTITION) int partition,
        @Header(KafkaHeaders.OFFSET) long offset,
        Acknowledgment acknowledgment
    ) {
        try {
            log.info("이미지 생성 작업 메시지 수신 - Partition: {}, Offset: {}", partition, offset);

            // JSON을 객체로 변환
            ImageGenerationJobMessage message = objectMapper.readValue(
                payload,
                ImageGenerationJobMessage.class
            );

            log.info("이미지 작업 메시지 파싱 완료 - JobId: {}, EventId: {}, Status: {}",
                message.getJobId(), message.getEventId(), message.getStatus());

            // 메시지 처리 로직
            processImageGenerationJob(message);

            // 수동 커밋
            acknowledgment.acknowledge();
            log.info("이미지 생성 작업 메시지 처리 완료 - JobId: {}", message.getJobId());

        } catch (Exception e) {
            log.error("이미지 생성 작업 메시지 처리 중 오류 발생 - Partition: {}, Offset: {}, Error: {}",
                partition, offset, e.getMessage(), e);
            // 에러 발생 시에도 커밋 (재처리 방지, DLQ 사용 권장)
            acknowledgment.acknowledge();
        }
    }

    /**
     * 이미지 생성 작업 처리
     *
     * @param message 이미지 생성 작업 메시지
     */
    @Transactional
    protected void processImageGenerationJob(ImageGenerationJobMessage message) {
        try {
            String jobId = message.getJobId();
            String eventId = message.getEventId();

            // Job 조회
            Job job = jobRepository.findById(jobId).orElse(null);
            if (job == null) {
                log.warn("Job을 찾을 수 없습니다 - JobId: {}", message.getJobId());
                return;
            }

            // Event 조회 (모든 케이스에서 사용)
            Event event = eventRepository.findById(eventId).orElse(null);

            switch (message.getStatus()) {
                case "COMPLETED":
                    log.info("이미지 작업 완료 처리 - JobId: {}, EventId: {}, ImageURL: {}",
                        message.getJobId(), message.getEventId(), message.getImageUrl());

                    // Job 상태 업데이트
                    job.complete(message.getImageUrl());
                    jobRepository.save(job);

                    // Event 조회
                    if (event != null) {
                        // GeneratedImage 엔티티 생성 및 Event에 추가
                        GeneratedImage generatedImage = GeneratedImage.builder()
                            .imageUrl(message.getImageUrl())
                            .build();

                        event.addGeneratedImage(generatedImage);
                        eventRepository.save(event);

                        log.info("이미지 저장 완료 - EventId: {}, ImageURL: {}",
                            eventId, message.getImageUrl());

                        // 사용자에게 알림 전송
                        String userId = event.getUserId();
                        notificationService.notifyJobCompleted(
                            userId,
                            jobId,
                            "IMAGE_GENERATION",
                            "이미지 생성이 완료되었습니다."
                        );
                    } else {
                        log.warn("Event를 찾을 수 없습니다 - EventId: {}", eventId);
                    }
                    break;

                case "FAILED":
                    log.error("이미지 작업 실패 처리 - JobId: {}, EventId: {}, Error: {}",
                        message.getJobId(), message.getEventId(), message.getErrorMessage());

                    // 재시도 로직
                    if (job.canRetry()) {
                        log.info("이미지 생성 재시도 - JobId: {}, RetryCount: {}/{}",
                            jobId, job.getRetryCount() + 1, job.getMaxRetryCount());

                        // 재시도 준비
                        job.prepareRetry();
                        jobRepository.save(job);

                        // 재시도 메시지 발행
                        if (event != null) {
                            String prompt = String.format("이벤트: %s (재시도 %d/%d)",
                                event.getEventName() != null ? event.getEventName() : "이벤트",
                                job.getRetryCount(),
                                job.getMaxRetryCount());

                            imageJobKafkaProducer.publishImageGenerationJob(
                                jobId.toString(),
                                message.getUserId(),
                                eventId.toString(),
                                prompt
                            );

                            log.info("이미지 생성 재시도 메시지 발행 완료 - JobId: {}", jobId);
                        }
                    } else {
                        // 최대 재시도 횟수 초과 - 완전 실패 처리
                        log.error("이미지 생성 최대 재시도 횟수 초과 - JobId: {}, RetryCount: {}",
                            jobId, job.getRetryCount());

                        job.fail(message.getErrorMessage());
                        jobRepository.save(job);

                        // 사용자에게 실패 알림 전송
                        if (event != null) {
                            String userId = event.getUserId();
                            notificationService.notifyJobFailed(
                                userId,
                                jobId,
                                "IMAGE_GENERATION",
                                "이미지 생성에 실패했습니다: " + message.getErrorMessage()
                            );
                        }
                    }
                    break;

                case "PROCESSING":
                    log.info("이미지 작업 진행 중 - JobId: {}, EventId: {}",
                        message.getJobId(), message.getEventId());

                    // Job 상태 업데이트
                    job.start();
                    jobRepository.save(job);

                    // 사용자에게 진행 상태 알림 전송
                    if (event != null) {
                        String userId = event.getUserId();
                        notificationService.notifyJobProgress(
                            userId,
                            jobId,
                            "IMAGE_GENERATION",
                            job.getProgress()
                        );
                    }
                    break;

                default:
                    log.warn("알 수 없는 작업 상태 - JobId: {}, EventId: {}, Status: {}",
                        message.getJobId(), message.getEventId(), message.getStatus());
            }

        } catch (Exception e) {
            log.error("이미지 작업 처리 중 예외 발생 - JobId: {}, Error: {}",
                message.getJobId(), e.getMessage(), e);
            throw e;
        }
    }
}
