package com.unicorn.hgzero.meeting.infra.gateway;

import com.unicorn.hgzero.meeting.biz.domain.Meeting;
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
import com.unicorn.hgzero.meeting.biz.usecase.out.ParticipantReader;
import com.unicorn.hgzero.meeting.infra.gateway.entity.MeetingEntity;
import com.unicorn.hgzero.meeting.infra.gateway.repository.MeetingJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 회의 Gateway 구현체
 * MeetingReader, MeetingWriter 인터페이스 구현
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MeetingGateway implements MeetingReader, MeetingWriter {

    private final MeetingJpaRepository meetingJpaRepository;
    private final ParticipantReader participantReader;

    @Override
    public Optional<Meeting> findById(String meetingId) {
        return meetingJpaRepository.findById(meetingId)
                .map(this::enrichWithParticipants);
    }

    @Override
    public List<Meeting> findByOrganizerId(String organizerId) {
        return meetingJpaRepository.findByOrganizerId(organizerId).stream()
                .map(this::enrichWithParticipants)
                .collect(Collectors.toList());
    }

    @Override
    public List<Meeting> findByStatus(String status) {
        return meetingJpaRepository.findByStatus(status).stream()
                .map(this::enrichWithParticipants)
                .collect(Collectors.toList());
    }

    @Override
    public List<Meeting> findByOrganizerIdAndStatus(String organizerId, String status) {
        return meetingJpaRepository.findByOrganizerIdAndStatus(organizerId, status).stream()
                .map(this::enrichWithParticipants)
                .collect(Collectors.toList());
    }

    @Override
    public List<Meeting> findByScheduledTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
                .map(this::enrichWithParticipants)
                .collect(Collectors.toList());
    }

    @Override
    public List<Meeting> findByTemplateId(String templateId) {
        return meetingJpaRepository.findByTemplateId(templateId).stream()
                .map(this::enrichWithParticipants)
                .collect(Collectors.toList());
    }

    /**
     * Meeting 엔티티를 도메인으로 변환하면서 participants 정보 추가
     */
    private Meeting enrichWithParticipants(MeetingEntity entity) {
        Meeting meeting = entity.toDomain();
        List<String> participants = participantReader.findParticipantsByMeetingId(entity.getMeetingId());
        return Meeting.builder()
                .meetingId(meeting.getMeetingId())
                .title(meeting.getTitle())
                .purpose(meeting.getPurpose())
                .description(meeting.getDescription())
                .scheduledAt(meeting.getScheduledAt())
                .endTime(meeting.getEndTime())
                .location(meeting.getLocation())
                .startedAt(meeting.getStartedAt())
                .endedAt(meeting.getEndedAt())
                .status(meeting.getStatus())
                .organizerId(meeting.getOrganizerId())
                .participants(participants)
                .templateId(meeting.getTemplateId())
                .build();
    }

    @Override
    public Meeting save(Meeting meeting) {
        MeetingEntity entity = MeetingEntity.fromDomain(meeting);
        MeetingEntity savedEntity = meetingJpaRepository.save(entity);
        return savedEntity.toDomain();
    }

    @Override
    public void delete(String meetingId) {
        meetingJpaRepository.deleteById(meetingId);
    }

    @Override
    public long countConflictingMeetings(String organizerId, LocalDateTime startTime, LocalDateTime endTime) {
        return meetingJpaRepository.countConflictingMeetings(organizerId, startTime, endTime);
    }
}
