import type { Achievement, Match, PassportStats, Prediction, Stadium, Sticker, Team, } from "../types/passport"; export interface PassportMetricInput { teams: Team[]; stadiums: Stadium[]; matches: Match[]; stickers: Sticker[]; achievements: Achievement[]; predictions: Prediction[]; triviaAnsweredCount: number; triviaCorrectCount: number; triviaTotalCount: number; memoryCount: number; currentStreak: number; } export function getCompletionRate(value: number, total: number): number { if (total <= 0) { return 0; } return Math.min(100, Math.max(0, Math.round((value / total) * 100))); } export function formatCompactNumber(value: number): string { return new Intl.NumberFormat("en-GB", { notation: value >= 10000 ? "compact" : "standard", maximumFractionDigits: 1, }).format(value); } export function formatMatchDate( isoDate: string, style: "short" | "long" | "time" = "short", ): string { const date = new Date(isoDate); const optionsByStyle: Record<"short" | "long" | "time", Intl.DateTimeFormatOptions> = { short: { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", timeZone: "UTC", }, long: { weekday: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", timeZone: "UTC", }, time: { hour: "2-digit", minute: "2-digit", timeZone: "UTC", }, }; return new Intl.DateTimeFormat("en-GB", optionsByStyle[style]).format(date); } export function getTeamById(teams: Team[], teamId: string): Team | undefined { return teams.find((team) => team.id === teamId); } export function getStadiumById(stadiums: Stadium[], stadiumId: string): Stadium | undefined { return stadiums.find((stadium) => stadium.id === stadiumId); } export function getCompletedGroups(teams: Team[]): string[] { const groups = new Map(); teams.forEach((team) => { const existing = groups.get(team.group) ?? []; groups.set(team.group, [...existing, team]); }); return Array.from(groups.entries()) .filter(([, groupTeams]) => groupTeams.length > 0 && groupTeams.every((team) => team.collected)) .map(([group]) => group); } export function countWatchedMatchesForTeam(matches: Match[], teamId: string): number { return matches.filter( (match) => match.watched && (match.homeTeamId === teamId || match.awayTeamId === teamId), ).length; } export function calculatePassportStats(input: PassportMetricInput): PassportStats { const collectedTeams = input.teams.filter((team) => team.collected).length; const watchedMatches = input.matches.filter((match) => match.watched).length; const collectedStadiums = input.stadiums.filter((stadium) => stadium.collected).length; const obtainedStickers = input.stickers.filter((sticker) => sticker.obtained).length; const unlockedAchievements = input.achievements.filter((achievement) => achievement.unlocked).length; const predictionPoints = input.predictions.reduce((total, prediction) => { if (prediction.result === "won") { return total + prediction.points; } if (prediction.userPickOptionId) { return total + Math.round(prediction.points * 0.15); } return total; }, 0); const earnedItems = collectedTeams + watchedMatches + collectedStadiums + obtainedStickers + unlockedAchievements + input.predictions.filter((prediction) => Boolean(prediction.userPickOptionId)).length + input.triviaAnsweredCount + input.memoryCount; const totalItems = input.teams.length + input.matches.length + input.stadiums.length + input.stickers.length + input.achievements.length + input.predictions.length + input.triviaTotalCount + Math.max(3, input.memoryCount); const totalScore = collectedTeams * 80 + watchedMatches * 55 + collectedStadiums * 120 + obtainedStickers * 35 + unlockedAchievements * 220 + input.triviaCorrectCount * 70 + input.memoryCount * 45 + predictionPoints + input.currentStreak * 25; return { totalScore, completionPercent: getCompletionRate(earnedItems, totalItems), collectedTeams, totalTeams: input.teams.length, watchedMatches, totalMatches: input.matches.length, collectedStadiums, totalStadiums: input.stadiums.length, obtainedStickers, totalStickers: input.stickers.length, unlockedAchievements, totalAchievements: input.achievements.length, predictionPoints, triviaAnswered: input.triviaAnsweredCount, triviaCorrect: input.triviaCorrectCount, memoryCount: input.memoryCount, currentStreak: input.currentStreak, }; }