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

import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
import com.unicorn.hgzero.meeting.biz.usecase.out.DashboardReader;
import com.unicorn.hgzero.meeting.infra.gateway.entity.MeetingEntity;
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesEntity;
import com.unicorn.hgzero.meeting.infra.gateway.entity.TodoEntity;
import com.unicorn.hgzero.meeting.infra.gateway.repository.MeetingJpaRepository;
import com.unicorn.hgzero.meeting.infra.gateway.repository.MeetingParticipantJpaRepository;
import com.unicorn.hgzero.meeting.infra.gateway.repository.MinutesJpaRepository;
import com.unicorn.hgzero.meeting.infra.gateway.repository.TodoJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 대시보드 Gateway 구현체
 * DashboardReader 인터페이스 구현
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DashboardGateway implements DashboardReader {

    private final MeetingJpaRepository meetingJpaRepository;
    private final MeetingParticipantJpaRepository meetingParticipantJpaRepository;
    private final MinutesJpaRepository minutesJpaRepository;
    private final TodoJpaRepository todoJpaRepository;

    @Override
    public Dashboard getDashboardByUserId(String userId) {
        log.info("대시보드 데이터 조회 시작 - userId: {}", userId);

        // 1. 다가오는 회의 목록 조회 (향후 30일, 최대 10개)
        List<Meeting> upcomingMeetings = getUpcomingMeetings(userId);
        
        // 2. 최근 회의록 목록 조회 (최근 7일, 최대 10개)
        List<Minutes> recentMinutes = getRecentMinutes(userId);
        
        // 3. 통계 정보 계산 (최근 30일 기준)
        Dashboard.Statistics statistics = calculateStatistics(userId);

        Dashboard dashboard = Dashboard.builder()
                .userId(userId)
                .period("7days")
                .upcomingMeetings(upcomingMeetings)
                .recentMinutes(recentMinutes)
                .assignedTodos(new ArrayList<>())
                .statistics(statistics)
                .build();

        log.info("대시보드 데이터 조회 완료 - userId: {}, 예정 회의: {}개, 최근 회의록: {}개", 
                userId, upcomingMeetings.size(), recentMinutes.size());

        return dashboard;
    }

    @Override
    public Dashboard getDashboardByUserIdAndPeriod(String userId, String period) {
        log.info("기간별 대시보드 데이터 조회 시작 - userId: {}, period: {}", userId, period);

        // 기간에 따른 조회 범위 계산
        LocalDateTime startTime = calculateStartTime(period);
        LocalDateTime endTime = LocalDateTime.now();

        // 1. 기간 내 다가오는 회의 목록 조회
        List<Meeting> upcomingMeetings = getUpcomingMeetingsByPeriod(userId, startTime, endTime);
        
        // 2. 기간 내 최근 회의록 목록 조회
        List<Minutes> recentMinutes = getRecentMinutesByPeriod(userId, startTime, endTime);
        
        // 3. 기간별 통계 정보 계산
        Dashboard.Statistics statistics = calculateStatisticsByPeriod(userId, startTime, endTime);

        Dashboard dashboard = Dashboard.builder()
                .userId(userId)
                .period(period)
                .upcomingMeetings(upcomingMeetings)
                .recentMinutes(recentMinutes)
                .assignedTodos(new ArrayList<>())
                .statistics(statistics)
                .build();

        log.info("기간별 대시보드 데이터 조회 완료 - userId: {}, period: {}", userId, period);
        return dashboard;
    }

    /**
     * 다가오는 회의 목록 조회
     */
    private List<Meeting> getUpcomingMeetings(String userId) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime endTime = now.plusDays(30); // 향후 30일
        
        return getUpcomingMeetingsByPeriod(userId, now, endTime);
    }

    /**
     * 기간별 다가오는 회의 목록 조회
     */
    private List<Meeting> getUpcomingMeetingsByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
        Set<String> userMeetingIds = new HashSet<>();
        
        // 주최자로 참여하는 예정/진행중 회의 조회
        List<MeetingEntity> organizerMeetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
                .filter(m -> userId.equals(m.getOrganizerId()))
                .filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
                .toList();
        
        organizerMeetings.forEach(m -> userMeetingIds.add(m.getMeetingId()));
        
        // 참석자로 참여하는 예정/진행중 회의 조회
        List<String> participantMeetingIds = meetingParticipantJpaRepository.findByUserId(userId).stream()
                .map(p -> p.getMeetingId())
                .toList();
        
        List<MeetingEntity> participantMeetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
                .filter(m -> participantMeetingIds.contains(m.getMeetingId()))
                .filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
                .toList();
        
        participantMeetings.forEach(m -> userMeetingIds.add(m.getMeetingId()));
        
        // 중복 제거된 회의 목록을 시간순 정렬하여 최대 10개만 반환
        return meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
                .filter(m -> userMeetingIds.contains(m.getMeetingId()))
                .filter(m -> "SCHEDULED".equals(m.getStatus()) || "IN_PROGRESS".equals(m.getStatus()))
                .sorted((m1, m2) -> m1.getScheduledAt().compareTo(m2.getScheduledAt()))
                .limit(10)
                .map(MeetingEntity::toDomain)
                .collect(Collectors.toList());
    }

    /**
     * 최근 회의록 목록 조회
     */
    private List<Minutes> getRecentMinutes(String userId) {
        LocalDateTime startTime = LocalDateTime.now().minusDays(7);
        return getRecentMinutesByPeriod(userId, startTime, LocalDateTime.now());
    }

    /**
     * 기간별 최근 회의록 목록 조회
     */
    private List<Minutes> getRecentMinutesByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
        Set<String> userMinutesIds = new HashSet<>();
        
        // 작성자로 참여한 회의록 조회
        List<MinutesEntity> createdMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
                .filter(m -> m.getCreatedAt().isAfter(startTime) && m.getCreatedAt().isBefore(endTime))
                .toList();
        
        createdMinutes.forEach(m -> userMinutesIds.add(m.getMinutesId()));
        
        // 참석한 회의의 회의록 조회
        List<String> participantMeetingIds = meetingParticipantJpaRepository.findByUserId(userId).stream()
                .map(p -> p.getMeetingId())
                .toList();
        
        List<MinutesEntity> participatedMinutes = minutesJpaRepository.findAll().stream()
                .filter(m -> participantMeetingIds.contains(m.getMeetingId()))
                .filter(m -> m.getCreatedAt().isAfter(startTime) && m.getCreatedAt().isBefore(endTime))
                .toList();
        
        participatedMinutes.forEach(m -> userMinutesIds.add(m.getMinutesId()));
        
        // 중복 제거 후 최종 수정 시간순 정렬하여 최대 10개만 반환
        return minutesJpaRepository.findAll().stream()
                .filter(m -> userMinutesIds.contains(m.getMinutesId()))
                .sorted((m1, m2) -> {
                    LocalDateTime time1 = m1.getUpdatedAt() != null ? m1.getUpdatedAt() : m1.getCreatedAt();
                    LocalDateTime time2 = m2.getUpdatedAt() != null ? m2.getUpdatedAt() : m2.getCreatedAt();
                    return time2.compareTo(time1); // 최신순
                })
                .limit(10)
                .map(MinutesEntity::toDomain)
                .collect(Collectors.toList());
    }


    /**
     * 통계 정보 계산
     */
    private Dashboard.Statistics calculateStatistics(String userId) {
        LocalDateTime startTime = LocalDateTime.now().minusDays(30); // 최근 30일
        LocalDateTime endTime = LocalDateTime.now();
        
        return calculateStatisticsByPeriod(userId, startTime, endTime);
    }

    /**
     * 기간별 통계 정보 계산
     */
    private Dashboard.Statistics calculateStatisticsByPeriod(String userId, LocalDateTime startTime, LocalDateTime endTime) {
        // 사용자가 관련된 모든 회의 ID 수집
        Set<String> userMeetingIds = new HashSet<>();
        
        // 주최자로 참여한 회의
        meetingJpaRepository.findByOrganizerId(userId).forEach(m -> userMeetingIds.add(m.getMeetingId()));
        
        // 참석자로 참여한 회의
        meetingParticipantJpaRepository.findByUserId(userId).stream()
                .map(p -> p.getMeetingId())
                .forEach(userMeetingIds::add);
        
        // 기간 내 회의 통계
        List<MeetingEntity> periodMeetings = meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
                .filter(m -> userMeetingIds.contains(m.getMeetingId()))
                .toList();
        
        long totalMeetings = periodMeetings.size();
        long scheduledMeetings = periodMeetings.stream().filter(m -> "SCHEDULED".equals(m.getStatus())).count();
        long inProgressMeetings = periodMeetings.stream().filter(m -> "IN_PROGRESS".equals(m.getStatus())).count();
        long completedMeetings = periodMeetings.stream().filter(m -> "COMPLETED".equals(m.getStatus())).count();
        
        // 회의록 통계 (사용자가 관련된 모든 회의록)
        Set<String> userMinutesIds = new HashSet<>();
        
        // 작성자로 참여한 회의록
        minutesJpaRepository.findByCreatedBy(userId).forEach(m -> userMinutesIds.add(m.getMinutesId()));
        
        // 참석한 회의의 회의록
        userMeetingIds.forEach(meetingId -> {
            minutesJpaRepository.findByMeetingId(meetingId).forEach(m -> userMinutesIds.add(m.getMinutesId()));
        });
        
        List<MinutesEntity> userMinutes = minutesJpaRepository.findAll().stream()
                .filter(m -> userMinutesIds.contains(m.getMinutesId()))
                .toList();
        
        long totalMinutes = userMinutes.size();
        long draftMinutes = userMinutes.stream().filter(m -> "DRAFT".equals(m.getStatus())).count();
        long finalizedMinutes = userMinutes.stream().filter(m -> "FINALIZED".equals(m.getStatus())).count();
        
        // Todo 통계
        List<TodoEntity> userTodos = todoJpaRepository.findByAssigneeId(userId);
        long totalTodos = userTodos.size();
        long pendingTodos = userTodos.stream().filter(t -> "PENDING".equals(t.getStatus())).count();
        long completedTodos = userTodos.stream().filter(t -> "COMPLETED".equals(t.getStatus())).count();
        long overdueTodos = userTodos.stream()
                .filter(t -> t.getDueDate() != null 
                        && LocalDate.now().isAfter(t.getDueDate()) 
                        && !"COMPLETED".equals(t.getStatus()))
                .count();

        return Dashboard.Statistics.builder()
                .totalMeetings((int) totalMeetings)
                .scheduledMeetings((int) scheduledMeetings)
                .inProgressMeetings((int) inProgressMeetings)
                .completedMeetings((int) completedMeetings)
                .totalMinutes((int) totalMinutes)
                .draftMinutes((int) draftMinutes)
                .finalizedMinutes((int) finalizedMinutes)
                .totalTodos((int) totalTodos)
                .pendingTodos((int) pendingTodos)
                .completedTodos((int) completedTodos)
                .overdueTodos((int) overdueTodos)
                .build();
    }

    /**
     * 기간 문자열로부터 시작 시간 계산
     */
    private LocalDateTime calculateStartTime(String period) {
        LocalDateTime now = LocalDateTime.now();

        return switch (period.toLowerCase()) {
            case "1day" -> now.minusDays(1);
            case "3days" -> now.minusDays(3);
            case "7days" -> now.minusDays(7);
            case "30days" -> now.minusDays(30);
            case "90days" -> now.minusDays(90);
            case "week" -> now.minusWeeks(1);
            case "month" -> now.minusMonths(1);
            case "quarter" -> now.minusMonths(3);
            case "year" -> now.minusYears(1);
            default -> now.minusDays(7); // 기본값: 7일
        };
    }
}
