package dev.awesomedev.mirim.service;

import dev.awesomedev.mirim.domain.CodingSubmission;
import dev.awesomedev.mirim.domain.CourseProgress;
import dev.awesomedev.mirim.domain.Progress;
import dev.awesomedev.mirim.domain.QuizResult;
import dev.awesomedev.mirim.domain.StudentProfile;
import dev.awesomedev.mirim.domain.Submission;
import dev.awesomedev.mirim.domain.User;
import dev.awesomedev.mirim.repository.ChecklistItemRepository;
import dev.awesomedev.mirim.repository.CodingProblemRepository;
import dev.awesomedev.mirim.repository.CodingSubmissionRepository;
import dev.awesomedev.mirim.repository.CourseProgressRepository;
import dev.awesomedev.mirim.repository.ProgressRepository;
import dev.awesomedev.mirim.repository.QuizResultRepository;
import dev.awesomedev.mirim.repository.StudentProfileRepository;
import dev.awesomedev.mirim.repository.SubmissionRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 이 파일이 하는 일:
 * 멘토용 "코호트(기수) 분석" — 학생 개개인 카드로는 안 보이는 "묶어서 본 그림"을 만든다.
 *  - 코호트별 KPI(진도·퀴즈·코딩·리뷰대기), 학생별 요약 + 마지막 활동/관심필요,
 *  - "어려운 지점"(평균 점수가 낮은 퀴즈·통과율이 낮은 코딩 문제) top 5.
 *
 * 학습 포인트 ① — "데이터의 주인"을 지킨다.
 *   서버는 자기가 저장한 것(퀴즈 점수·코딩 통과·활동 시각)만 집계한다. "코스 총 몇 개"는
 *   프론트 카탈로그의 지식이라 여기서 %로 만들지 않고 coursesDone(원자료)만 준다 — 화면이 계산.
 *
 * 학습 포인트 ② — 지금 규모(학생 4명)에선 학생마다 조회(N+1)해도 충분히 빠르다.
 *   수백 명이 되면 집계 쿼리(GROUP BY)로 바꾼다. "규모가 설계를 정한다."
 */
@Service
public class MentorAnalyticsService {

    /** 이 일수를 넘게 아무 활동이 없으면 '관심 필요'로 본다. */
    public static final int STALE_DAYS = 7;

    private final StudentProfileRepository studentProfileRepository;
    private final QuizResultRepository quizResultRepository;
    private final CodingSubmissionRepository codingSubmissionRepository;
    private final CodingProblemRepository codingProblemRepository;
    private final CourseProgressRepository courseProgressRepository;
    private final ProgressRepository progressRepository;
    private final ChecklistItemRepository checklistItemRepository;
    private final SubmissionRepository submissionRepository;
    private final Clock clock;

    public MentorAnalyticsService(StudentProfileRepository studentProfileRepository,
                                  QuizResultRepository quizResultRepository,
                                  CodingSubmissionRepository codingSubmissionRepository,
                                  CodingProblemRepository codingProblemRepository,
                                  CourseProgressRepository courseProgressRepository,
                                  ProgressRepository progressRepository,
                                  ChecklistItemRepository checklistItemRepository,
                                  SubmissionRepository submissionRepository,
                                  Clock clock) {
        this.studentProfileRepository = studentProfileRepository;
        this.quizResultRepository = quizResultRepository;
        this.codingSubmissionRepository = codingSubmissionRepository;
        this.codingProblemRepository = codingProblemRepository;
        this.courseProgressRepository = courseProgressRepository;
        this.progressRepository = progressRepository;
        this.checklistItemRepository = checklistItemRepository;
        this.submissionRepository = submissionRepository;
        this.clock = clock;
    }

    public record CohortCount(String cohort, int count) {
    }

    /** 학생 한 명의 분석 요약 한 줄. lastActiveAt이 null이면 아직 아무 활동도 없다. */
    public record StudentRow(Long userId, String name, String track, String cohort,
                             int coursesDone, int quizAttempted, int quizAvgPct,
                             int codingPassed, int codingTotal,
                             int checklistDone, int checklistTotal,
                             String lastActiveAt, Long daysSinceActive) {
    }

    public record QuizHotspot(String courseSlug, int avgScorePct, int attempts) {
    }

    public record CodingHotspot(Long problemId, String title, int passRatePct, int attempts) {
    }

    public record Analytics(String cohort, int studentCount, int pendingReviews,
                            List<CohortCount> cohorts, List<StudentRow> students,
                            List<QuizHotspot> hardestQuizzes, List<CodingHotspot> hardestCoding) {
    }

    @Transactional(readOnly = true)
    public Analytics analytics(String cohortFilter) {
        List<StudentProfile> all = studentProfileRepository.findAllWithUser();

        // 코호트 목록 + 인원수 (필터와 무관하게 전체 기준 — 화면의 기수 선택지가 된다)
        Map<String, Integer> counts = new TreeMap<>();
        for (StudentProfile p : all) {
            counts.merge(p.getCohort(), 1, Integer::sum);
        }
        List<CohortCount> cohorts = counts.entrySet().stream()
                .map(e -> new CohortCount(e.getKey(), e.getValue()))
                .toList();

        boolean allCohorts = cohortFilter == null || cohortFilter.isBlank() || "all".equals(cohortFilter);
        List<StudentProfile> scoped = allCohorts ? all
                : all.stream().filter(p -> cohortFilter.equals(p.getCohort())).toList();

        int codingTotal = (int) codingProblemRepository.count();
        int checklistTotal = (int) checklistItemRepository.count();
        Instant now = clock.instant();

        // 핫스팟 누적기: 코스별 [점수합, 응시자수] / 문제별 [통과수, 시도자수]
        Map<String, int[]> quizAgg = new HashMap<>();
        Map<Long, int[]> codeAgg = new HashMap<>();
        Map<Long, String> codeTitle = new HashMap<>();
        int pendingReviews = 0;

        List<StudentRow> rows = new ArrayList<>();
        for (StudentProfile p : scoped) {
            User u = p.getUser();
            Instant last = null;

            // 퀴즈 — 코스별 최고 점수(%)
            Map<String, Integer> bestPct = new HashMap<>();
            for (QuizResult qr : quizResultRepository.findByUserOrderBySubmittedAtDesc(u)) {
                int pct = qr.getTotal() > 0 ? qr.getScore() * 100 / qr.getTotal() : 0;
                bestPct.merge(qr.getCourseSlug(), pct, Math::max);
                last = later(last, qr.getSubmittedAt());
            }
            int quizAttempted = bestPct.size();
            int quizAvgPct = quizAttempted == 0 ? 0
                    : (int) Math.round(bestPct.values().stream().mapToInt(Integer::intValue).average().orElse(0));
            for (Map.Entry<String, Integer> e : bestPct.entrySet()) {
                int[] agg = quizAgg.computeIfAbsent(e.getKey(), k -> new int[2]);
                agg[0] += e.getValue();
                agg[1] += 1;
            }

            // 코딩 — 통과 수 + 문제별 통과율 누적
            int codingPassed = 0;
            for (CodingSubmission cs : codingSubmissionRepository.findByUser(u)) {
                boolean passed = "PASSED".equals(cs.getStatus());
                if (passed) {
                    codingPassed++;
                }
                Long pid = cs.getProblem().getId();
                int[] agg = codeAgg.computeIfAbsent(pid, k -> new int[2]);
                if (passed) {
                    agg[0] += 1;
                }
                agg[1] += 1;
                codeTitle.putIfAbsent(pid, cs.getProblem().getTitle());
                last = later(last, cs.getSubmittedAt());
            }

            // 코스 진도 (완료한 slug 수) — 총 코스 수는 프론트가 카탈로그로 안다
            List<CourseProgress> courses = courseProgressRepository.findByUser(u);
            for (CourseProgress cp : courses) {
                last = later(last, cp.getCompletedAt());
            }

            // 체크리스트 완료 수 + 체크한 시각도 "활동"으로 반영한다.
            // (안 그러면 최근에 체크리스트만 열심히 한 학생이 '관심 필요'로 잘못 잡힌다.)
            List<Progress> progresses = progressRepository.findByUser(u);
            for (Progress pr : progresses) {
                last = later(last, pr.getCheckedAt());
            }
            int checklistDone = progresses.size();

            // 과제 — 리뷰 대기(SUBMITTED) 집계 + 활동 시각
            for (Submission s : submissionRepository.findByUserOrderBySubmittedAtDesc(u)) {
                if ("SUBMITTED".equals(s.getStatus())) {
                    pendingReviews++;
                }
                last = later(last, s.getSubmittedAt());
            }

            Long daysSince = last == null ? null : Duration.between(last, now).toDays();
            rows.add(new StudentRow(u.getId(), u.getName(), u.getTrack(), p.getCohort(),
                    courses.size(), quizAttempted, quizAvgPct, codingPassed, codingTotal,
                    checklistDone, checklistTotal,
                    last == null ? null : last.toString(), daysSince));
        }

        // 어려운 지점 = 평균이 낮은 순 top 5 (응시/시도가 있는 것만)
        List<QuizHotspot> hardestQuizzes = quizAgg.entrySet().stream()
                .map(e -> new QuizHotspot(e.getKey(),
                        (int) Math.round((double) e.getValue()[0] / e.getValue()[1]), e.getValue()[1]))
                .sorted(Comparator.comparingInt(QuizHotspot::avgScorePct))
                .limit(5)
                .toList();
        List<CodingHotspot> hardestCoding = codeAgg.entrySet().stream()
                .map(e -> new CodingHotspot(e.getKey(), codeTitle.get(e.getKey()),
                        e.getValue()[0] * 100 / e.getValue()[1], e.getValue()[1]))
                .sorted(Comparator.comparingInt(CodingHotspot::passRatePct))
                .limit(5)
                .toList();

        String label = allCohorts ? "전체" : cohortFilter;
        return new Analytics(label, scoped.size(), pendingReviews, cohorts, rows,
                hardestQuizzes, hardestCoding);
    }

    /** 두 시각 중 나중 것(둘 중 하나가 null이면 나머지). */
    private static Instant later(Instant a, Instant b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.isAfter(b) ? a : b;
    }
}
