/**
 * 검토자 배정 로직
 *
 * 자기 검토 금지, 연속 배정 제한, 라운드 로빈 선택을 통해
 * 최적의 검토자를 배정합니다.
 */

import * as FirebaseFirestore from '@google-cloud/firestore';

// ── 타입 정의 ─────────────────────────────────────────────────────────────────

export interface ReviewerCandidate {
  uid: string;
  name: string;
  role: string; // 'reviewer' | 'admin'
  recentAssignmentCount: number; // 최근 7일 배정 횟수
  lastAssignedAt?: FirebaseFirestore.Timestamp;
}

// ── 리뷰어 풀 조회 ────────────────────────────────────────────────────────────

/**
 * users 컬렉션에서 role이 reviewer 또는 admin인 유저를 조회하고
 * 각 유저의 최근 7일 배정 횟수를 함께 계산합니다.
 */
export async function getReviewerPool(
  db: FirebaseFirestore.Firestore
): Promise<ReviewerCandidate[]> {
  const sevenDaysAgo = new Date();
  sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

  const usersSnapshot = await db
    .collection('users')
    .where('role', 'in', ['reviewer', 'admin'])
    .get();

  const candidates: ReviewerCandidate[] = await Promise.all(
    usersSnapshot.docs.map(async (doc) => {
      const data = doc.data();

      // 최근 7일 배정 횟수 조회
      const assignmentSnapshot = await doc.ref
        .collection('assignments')
        .where('assignedAt', '>', sevenDaysAgo)
        .get();

      return {
        uid: doc.id,
        name: data['name'] ?? data['displayName'] ?? '',
        role: data['role'] ?? 'reviewer',
        recentAssignmentCount: assignmentSnapshot.size,
        lastAssignedAt: data['lastAssignedAt'],
      };
    })
  );

  return candidates;
}

// ── 자기 검토 금지 필터 ───────────────────────────────────────────────────────

/**
 * contributorIds에 포함된 reviewer를 풀에서 제외합니다.
 */
export function filterSelfReviewers(
  pool: ReviewerCandidate[],
  contributorIds: string[]
): ReviewerCandidate[] {
  if (contributorIds.length === 0) return pool;
  const contributorSet = new Set(contributorIds);
  return pool.filter((candidate) => !contributorSet.has(candidate.uid));
}

// ── 연속 배정 제한 ────────────────────────────────────────────────────────────

/**
 * 최근 2건의 리뷰어 ID에 포함된 reviewer를 풀에서 제외합니다.
 * 동일 문서에 연속 2회 이상 배정되지 않도록 합니다.
 */
export function filterConsecutiveAssignment(
  pool: ReviewerCandidate[],
  recentReviewerIds: string[]
): ReviewerCandidate[] {
  if (recentReviewerIds.length === 0) return pool;
  const recentSet = new Set(recentReviewerIds);
  return pool.filter((candidate) => !recentSet.has(candidate.uid));
}

// ── 라운드 로빈 선택 ──────────────────────────────────────────────────────────

/**
 * 배정 횟수가 가장 적은 reviewer를 선택합니다.
 * 동일 횟수이면 무작위(Math.random shuffle)로 선택합니다.
 */
export function selectReviewerRoundRobin(
  pool: ReviewerCandidate[]
): ReviewerCandidate | null {
  if (pool.length === 0) return null;

  // 최소 배정 횟수 찾기
  const minCount = Math.min(...pool.map((c) => c.recentAssignmentCount));

  // 최소 횟수를 가진 후보들 필터링
  const minCandidates = pool.filter((c) => c.recentAssignmentCount === minCount);

  // 무작위 셔플 후 첫 번째 선택
  const shuffled = [...minCandidates].sort(() => Math.random() - 0.5);
  return shuffled[0];
}

// ── 리뷰어 풀 최소 3명 체크 ──────────────────────────────────────────────────

/**
 * 풀에 리뷰어가 최소 3명 이상인지 확인합니다.
 * 3명 미만이면 admin 경고가 필요합니다.
 */
export function validatePoolSize(pool: ReviewerCandidate[]): {
  valid: boolean;
  count: number;
} {
  return {
    valid: pool.length >= 3,
    count: pool.length,
  };
}

// ── 통합 배정 함수 ────────────────────────────────────────────────────────────

interface AssignReviewerParams {
  docId: string;
  authorId?: string;
  contributorIds: string[];
  db: FirebaseFirestore.Firestore;
  recentReviewerIds?: string[]; // 최근 2건의 리뷰어 ID (연속 배정 제한용)
}

interface AssignReviewerResult {
  reviewer: ReviewerCandidate | null;
  poolSize: number;
  poolWarning: boolean; // 3명 미만 시 true
}

/**
 * 자기 검토 금지, 연속 배정 제한, 라운드 로빈 선택을 적용하여
 * 최적의 검토자를 배정합니다.
 */
export async function assignReviewer(
  params: AssignReviewerParams
): Promise<AssignReviewerResult> {
  const { docId: _docId, authorId, contributorIds, db, recentReviewerIds = [] } = params;

  // 1. 전체 리뷰어 풀 조회
  const fullPool = await getReviewerPool(db);

  // 2. 자기 검토 금지 필터 (contributorIds + authorId 병합)
  const excludeIds = authorId
    ? [...new Set([...contributorIds, authorId])]
    : contributorIds;
  const poolAfterSelfFilter = filterSelfReviewers(fullPool, excludeIds);

  // 3. 연속 배정 제한 필터
  const poolAfterConsecutiveFilter = filterConsecutiveAssignment(
    poolAfterSelfFilter,
    recentReviewerIds
  );

  // 4. 풀 크기 검증
  const { valid, count } = validatePoolSize(poolAfterConsecutiveFilter);

  // 5. 라운드 로빈으로 리뷰어 선택
  const reviewer = selectReviewerRoundRobin(poolAfterConsecutiveFilter);

  return {
    reviewer,
    poolSize: count,
    poolWarning: !valid,
  };
}
