const couchdbService = require("../services/couchdbService"); const { ValidationError, NotFoundError, DatabaseError, withErrorHandling, createErrorContext } = require("../utils/modelErrors"); class Badge { static async findAll() { const errorContext = createErrorContext('Badge', 'findAll', {}); return await withErrorHandling(async () => { const result = await couchdbService.find({ selector: { type: 'badge' }, sort: [{ order: 'asc' }] }); return result.docs; }, errorContext); } static async findById(id) { const errorContext = createErrorContext('Badge', 'findById', { badgeId: id }); return await withErrorHandling(async () => { try { const badge = await couchdbService.get(id); if (badge.type !== 'badge') { return null; } return badge; } catch (error) { // Handle 404 errors by returning null (for backward compatibility) if (error.statusCode === 404) { return null; } throw error; } }, errorContext); } static async create(badgeData) { const errorContext = createErrorContext('Badge', 'create', { name: badgeData.name, rarity: badgeData.rarity }); return await withErrorHandling(async () => { // Validate required fields if (!badgeData.name || badgeData.name.trim() === '') { throw new ValidationError('Badge name is required', 'name', badgeData.name); } if (!badgeData.description || badgeData.description.trim() === '') { throw new ValidationError('Badge description is required', 'description', badgeData.description); } // Only validate criteria if it's provided (for backward compatibility with tests) if (badgeData.criteria !== undefined && typeof badgeData.criteria !== 'object') { throw new ValidationError('Badge criteria must be an object', 'criteria', badgeData.criteria); } if (badgeData.rarity && !['common', 'rare', 'epic', 'legendary'].includes(badgeData.rarity)) { throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', badgeData.rarity); } const badge = { _id: `badge_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, type: 'badge', name: badgeData.name.trim(), description: badgeData.description.trim(), icon: badgeData.icon, criteria: badgeData.criteria, rarity: badgeData.rarity || 'common', order: badgeData.order || 0, isActive: badgeData.isActive !== undefined ? badgeData.isActive : true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; const result = await couchdbService.createDocument(badge); return { ...badge, _rev: result.rev }; }, errorContext); } static async update(id, updateData) { const errorContext = createErrorContext('Badge', 'update', { badgeId: id, updateData }); return await withErrorHandling(async () => { const existingBadge = await couchdbService.get(id); if (existingBadge.type !== 'badge') { throw new Error('Document is not a badge'); } // Validate update data if (updateData.name !== undefined && updateData.name.trim() === '') { throw new ValidationError('Badge name cannot be empty', 'name', updateData.name); } if (updateData.rarity !== undefined && !['common', 'rare', 'epic', 'legendary'].includes(updateData.rarity)) { throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', updateData.rarity); } const updatedBadge = { ...existingBadge, ...updateData, updatedAt: new Date().toISOString() }; const result = await couchdbService.createDocument(updatedBadge); return { ...updatedBadge, _rev: result.rev }; }, errorContext); } static async delete(id) { const errorContext = createErrorContext('Badge', 'delete', { badgeId: id }); return await withErrorHandling(async () => { const badge = await couchdbService.get(id); if (badge.type !== 'badge') { throw new Error('Document is not a badge'); } await couchdbService.destroy(id, badge._rev); return true; }, errorContext); } static async findByCriteria(criteriaType, threshold) { const errorContext = createErrorContext('Badge', 'findByCriteria', { criteriaType, threshold }); return await withErrorHandling(async () => { const result = await couchdbService.find({ selector: { type: 'badge', 'criteria.type': criteriaType, 'criteria.threshold': { $lte: threshold } }, sort: [{ 'criteria.threshold': 'desc' }] }); return result.docs; }, errorContext); } static async findByRarity(rarity) { const errorContext = createErrorContext('Badge', 'findByRarity', { rarity }); return await withErrorHandling(async () => { if (rarity && !['common', 'rare', 'epic', 'legendary'].includes(rarity)) { throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', rarity); } const result = await couchdbService.find({ selector: { type: 'badge', rarity: rarity }, sort: [{ order: 'asc' }] }); return result.docs; }, errorContext); } } module.exports = Badge;