import { v4 as uuidv4 } from 'uuid'; import { User, Medication, UserSettings, TakenDoses, CustomReminder, CouchDBDocument, UserRole, } from '../../types'; import { AccountStatus } from '../auth/auth.constants'; import { DatabaseStrategy, DatabaseError } from './types'; // Simulate network latency for realistic testing const latency = () => new Promise(res => setTimeout(res, Math.random() * 200 + 50)); export class MockDatabaseStrategy implements DatabaseStrategy { private async getDb(dbName: string): Promise { await latency(); const db = localStorage.getItem(dbName); return db ? JSON.parse(db) : []; } private async saveDb(dbName: string, data: T[]): Promise { await latency(); localStorage.setItem(dbName, JSON.stringify(data)); } private async getDoc( dbName: string, id: string ): Promise { const allDocs = await this.getDb(dbName); return allDocs.find(doc => doc._id === id) || null; } private async query( dbName: string, predicate: (doc: T) => boolean ): Promise { const allDocs = await this.getDb(dbName); return allDocs.filter(predicate); } private async putDoc( dbName: string, doc: T ): Promise { const allDocs = await this.getDb(dbName); const existingIndex = allDocs.findIndex(d => d._id === doc._id); if (existingIndex !== -1) { const existing = allDocs[existingIndex]; if (existing._rev !== doc._rev) { throw new DatabaseError(`Document update conflict for ${doc._id}`, 409); } allDocs[existingIndex] = { ...doc, _rev: uuidv4() }; } else { allDocs.push({ ...doc, _rev: uuidv4() }); } await this.saveDb(dbName, allDocs); return allDocs.find(d => d._id === doc._id)!; } private async deleteDoc(dbName: string, id: string): Promise { const allDocs = await this.getDb(dbName); const filtered = allDocs.filter(doc => doc._id !== id); if (filtered.length === allDocs.length) { return false; // Document not found } await this.saveDb(dbName, filtered); return true; } // User operations async createUser(user: Omit): Promise { const newUser: User = { ...user, _id: uuidv4(), _rev: uuidv4(), status: user.status || AccountStatus.ACTIVE, role: user.role || UserRole.USER, createdAt: user.createdAt || new Date(), }; return this.putDoc('users', newUser); } async updateUser(user: User): Promise { return this.putDoc('users', user); } async getUserById(id: string): Promise { return this.getDoc('users', id); } async findUserByEmail(email: string): Promise { const users = await this.query('users', user => user.email === email); return users[0] || null; } async deleteUser(id: string): Promise { return this.deleteDoc('users', id); } async getAllUsers(): Promise { return this.getDb('users'); } // Medication operations async createMedication( userId: string, medication: Omit ): Promise { const newMedication: Medication = { ...medication, _id: `${userId}-med-${uuidv4()}`, _rev: uuidv4(), }; return this.putDoc('medications', newMedication); } async updateMedication(medication: Medication): Promise { return this.putDoc('medications', medication); } async getMedications(userId: string): Promise { return this.query('medications', med => med._id.startsWith(`${userId}-med-`) ); } async deleteMedication(id: string): Promise { return this.deleteDoc('medications', id); } // User settings operations async getUserSettings(userId: string): Promise { const existing = await this.getDoc('settings', userId); if (existing) { return existing; } // Create default settings if none exist const defaultSettings: UserSettings = { _id: userId, _rev: uuidv4(), notificationsEnabled: true, hasCompletedOnboarding: false, }; return this.putDoc('settings', defaultSettings); } async updateUserSettings(settings: UserSettings): Promise { return this.putDoc('settings', settings); } // Taken doses operations async getTakenDoses(userId: string): Promise { const existing = await this.getDoc('taken_doses', userId); if (existing) { return existing; } // Create default taken doses record if none exists const defaultTakenDoses: TakenDoses = { _id: userId, _rev: uuidv4(), doses: {}, }; return this.putDoc('taken_doses', defaultTakenDoses); } async updateTakenDoses(takenDoses: TakenDoses): Promise { return this.putDoc('taken_doses', takenDoses); } // Custom reminders operations async createCustomReminder( userId: string, reminder: Omit ): Promise { const newReminder: CustomReminder = { ...reminder, _id: `${userId}-reminder-${uuidv4()}`, _rev: uuidv4(), }; return this.putDoc('reminders', newReminder); } async updateCustomReminder( reminder: CustomReminder ): Promise { return this.putDoc('reminders', reminder); } async getCustomReminders(userId: string): Promise { return this.query('reminders', reminder => reminder._id.startsWith(`${userId}-reminder-`) ); } async deleteCustomReminder(id: string): Promise { return this.deleteDoc('reminders', id); } // User operations with password async createUserWithPassword( email: string, hashedPassword: string, username?: string ): Promise { // Check if user already exists const existingUser = await this.findUserByEmail(email); if (existingUser) { throw new DatabaseError('User already exists with this email', 409); } return this.createUser({ username: username || email.split('@')[0], email, password: hashedPassword, emailVerified: false, status: AccountStatus.PENDING, role: UserRole.USER, createdAt: new Date(), }); } async createUserFromOAuth( email: string, username: string, _provider: string ): Promise { // Check if user already exists const existingUser = await this.findUserByEmail(email); if (existingUser) { // Update last login and return existing user return this.updateUser({ ...existingUser, lastLoginAt: new Date(), }); } return this.createUser({ username, email, emailVerified: true, // OAuth emails are considered verified status: AccountStatus.ACTIVE, role: UserRole.USER, createdAt: new Date(), lastLoginAt: new Date(), }); } }