import { FastifyInstance } from 'fastify' import { requireAuth } from './authRoutes.js' import { resolveRipStatusBatch } from '../ripstatus.js' function countBy(values: string[]): { name: string; count: number }[] { const map = new Map() for (const v of values) { if (!v) continue map.set(v, (map.get(v) ?? 0) + 1) } return [...map.entries()].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count) } export async function registerStatsRoutes(app: FastifyInstance): Promise { app.get('/api/stats', { preHandler: [requireAuth] }, async (request) => { const db = request.server.db const userId = request.user!.id const items = db .prepare('SELECT id, title, artist, year, formats, genres, date_added, rip_override FROM collection_items WHERE user_id = ?') .all(userId) as { id: number title: string artist: string year: number | null formats: string genres: string date_added: string rip_override: number | null }[] const statuses = resolveRipStatusBatch(db, userId, items) let ripped = 0 const formats: string[] = [] const genres: string[] = [] const months = new Map() items.forEach((item, i) => { if (statuses[i] === 'ripped') ripped++ for (const f of JSON.parse(item.formats) as string[]) formats.push(f) for (const g of JSON.parse(item.genres) as string[]) genres.push(g) const month = (item.date_added ?? '').slice(0, 7) if (/^\d{4}-\d{2}$/.test(month)) months.set(month, (months.get(month) ?? 0) + 1) }) const onLoan = ( db.prepare('SELECT COUNT(*) AS n FROM loans WHERE user_id = ? AND returned_at IS NULL').get(userId) as { n: number } ).n const addedByMonth: { month: string; count: number }[] = [] const now = new Date() for (let i = 11; i >= 0; i--) { const d = new Date(now.getFullYear(), now.getMonth() - i, 1) const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}` addedByMonth.push({ month: key, count: months.get(key) ?? 0 }) } return { totals: { items: items.length, ripped, notRipped: items.length - ripped, onLoan }, ripRatio: items.length === 0 ? 0 : ripped / items.length, formats: countBy(formats), decades: countBy(items.map((i) => (i.year ? `${Math.floor(i.year / 10) * 10}s` : ''))), topGenres: countBy(genres).slice(0, 10), topArtists: countBy(items.map((i) => i.artist)).slice(0, 10), addedByMonth, } }) }