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.domain.Minutes;
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
import com.unicorn.hgzero.meeting.biz.dto.MinutesDTO;
import com.unicorn.hgzero.meeting.biz.usecase.in.minutes.*;
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesReader;
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionReader;
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionWriter;
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesWriter;
import com.unicorn.hgzero.meeting.infra.cache.CacheService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 회의록 Service
 * Minutes 관련 모든 UseCase 구현
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MinutesService implements
        CreateMinutesUseCase,
        UpdateMinutesUseCase,
        FinalizeMinutesUseCase,
        GetMinutesUseCase {

    private final MinutesReader minutesReader;
    private final MinutesWriter minutesWriter;
    private final MeetingReader meetingReader;
    private final MinutesSectionReader minutesSectionReader;
    private final MinutesSectionWriter minutesSectionWriter;
    private final CacheService cacheService;
    private final com.unicorn.hgzero.meeting.biz.usecase.out.ParticipantReader participantReader;

    /**
     * 회의록 생성
     */
    @Override
    @Transactional
    public Minutes createMinutes(CreateMinutesCommand command) {
        log.info("Creating minutes for meeting: {}", command.meetingId());

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

        // 회의록 도메인 객체 생성
        Minutes minutes = Minutes.builder()
                .minutesId(minutesId)
                .meetingId(command.meetingId())
                .title(command.title())
                .status("DRAFT")
                .version(1)
                .createdBy(command.createdBy())
                .build();

        // 회의록 저장
        Minutes savedMinutes = minutesWriter.save(minutes);

        log.info("Minutes created successfully: {}", savedMinutes.getMinutesId());
        return savedMinutes;
    }

    /**
     * 회의록 제목 수정
     */
    @Override
    @Transactional
    public Minutes updateMinutesTitle(String minutesId, String title) {
        log.info("Updating minutes title: {}", minutesId);

        // 회의록 조회
        Minutes minutes = minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 상태 검증 (확정된 회의록은 수정 불가)
        if ("FINALIZED".equals(minutes.getStatus())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
        }

        // 제목 수정
        minutes.updateTitle(title);

        // 저장
        Minutes updatedMinutes = minutesWriter.save(minutes);

        log.info("Minutes title updated successfully: {}", minutesId);
        return updatedMinutes;
    }

    /**
     * 회의록 버전 증가
     */
    @Override
    @Transactional
    public Minutes incrementVersion(String minutesId) {
        log.info("Incrementing minutes version: {}", minutesId);

        // 회의록 조회
        Minutes minutes = minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 버전 증가
        minutes.incrementVersion();

        // 저장
        Minutes updatedMinutes = minutesWriter.save(minutes);

        log.info("Minutes version incremented: {} -> {}", minutesId, updatedMinutes.getVersion());
        return updatedMinutes;
    }

    /**
     * 회의록 확정
     */
    @Override
    @Transactional
    public Minutes finalizeMinutes(String minutesId, String userId) {
        log.info("Finalizing minutes: {} by user: {}", minutesId, userId);

        // 1. 회의록 조회
        Minutes minutes = minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND, "회의록을 찾을 수 없습니다."));

        // 2. 회의 정보 조회
        Meeting meeting = meetingReader.findById(minutes.getMeetingId())
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND, "회의 정보를 찾을 수 없습니다."));

        // 3. 회의록 섹션 조회 및 설정
        List<MinutesSection> sections = minutesSectionReader.findByMinutesIdOrderByOrder(minutesId);
        minutes = Minutes.builder()
                .minutesId(minutes.getMinutesId())
                .meetingId(minutes.getMeetingId())
                .title(minutes.getTitle())
                .sections(sections)
                .status(minutes.getStatus())
                .version(minutes.getVersion())
                .createdBy(minutes.getCreatedBy())
                .createdAt(minutes.getCreatedAt())
                .lastModifiedAt(minutes.getLastModifiedAt())
                .lastModifiedBy(minutes.getLastModifiedBy())
                .finalizedBy(minutes.getFinalizedBy())
                .finalizedAt(minutes.getFinalizedAt())
                .build();

        // 4. 회의록 확정 가능 여부 검증
        minutes.validateCanConfirm(meeting, userId);

        // 5. 모든 섹션 잠금
        minutes.lockAllSections(userId);

        // 6. 회의록 확정
        minutes.finalize(userId);

        // 7. 회의록 저장
        Minutes finalizedMinutes = minutesWriter.save(minutes);

        // 8. 섹션 잠금 상태 저장 (기존 엔티티 조회 후 업데이트하므로 연관관계 유지됨)
        if (sections != null) {
            for (MinutesSection section : sections) {
                minutesSectionWriter.save(section);
            }
        }

        // 9. 캐시에 저장 (TTL: 10분) - 컨트롤러에서 처리됨

        log.info("Minutes finalized successfully: {}, version: {}", minutesId, finalizedMinutes.getVersion());
        return finalizedMinutes;
    }

    /**
     * ID로 회의록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public Minutes getMinutes(String minutesId) {
        log.debug("Getting minutes: {}", minutesId);

        return minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
    }

    /**
     * 회의 ID로 회의록 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Minutes> getMinutesByMeeting(String meetingId) {
        log.debug("Getting minutes by meeting: {}", meetingId);

        return minutesReader.findByMeetingId(meetingId);
    }

    /**
     * 회의 ID로 최신 회의록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public Minutes getLatestMinutes(String meetingId) {
        log.debug("Getting latest minutes for meeting: {}", meetingId);

        return minutesReader.findLatestByMeetingId(meetingId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
    }

    /**
     * 작성자 ID로 회의록 목록 조회
     */
    @Override
    @Transactional(readOnly = true)
    public List<Minutes> getMinutesByCreator(String createdBy) {
        log.debug("Getting minutes by creator: {}", createdBy);

        return minutesReader.findByCreatedBy(createdBy);
    }

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

        return minutesReader.findByStatus(status);
    }

    /**
     * 사용자 ID로 회의록 목록 조회 (페이징)
     */
    @Transactional(readOnly = true)
    public Page<MinutesDTO> getMinutesListByUserId(String userId, Pageable pageable) {
        log.debug("Getting minutes list by userId: {}", userId);

        // 여기서는 임시로 작성자 기준으로 조회 (실제로는 참석자나 권한 기반으로 조회해야 함)
        List<Minutes> minutesList = minutesReader.findByCreatedBy(userId);
        
        // Minutes를 MinutesDTO로 변환
        List<MinutesDTO> minutesDTOList = minutesList.stream()
                .map(this::convertToMinutesDTO)
                .collect(Collectors.toList());

        // 페이징 처리 (임시로 전체 목록 반환)
        int start = (int) pageable.getOffset();
        int end = Math.min((start + pageable.getPageSize()), minutesDTOList.size());
        List<MinutesDTO> pageContent = minutesDTOList.subList(start, end);

        return new PageImpl<>(pageContent, pageable, minutesDTOList.size());
    }

    /**
     * ID로 회의록 조회 (DTO 반환)
     */
    @Transactional(readOnly = true)
    public MinutesDTO getMinutesById(String minutesId) {
        log.debug("Getting minutes DTO by id: {}", minutesId);

        Minutes minutes = minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        return convertToMinutesDTO(minutes);
    }

    /**
     * 회의록 수정 (제목, 메모)
     */
    @Transactional
    public MinutesDTO updateMinutes(String minutesId, String title, String memo, String userId) {
        log.info("Updating minutes: {}", minutesId);

        // 회의록 조회
        Minutes minutes = minutesReader.findById(minutesId)
                .orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));

        // 상태 검증 (확정된 회의록은 수정 불가)
        if ("FINALIZED".equals(minutes.getStatus())) {
            throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
        }

        // 제목과 메모 수정
        if (title != null) {
            minutes.updateTitle(title);
        }
        // memo 필드가 없어서 제목만 수정

        // 저장
        Minutes updatedMinutes = minutesWriter.save(minutes);

        log.info("Minutes updated successfully: {}", minutesId);
        return convertToMinutesDTO(updatedMinutes);
    }

    /**
     * 회의록 확정 (DTO 반환)
     */
    @Transactional
    public MinutesDTO finalizeMinutesDTO(String minutesId, String userId) {
        log.info("Finalizing minutes: {}", minutesId);

        Minutes finalizedMinutes = finalizeMinutes(minutesId, userId);
        return convertToMinutesDTO(finalizedMinutes);
    }

    /**
     * Minutes 도메인을 MinutesDTO로 변환
     */
    private MinutesDTO convertToMinutesDTO(Minutes minutes) {
        // 회의 정보 조회
        String meetingTitle = "회의 제목 없음";
        try {
            Meeting meeting = meetingReader.findById(minutes.getMeetingId()).orElse(null);
            if (meeting != null) {
                meetingTitle = meeting.getTitle();
            }
        } catch (Exception e) {
            log.warn("회의 정보 조회 실패 - meetingId: {}", minutes.getMeetingId(), e);
        }
        
        // TODO 정보는 추후 구현 (현재는 기본값)
        int todoCount = 0;
        int completedTodoCount = 0;
        
        // 참석자 수 계산 (모든 참석자)
        int participantCount = 0;
        try {
            participantCount = participantReader.countParticipantsByMeetingId(minutes.getMeetingId());
        } catch (Exception e) {
            log.warn("참석자 수 계산 실패 - meetingId: {}", minutes.getMeetingId(), e);
        }
        
        return MinutesDTO.builder()
                .minutesId(minutes.getMinutesId())
                .meetingId(minutes.getMeetingId())
                .title(minutes.getTitle())
                .status(minutes.getStatus())
                .version(minutes.getVersion())
                .createdAt(minutes.getCreatedAt())
                .lastModifiedAt(minutes.getLastModifiedAt())
                .createdBy(minutes.getCreatedBy())
                .lastModifiedBy(minutes.getLastModifiedBy())
                .meetingTitle(meetingTitle)
                .todoCount(todoCount)
                .completedTodoCount(completedTodoCount)
                .participantCount(participantCount)
                .memo("") // 메모 필드는 추후 구현
                .build();
    }

    /**
     * 회의 ID로 참석자별 회의록 조회
     * AI Service가 통합 회의록 생성 시 사용
     *
     * @param meetingId 회의 ID
     * @return 참석자별 회의록 목록 (user_id IS NOT NULL)
     */
    @Transactional(readOnly = true)
    public List<Minutes> getParticipantMinutesByMeeting(String meetingId) {
        log.info("회의 ID로 참석자별 회의록 조회: {}", meetingId);
        return minutesReader.findParticipantMinutesByMeetingId(meetingId);
    }

    /**
     * 회의 ID로 AI 통합 회의록 조회
     *
     * @param meetingId 회의 ID
     * @return AI 통합 회의록 (user_id IS NULL)
     */
    @Transactional(readOnly = true)
    public Minutes getConsolidatedMinutesByMeeting(String meetingId) {
        log.info("회의 ID로 AI 통합 회의록 조회: {}", meetingId);
        return minutesReader.findConsolidatedMinutesByMeetingId(meetingId)
                .orElse(null);
    }
}
