import { ServerValue } from "firebase-admin/database"; import { rtdb } from "../firebase"; import type { VoteEntry } from "../types/panit"; import type { DateString } from "../types/dateString"; /** * 특정 경기의 해당 유저 투표를 조회한다. * * @param gameId - 경기 ID * @param uid - 유저 ID * @returns 유저의 투표 정보. 없으면 `null`. */ export async function getUserVote(gameId: string, uid: string): Promise { const snap = await rtdb.ref(`/votes/${gameId}/users/${uid}`).get(); return snap.exists() ? (snap.val() as VoteEntry) : null; } /** * 경기의 홈/원정 투표 카운트를 조회한다. 값이 없으면 0으로 채운다. * * @param gameId - 경기 ID */ export async function getCounts( gameId: string ): Promise<{ homeCount: number; awayCount: number }> { const snap = await rtdb.ref(`/votes/${gameId}/counts`).get(); const val = snap.val() ?? {}; return { homeCount: Number(val.homeCount ?? 0), awayCount: Number(val.awayCount ?? 0), }; } /** * 특정 경기에 투표한 모든 유저의 기록을 조회한다. * * @param gameId - 경기 ID * @returns `{ [uid]: VoteEntry }` 맵. 아무도 없으면 빈 객체. */ export async function getAllUserVotes( gameId: string ): Promise> { const snap = await rtdb.ref(`/votes/${gameId}/users`).get(); return snap.exists() ? (snap.val() as Record) : {}; } /** * 새로운 투표를 제출한다. 카운트 증가, 유저별 투표, 날짜별 유저 투표 인덱스를 원자적으로 갱신한다. * * @param params.gameId - 경기 ID * @param params.uid - 투표한 유저 ID * @param params.date - 경기 날짜 (유저별 날짜 인덱스 경로에 사용) * @param params.side - 투표 진영 (`home` | `away`) * @param params.team - 투표한 팀 코드 */ export async function submitVote(params: { gameId: string; uid: string; date: DateString; side: "home" | "away"; team: string; }): Promise { const { gameId, uid, date, side, team } = params; const counterKey = side === "home" ? "homeCount" : "awayCount"; const updates: Record = {}; updates[`/votes/${gameId}/counts/${counterKey}`] = ServerValue.increment(1); updates[`/votes/${gameId}/users/${uid}`] = { team }; updates[`/userVotes/${uid}/${date}/${gameId}`] = { team }; await rtdb.ref().update(updates); } /** * 기존 투표를 다른 진영/팀으로 변경한다. 이전 카운트를 감소시키고 새 카운트를 증가시킨다. * * @param params.gameId - 경기 ID * @param params.uid - 유저 ID * @param params.date - 경기 날짜 * @param params.oldSide - 기존 투표 진영 * @param params.newSide - 변경할 진영 * @param params.newTeam - 변경할 팀 코드 */ export async function changeVote(params: { gameId: string; uid: string; date: DateString; oldSide: "home" | "away"; newSide: "home" | "away"; newTeam: string; }): Promise { const { gameId, uid, date, oldSide, newSide, newTeam } = params; const updates: Record = {}; updates[`/votes/${gameId}/counts/${oldSide}Count`] = ServerValue.increment(-1); updates[`/votes/${gameId}/counts/${newSide}Count`] = ServerValue.increment(1); updates[`/votes/${gameId}/users/${uid}`] = { team: newTeam }; updates[`/userVotes/${uid}/${date}/${gameId}`] = { team: newTeam }; await rtdb.ref().update(updates); } /** * 특정 유저가 특정 날짜에 한 모든 경기 투표를 조회한다. * * @param uid - 유저 ID * @param date - 날짜 (YYYY-MM-DD) * @returns `{ [gameId]: VoteEntry }` 맵. 투표가 없으면 빈 객체. */ export async function getUserDateVotes( uid: string, date: DateString ): Promise> { const snap = await rtdb.ref(`/userVotes/${uid}/${date}`).get(); return snap.exists() ? (snap.val() as Record) : {}; } /** * 특정 경기의 모든 투표 데이터를 삭제한다 (카운트/유저 투표 전체). * * 주의: `/userVotes/{uid}/{date}/{gameId}` 인덱스는 이 함수로 정리되지 않는다. * * @param gameId - 삭제할 경기 ID */ export async function deleteGameVotes(gameId: string): Promise { await rtdb.ref(`/votes/${gameId}`).remove(); } /** * 유저별 일자 인덱스에서 특정 경기 항목을 제거한다. * 취소/리컨실리 경로에서 아카이브 대상에서 제외하기 위해 사용. */ export async function deleteUserVoteGame( uid: string, date: DateString, gameId: string ): Promise { await rtdb.ref(`/userVotes/${uid}/${date}/${gameId}`).remove(); }