// lib/db/services/LibraryService.ts import { SQLiteDatabase } from 'expo-sqlite'; import { DbService } from '../db-service'; import { BaseExercise, ExerciseDisplay } from '@/types/exercise'; import { StorageSource } from '@/types/shared'; import { generateId } from '@/utils/ids'; export class LibraryService { private readonly db: DbService; private readonly DEBUG = __DEV__; constructor(database: SQLiteDatabase) { this.db = new DbService(database); } async getExercises(): Promise { try { const result = await this.db.getAllAsync<{ id: string; title: string; type: string; category: string; equipment: string | null; description: string | null; instructions: string | null; tags: string | null; created_at: number; source: string; format_json: string | null; format_units_json: string | null; }>( `SELECT e.*, GROUP_CONCAT(DISTINCT t.tag) as tags FROM exercises e LEFT JOIN exercise_tags t ON e.id = t.exercise_id GROUP BY e.id ORDER BY e.title` ); return result.map(row => ({ id: row.id, title: row.title, type: row.type as ExerciseDisplay['type'], category: row.category as ExerciseDisplay['category'], equipment: row.equipment as ExerciseDisplay['equipment'] || undefined, description: row.description || undefined, instructions: row.instructions ? row.instructions.split(',') : undefined, tags: row.tags ? row.tags.split(',') : [], created_at: row.created_at, source: row.source as ExerciseDisplay['source'], availability: { source: [row.source as StorageSource] }, format: row.format_json ? JSON.parse(row.format_json) : undefined, format_units: row.format_units_json ? JSON.parse(row.format_units_json) : undefined })); } catch (error) { console.error('Error getting exercises:', error); throw error; } } async addExercise(exercise: Omit): Promise { try { const id = generateId(); const timestamp = Date.now(); // Use same timestamp for both created_at and updated_at initially await this.db.withTransactionAsync(async () => { // Insert main exercise data await this.db.runAsync( `INSERT INTO exercises ( id, title, type, category, equipment, description, created_at, updated_at, source, format_json, format_units_json ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ id, exercise.title, exercise.type, exercise.category, exercise.equipment || null, exercise.description || null, timestamp, // created_at timestamp, // updated_at exercise.source, exercise.format ? JSON.stringify(exercise.format) : null, exercise.format_units ? JSON.stringify(exercise.format_units) : null ] ); // Insert tags if (exercise.tags?.length) { for (const tag of exercise.tags) { await this.db.runAsync( `INSERT INTO exercise_tags (exercise_id, tag) VALUES (?, ?)`, [id, tag] ); } } // Insert instructions if (exercise.instructions?.length) { for (const [index, instruction] of exercise.instructions.entries()) { await this.db.runAsync( `INSERT INTO exercise_instructions ( exercise_id, instruction, display_order ) VALUES (?, ?, ?)`, [id, instruction, index] ); } } }); return id; } catch (error) { console.error('Error adding exercise:', error); throw error; } } async deleteExercise(id: string): Promise { try { const result = await this.db.runAsync( 'DELETE FROM exercises WHERE id = ?', [id] ); if (!result.changes) { throw new Error('Exercise not found'); } } catch (error) { console.error('Error deleting exercise:', error); throw error; } } }