package com.unicorn.hgzero.meeting.biz.service;

import com.unicorn.hgzero.common.exception.BusinessException;
import com.unicorn.hgzero.common.exception.ErrorCode;
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
import com.unicorn.hgzero.meeting.biz.usecase.in.meeting.*;
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

/**
 * 회의 Service
 * Meeting 관련 모든 UseCase 구현
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MeetingService implements
        CreateMeetingUseCase,
        StartMeetingUseCase,
        EndMeetingUseCase,
        CancelMeetingUseCase,
        GetMeetingUseCase {

    private final MeetingReader meetingReader;
    private final MeetingWriter meetingWriter;

    /**
     * 회의 생성
     */
    @Override
    @Transactional
    public Meeting createMeeting(CreateMeetingCommand command) {
        log.info("Creating meeting: {}", command.title());

        // 회의 ID 생성
        String meetingId = UUID.randomUUID().toString();

        // 회의 도메인 객체 생성
        Meeting meeting = Meeting.builder()
                .meetingId(meetingId)
                .title(command.title())
                .description(command.description())
                .scheduledAt(command.scheduledAt())
                .status("SCHEDULED")
                .organizerId(command.organizerId())
                .participants(command.participants())
                .templateId(command.templateId())
                .build();

        // 회의 저장
        Meeting savedMeeting = meetingWriter.save(meeting);

        log.info("Meeting created successfully: {}", savedMeeting.getMeetingId());
        return savedMeeting;
    }

    /**
     * 회의 시작
     */
    @Override
    @Transactional
    public Meeting startMeeting(String meetingId) {
        log.info("Starting meeting: {}", meetingId);

        // 회의 조회
        Meeting meeting = meetingReader.findById(meetingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 회의 상태 검증
        if (!"SCHEDULED".equals(meeting.getStatus())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
        }

        // 회의 시작
        meeting.start();

        // 저장
        Meeting updatedMeeting = meetingWriter.save(meeting);

        log.info("Meeting started successfully: {}", meetingId);
        return updatedMeeting;
    }

    /**
     * 회의 종료
     */
    @Override
    @Transactional
    public Meeting endMeeting(String meetingId) {
        log.info("Ending meeting: {}", meetingId);

        // 회의 조회
        Meeting meeting = meetingReader.findById(meetingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 회의 상태 검증
        if (!"IN_PROGRESS".equals(meeting.getStatus())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
        }

        // 회의 종료
        meeting.end();

        // 저장
        Meeting updatedMeeting = meetingWriter.save(meeting);

        log.info("Meeting ended successfully: {}", meetingId);
        return updatedMeeting;
    }

    /**
     * 회의 취소
     */
    @Override
    @Transactional
    public Meeting cancelMeeting(String meetingId) {
        log.info("Canceling meeting: {}", meetingId);

        // 회의 조회
        Meeting meeting = meetingReader.findById(meetingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 회의 취소 가능 상태 검증
        if ("COMPLETED".equals(meeting.getStatus()) || "CANCELLED".equals(meeting.getStatus())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
        }

        // 회의 취소
        meeting.cancel();

        // 저장
        Meeting updatedMeeting = meetingWriter.save(meeting);

        log.info("Meeting cancelled successfully: {}", meetingId);
        return updatedMeeting;
    }

    /**
     * ID로 회의 조회
     */
    @Override
    @Transactional(readOnly = true)
    public Meeting getMeeting(String meetingId) {
        log.debug("Getting meeting: {}", meetingId);

        return meetingReader.findById(meetingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
    }

    /**
     * 주최자 ID로 회의 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Meeting> getMeetingsByOrganizer(String organizerId) {
        log.debug("Getting meetings by organizer: {}", organizerId);

        return meetingReader.findByOrganizerId(organizerId);
    }

    /**
     * 상태로 회의 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Meeting> getMeetingsByStatus(String status) {
        log.debug("Getting meetings by status: {}", status);

        return meetingReader.findByStatus(status);
    }

    /**
     * 일정 시간 범위로 회의 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Meeting> getMeetingsByScheduledTime(LocalDateTime startTime, LocalDateTime endTime) {
        log.debug("Getting meetings by scheduled time: {} ~ {}", startTime, endTime);

        return meetingReader.findByScheduledTimeBetween(startTime, endTime);
    }

    /**
     * 주최자 ID와 상태로 회의 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Meeting> getMeetingsByOrganizerAndStatus(String organizerId, String status) {
        log.debug("Getting meetings by organizer: {} and status: {}", organizerId, status);

        return meetingReader.findByOrganizerIdAndStatus(organizerId, status);
    }
}
