package com.unicorn.hgzero.notification.event;

import com.azure.messaging.eventhubs.models.PartitionEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.notification.event.event.MeetingCreatedEvent;
import com.unicorn.hgzero.notification.event.event.TodoAssignedEvent;
import com.unicorn.hgzero.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.function.Consumer;

/**
 * Event Hub 이벤트 핸들러
 *
 * Azure Event Hub로부터 이벤트를 수신하여 처리
 * 이벤트 유형에 따라 적절한 알림 발송 로직 실행
 *
 * @author 준호 (Backend Developer)
 * @version 1.0
 * @since 2025-10-23
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class EventHandler implements Consumer<PartitionEvent> {

    private final NotificationService notificationService;
    private final ObjectMapper objectMapper;
    private final RetryTemplate retryTemplate;

    /**
     * Event Hub 이벤트 처리
     *
     * @param partitionEvent Event Hub 파티션 이벤트
     */
    @Override
    public void accept(PartitionEvent partitionEvent) {
        try {
            // 이벤트 데이터 추출
            var eventData = partitionEvent.getData();

            // 이벤트 속성 추출
            Map<String, Object> properties = eventData.getProperties();
            String topic = (String) properties.get("topic");
            String eventType = (String) properties.get("eventType");

            log.info("이벤트 수신 - Topic: {}, EventType: {}", topic, eventType);

            // 이벤트 본문 추출
            String eventBody = eventData.getBodyAsString();

            // 토픽 및 이벤트 유형에 따라 처리
            if ("meeting".equals(topic)) {
                handleMeetingEvent(eventType, eventBody);
            } else if ("todo".equals(topic)) {
                handleTodoEvent(eventType, eventBody);
            } else {
                log.warn("알 수 없는 토픽: {}", topic);
            }

            // 체크포인트 업데이트 (처리 성공 시)
            // TODO: Azure Event Hubs 5.x API에 맞게 체크포인트 업데이트 구현 필요
            // partitionEvent.getPartitionContext().updateCheckpointAsync().block();
            log.info("이벤트 처리 완료");

        } catch (Exception e) {
            log.error("이벤트 처리 중 오류 발생", e);
            // 체크포인트를 업데이트하지 않아 재처리 가능
            throw new RuntimeException("이벤트 처리 실패", e);
        }
    }

    /**
     * 회의 관련 이벤트 처리
     *
     * @param eventType 이벤트 유형
     * @param eventBody 이벤트 본문 (JSON)
     */
    private void handleMeetingEvent(String eventType, String eventBody) {
        try {
            switch (eventType) {
                case "MeetingCreated":
                    MeetingCreatedEvent meetingEvent = objectMapper.readValue(
                        eventBody,
                        MeetingCreatedEvent.class
                    );
                    processMeetingCreatedEvent(meetingEvent);
                    break;

                case "MeetingUpdated":
                    log.info("회의 수정 이벤트 처리 (향후 구현)");
                    break;

                case "MeetingCancelled":
                    log.info("회의 취소 이벤트 처리 (향후 구현)");
                    break;

                default:
                    log.warn("알 수 없는 회의 이벤트 유형: {}", eventType);
            }
        } catch (Exception e) {
            log.error("회의 이벤트 처리 중 오류 발생 - EventType: {}", eventType, e);
            throw new RuntimeException("회의 이벤트 처리 실패", e);
        }
    }

    /**
     * Todo 관련 이벤트 처리
     *
     * @param eventType 이벤트 유형
     * @param eventBody 이벤트 본문 (JSON)
     */
    private void handleTodoEvent(String eventType, String eventBody) {
        try {
            switch (eventType) {
                case "TodoAssigned":
                    TodoAssignedEvent todoEvent = objectMapper.readValue(
                        eventBody,
                        TodoAssignedEvent.class
                    );
                    processTodoAssignedEvent(todoEvent);
                    break;

                case "TodoCompleted":
                    log.info("Todo 완료 이벤트 처리 (향후 구현)");
                    break;

                case "TodoUpdated":
                    log.info("Todo 수정 이벤트 처리 (향후 구현)");
                    break;

                default:
                    log.warn("알 수 없는 Todo 이벤트 유형: {}", eventType);
            }
        } catch (Exception e) {
            log.error("Todo 이벤트 처리 중 오류 발생 - EventType: {}", eventType, e);
            throw new RuntimeException("Todo 이벤트 처리 실패", e);
        }
    }

    /**
     * 회의 생성 이벤트 처리 (재시도 지원)
     *
     * @param event 회의 생성 이벤트
     */
    private void processMeetingCreatedEvent(MeetingCreatedEvent event) {
        retryTemplate.execute(context -> {
            log.info("회의 초대 알림 발송 시작 - MeetingId: {}, EventId: {}",
                event.getMeetingId(), event.getEventId());

            notificationService.sendMeetingInvitation(event);

            log.info("회의 초대 알림 발송 완료 - MeetingId: {}", event.getMeetingId());
            return null;
        });
    }

    /**
     * Todo 할당 이벤트 처리 (재시도 지원)
     *
     * @param event Todo 할당 이벤트
     */
    private void processTodoAssignedEvent(TodoAssignedEvent event) {
        retryTemplate.execute(context -> {
            log.info("Todo 할당 알림 발송 시작 - TodoId: {}, EventId: {}",
                event.getTodoId(), event.getEventId());

            notificationService.sendTodoAssignment(event);

            log.info("Todo 할당 알림 발송 완료 - TodoId: {}", event.getTodoId());
            return null;
        });
    }
}
