package com.unicorn.hgzero.meeting.infra.event.publisher;

import com.azure.messaging.eventhubs.EventData;
import com.azure.messaging.eventhubs.EventDataBatch;
import com.azure.messaging.eventhubs.EventHubProducerClient;
import com.azure.messaging.eventhubs.models.CreateBatchOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.meeting.infra.event.constant.EventHubConstants;
import com.unicorn.hgzero.meeting.infra.event.dto.MeetingStartedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MeetingEndedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
import com.unicorn.hgzero.meeting.infra.event.dto.MinutesAnalysisRequestEvent;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
 * Azure EventHub 이벤트 발행 구현체
 * Azure EventHub를 통한 이벤트 발행
 */
@Component
@Slf4j
@org.springframework.boot.autoconfigure.condition.ConditionalOnBean(name = "eventProducer")
public class EventHubPublisher implements EventPublisher {

    private final EventHubProducerClient eventProducer;
    private final ObjectMapper objectMapper;

    public EventHubPublisher(
            @Qualifier("eventProducer") EventHubProducerClient eventProducer,
            ObjectMapper objectMapper) {
        this.eventProducer = eventProducer;
        this.objectMapper = objectMapper;
    }

    @Override
    public void publishMeetingStarted(MeetingStartedEvent event) {
        publishEvent(event, event.getMeetingId(),
                EventHubConstants.TOPIC_MEETING,
                EventHubConstants.EVENT_TYPE_MEETING_STARTED);
    }

    @Override
    public void publishMeetingEnded(MeetingEndedEvent event) {
        publishEvent(event, event.getMeetingId(),
                EventHubConstants.TOPIC_MEETING,
                EventHubConstants.EVENT_TYPE_MEETING_ENDED);
    }

    @Override
    public void publishTodoAssigned(TodoAssignedEvent event) {
        publishEvent(event, event.getTodoId(),
                EventHubConstants.TOPIC_TODO,
                EventHubConstants.EVENT_TYPE_TODO_ASSIGNED);
    }

    @Override
    public void publishNotificationRequest(NotificationRequestEvent event) {
        publishEvent(event, event.getRecipientEmail(),
                EventHubConstants.TOPIC_NOTIFICATION,
                EventHubConstants.EVENT_TYPE_NOTIFICATION_REQUEST);
    }

    @Override
    public void publishTodoAssigned(String todoId, String title, String assigneeId, String assigneeName,
                                   String assignedBy, String assignedByName, LocalDate dueDate) {
        TodoAssignedEvent event = TodoAssignedEvent.builder()
                .todoId(todoId)
                .title(title)
                .assigneeId(assigneeId)
                .assigneeName(assigneeName)
                .assignedBy(assignedBy)
                .assignedByName(assignedByName)
                .dueDate(dueDate)
                .assignedAt(LocalDateTime.now())
                .build();

        publishTodoAssigned(event);
    }

    @Override
    public void publishTodoCompleted(String todoId, String title, String assigneeId, String assigneeName,
                                    String completedBy, String completedByName) {
        NotificationRequestEvent event = NotificationRequestEvent.builder()
                .notificationType("TODO_COMPLETED")
                .recipientId(assigneeId)
                .recipientName(assigneeName)
                .title("Todo 완료")
                .message(String.format("Todo '%s'가 완료되었습니다", title))
                .relatedEntityId(todoId)
                .relatedEntityType("TODO")
                .requestedBy(completedBy)
                .requestedByName(completedByName)
                .eventTime(LocalDateTime.now())
                .build();

        publishEvent(event, todoId,
                EventHubConstants.TOPIC_TODO,
                EventHubConstants.EVENT_TYPE_TODO_COMPLETED);
    }

    @Override
    public void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName) {
        NotificationRequestEvent event = NotificationRequestEvent.builder()
                .notificationType("MINUTES_FINALIZED")
                .recipientId(finalizedBy)
                .recipientName(finalizedByName)
                .title("회의록 확정")
                .message(String.format("회의록 '%s'가 확정되었습니다", title))
                .relatedEntityId(minutesId)
                .relatedEntityType("MINUTES")
                .requestedBy(finalizedBy)
                .requestedByName(finalizedByName)
                .eventTime(LocalDateTime.now())
                .build();

        publishEvent(event, minutesId,
                EventHubConstants.TOPIC_MINUTES,
                EventHubConstants.EVENT_TYPE_MINUTES_FINALIZED);
    }

    @Override
    public void publishMeetingCreated(String meetingId, String title, LocalDateTime startTime,
                                     String location, java.util.List<String> participants,
                                     String organizerId, String organizerName) {
        // 각 참석자에게 개별 알림 이벤트 발행
        for (String participantEmail : participants) {
            NotificationRequestEvent event = NotificationRequestEvent.builder()
                    .notificationType("MEETING_INVITATION")
                    .recipientEmail(participantEmail)
                    .recipientId(participantEmail)
                    .recipientName(participantEmail)
                    .title("회의 초대")
                    .message(String.format("'%s' 회의에 초대되었습니다. 일시: %s, 장소: %s",
                            title, startTime, location))
                    .relatedEntityId(meetingId)
                    .relatedEntityType("MEETING")
                    .requestedBy(organizerId)
                    .requestedByName(organizerName)
                    .eventTime(LocalDateTime.now())
                    .build();

            publishNotificationRequest(event);
        }

        log.info("회의 생성 알림 발행 완료 - meetingId: {}, participants count: {}",
                meetingId, participants.size());
    }

    @Override
    public void publishMinutesAnalysisRequest(MinutesAnalysisRequestEvent event) {
        publishEvent(event, event.getMinutesId(),
                EventHubConstants.TOPIC_AI_ANALYSIS,
                EventHubConstants.EVENT_TYPE_MINUTES_ANALYSIS_REQUEST);
    }

    /**
     * 이벤트 발행 공통 메서드
     *
     * @param event 발행할 이벤트 객체
     * @param partitionKey 파티션 키 (이벤트 ID 등)
     * @param topic 토픽 이름
     * @param eventType 이벤트 타입
     * @param <T> 이벤트 타입
     */
    private <T> void publishEvent(T event, String partitionKey, String topic, String eventType) {
        try {
            CreateBatchOptions options = new CreateBatchOptions()
                    .setPartitionKey(partitionKey);

            EventDataBatch batch = eventProducer.createBatch(options);
            String eventJson = objectMapper.writeValueAsString(event);
            EventData eventData = new EventData(eventJson);

            // 이벤트 속성 설정
            eventData.getProperties().put(EventHubConstants.PROPERTY_TYPE, eventType);
            eventData.getProperties().put(EventHubConstants.PROPERTY_TOPIC, topic);

            if (!batch.tryAdd(eventData)) {
                throw new RuntimeException("이벤트 크기가 너무 큽니다");
            }

            eventProducer.send(batch);
            log.info("이벤트 발행 완료: topic={}, type={}, partitionKey={}",
                    topic, eventType, partitionKey);

        } catch (Exception e) {
            log.error("이벤트 발행 실패: topic={}, type={}, partitionKey={}, error={}",
                    topic, eventType, partitionKey, e.getMessage(), e);
            throw new RuntimeException("이벤트 발행 중 오류가 발생했습니다", e);
        }
    }
}
