import { db } from "./client"; import { generateUUID } from "../utils/uuid"; import { workouts, workoutSessions, sets, laps, templates } from "./schema"; import { eq, desc, sql } from "drizzle-orm"; import type { WorkoutSet, LapData } from "@types/workout"; import WatchConnectivity from "../modules/watch-connectivity"; // ─── Templates ─────────────────────────────────────────────── export async function saveTemplate(name: string, poolLength: number, setsData: WorkoutSet[]) { const id = generateUUID(); await db.insert(templates).values({ id, name, poolLength, jsonData: JSON.stringify(setsData), createdAt: new Date().toISOString(), }); return id; } export async function getTemplates() { return db.select().from(templates).orderBy(desc(templates.createdAt)); } export async function deleteTemplate(id: string) { await db.delete(templates).where(eq(templates.id, id)); } export async function toggleTemplateFavorite(id: string, isFavorite: boolean) { await db.update(templates).set({ isFavorite }).where(eq(templates.id, id)); } // ─── Workout Plans ─────────────────────────────────────────── export async function saveWorkout( name: string | null, poolLength: number, setsData: WorkoutSet[], templateId?: string ) { const workoutId = generateUUID(); await db.insert(workouts).values({ id: workoutId, name, templateId: templateId ?? null, poolLength, createdAt: new Date().toISOString(), }); for (let i = 0; i < setsData.length; i++) { const s = setsData[i]; await db.insert(sets).values({ id: generateUUID(), workoutId, position: i + 1, stroke: s.stroke, distancePerRep: s.distancePerRep, repCount: s.repCount, restType: s.restType, restDuration: s.restDuration, targetTime: s.targetTime ?? null, }); } return workoutId; } export async function getWorkoutWithSets(workoutId: string) { const workout = await db.select().from(workouts).where(eq(workouts.id, workoutId)).get(); if (!workout) return null; const workoutSets = await db.select().from(sets).where(eq(sets.workoutId, workoutId)); return { ...workout, sets: workoutSets }; } export async function getAllWorkouts() { return db.select().from(workouts).orderBy(desc(workouts.createdAt)); } // ─── Workout Sessions ──────────────────────────────────────── export async function finishWorkout( workoutId: string, duration: number, calories: number, avgHeartRate: number, totalDistance: number ) { const sessionId = generateUUID(); const workout = await db.select().from(workouts).where(eq(workouts.id, workoutId)).get(); await db.insert(workoutSessions).values({ id: sessionId, workoutId, name: workout?.name ?? null, poolLength: workout?.poolLength ?? 25, startedAt: new Date().toISOString(), finishedAt: new Date().toISOString(), totalDistance, totalDuration: duration, calories, avgHeartRate, source: "phone", }); return sessionId; } export async function saveWatchCompletedWorkout(data: { workoutId: string; sessionId?: string; duration: number; totalDistance: number; calories: number; laps: Array<{ setPosition: number; repNumber: number; distance: number; duration: number; heartRate: number; strokeCount?: number; }>; }) { const avgHeartRate = data.laps.length > 0 ? Math.round(data.laps.reduce((sum, l) => sum + (l.heartRate || 0), 0) / data.laps.length) : 0; // Haal plan op als het bestaat const plan = await db .select() .from(workouts) .where(eq(workouts.id, data.workoutId)) .get(); // Gebruik sessionId van de watch, of genereer een nieuwe const sessionId = data.sessionId ?? generateUUID(); // Check of er al een sessie bestaat voor dit sessionId const existingSession = await db .select({ id: workoutSessions.id }) .from(workoutSessions) .where(eq(workoutSessions.id, sessionId)) .limit(1); if (existingSession.length === 0) { await db.insert(workoutSessions).values({ id: sessionId, workoutId: data.workoutId, name: plan?.name ?? "Watch Workout", poolLength: plan?.poolLength ?? 25, startedAt: new Date().toISOString(), finishedAt: new Date().toISOString(), totalDistance: data.totalDistance || 0, totalDuration: data.duration, calories: data.calories || 0, avgHeartRate, source: "watch", }); } else { await db .update(workoutSessions) .set({ finishedAt: new Date().toISOString(), totalDuration: data.duration, totalDistance: data.totalDistance || 0, calories: data.calories || 0, avgHeartRate, }) .where(eq(workoutSessions.id, sessionId)); } for (const lap of data.laps) { const strokeCount = lap.strokeCount ?? null; const swolf = strokeCount != null ? strokeCount + Math.round(lap.duration) : null; await db.insert(laps).values({ id: generateUUID(), sessionId, setPosition: lap.setPosition, repNumber: lap.repNumber, distance: lap.distance, duration: lap.duration, heartRate: lap.heartRate || null, strokeCount, swolf, }); } } export async function getAllSessions() { return db .select() .from(workoutSessions) .orderBy(desc(workoutSessions.startedAt)); } export async function getSessionWithDetails(sessionId: string) { const session = await db .select() .from(workoutSessions) .where(eq(workoutSessions.id, sessionId)) .get(); if (!session) return null; const sessionLaps = await db.select().from(laps).where(eq(laps.sessionId, sessionId)); let sessionSets: any[] = []; if (session.workoutId) { sessionSets = await db.select().from(sets).where(eq(sets.workoutId, session.workoutId)); } return { ...session, laps: sessionLaps, sets: sessionSets }; } // ─── Laps ──────────────────────────────────────────────────── export async function saveLap(sessionId: string, lap: LapData) { const swolf = lap.strokeCount != null ? lap.strokeCount + Math.round(lap.duration) : null; await db.insert(laps).values({ id: generateUUID(), sessionId, setPosition: lap.setPosition, repNumber: lap.repNumber, distance: lap.distance, duration: lap.duration, strokeCount: lap.strokeCount ?? null, heartRate: lap.heartRate ?? null, swolf, }); } export async function saveLaps(sessionId: string, lapsData: LapData[]) { for (const lap of lapsData) { await saveLap(sessionId, lap); } } export async function getSessionLaps(sessionId: string) { return db.select().from(laps).where(eq(laps.sessionId, sessionId)); } // ─── Delete ────────────────────────────────────────────────── export async function deleteSession(sessionId: string) { await db.delete(laps).where(eq(laps.sessionId, sessionId)); await db.delete(workoutSessions).where(eq(workoutSessions.id, sessionId)); } export async function deleteSessionAndSync(sessionId: string) { await deleteSession(sessionId); await WatchConnectivity.sendWorkoutDeleted(sessionId); } export async function deleteWorkout(id: string) { // Verwijder alle sessies die aan dit plan gelinkt zijn const sessions = await db .select({ id: workoutSessions.id }) .from(workoutSessions) .where(eq(workoutSessions.workoutId, id)); for (const session of sessions) { await db.delete(laps).where(eq(laps.sessionId, session.id)); } await db.delete(workoutSessions).where(eq(workoutSessions.workoutId, id)); await db.delete(sets).where(eq(sets.workoutId, id)); await db.delete(workouts).where(eq(workouts.id, id)); } export async function deleteWorkoutAndSync(id: string) { await deleteWorkout(id); await WatchConnectivity.sendWorkoutDeleted(id); } // ─── Stats ─────────────────────────────────────────────────── export async function getWeeklyStats() { const now = new Date(); const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); return db .select({ count: sql`count(*)`, totalDistance: sql`coalesce(sum(${workoutSessions.totalDistance}), 0)`, totalDuration: sql`coalesce(sum(${workoutSessions.totalDuration}), 0)`, avgCalories: sql`coalesce(avg(${workoutSessions.calories}), 0)`, }) .from(workoutSessions) .where(sql`${workoutSessions.startedAt} >= ${weekAgo.toISOString()}`) .get(); } export async function getPersonalBests() { return db .select({ distance: laps.distance, bestTime: sql`min(${laps.duration})`, bestSwolf: sql`min(${laps.swolf})`, }) .from(laps) .groupBy(laps.distance) .having(sql`${laps.distance} > 0`); } export async function getTotalStats() { return db .select({ totalWorkouts: sql`count(*)`, totalDistance: sql`coalesce(sum(${workoutSessions.totalDistance}), 0)`, totalDuration: sql`coalesce(sum(${workoutSessions.totalDuration}), 0)`, }) .from(workoutSessions) .where(sql`${workoutSessions.finishedAt} IS NOT NULL`) .get(); }