// /components/professors/professorService.js const Professor = require('./professorModel'); const AppError = require('../../utils/AppError'); const eventEmitter = require('../../events/eventEmitter'); const EVENT_NAMES = require('../../constants/eventNames'); const { parsePaginationAndSort, buildFilterQuery, calculateMeta } = require('../../utils/pagination'); const { allocatePlaceholderNationalId } = require('../../utils/nationalId'); const createProfessor = async (data) => { let nationalIdCode = String(data.nationalIdCode || data.nationalId || '').trim(); const phoneNumber = String(data.phoneNumber || data.phone || '').trim(); if (!nationalIdCode) { nationalIdCode = await allocatePlaceholderNationalId(phoneNumber); } const payload = { ...data, name: String(data.name || '').trim(), surname: String(data.surname || '').trim(), nationalIdCode, phoneNumber, email: data.email ? String(data.email).trim().toLowerCase() : undefined, cardNumber: data.cardNumber ? String(data.cardNumber).trim() : undefined, shabaNumber: data.shabaNumber || data.iban ? String(data.shabaNumber || data.iban).trim() : undefined, bio: data.bio ? String(data.bio).trim() : undefined }; const existing = await Professor.findOne({ $or: [ { nationalIdCode: payload.nationalIdCode }, { phoneNumber: payload.phoneNumber }, ...(payload.email ? [{ email: payload.email }] : []) ] }); if (existing) { throw new AppError('PROFESSOR_ALREADY_EXISTS'); } const professor = await Professor.create(payload); eventEmitter.emit(EVENT_NAMES.PROFESSOR_CREATED, { professorId: professor._id, name: `${professor.name} ${professor.surname}` }); return professor; }; const getProfessorById = async (id) => { const professor = await Professor.findById(id).populate('courses', 'title type price'); if (!professor) { throw new AppError('PROFESSOR_NOT_FOUND'); } return professor; }; const getAllProfessors = async (queryParams) => { const { page, limit, skip, sort } = parsePaginationAndSort(queryParams); const filter = buildFilterQuery(queryParams, ['name', 'surname', 'nationalIdCode', 'phoneNumber', 'email', 'expertise']); const [professors, totalCount] = await Promise.all([ Professor.find(filter).populate('courses', 'title').sort(sort).skip(skip).limit(limit), Professor.countDocuments(filter) ]); const meta = calculateMeta(totalCount, page, limit); return { data: professors, meta }; }; const updateProfessor = async (id, updateData) => { const professor = await Professor.findById(id); if (!professor) { throw new AppError('PROFESSOR_NOT_FOUND'); } const payload = { ...updateData }; if (payload.nationalIdCode !== undefined || payload.nationalId !== undefined) { payload.nationalIdCode = String(payload.nationalIdCode || payload.nationalId || '').trim(); delete payload.nationalId; } if (payload.phoneNumber !== undefined || payload.phone !== undefined) { payload.phoneNumber = String(payload.phoneNumber || payload.phone || '').trim(); delete payload.phone; } if (payload.name !== undefined) payload.name = String(payload.name).trim(); if (payload.surname !== undefined) payload.surname = String(payload.surname).trim(); if (payload.email !== undefined) { payload.email = payload.email ? String(payload.email).trim().toLowerCase() : undefined; } if (payload.cardNumber !== undefined) { payload.cardNumber = payload.cardNumber ? String(payload.cardNumber).trim() : undefined; } if (payload.shabaNumber !== undefined || payload.iban !== undefined) { const val = payload.shabaNumber || payload.iban; payload.shabaNumber = val ? String(val).trim() : undefined; delete payload.iban; } if (payload.bio !== undefined) { payload.bio = payload.bio ? String(payload.bio).trim() : undefined; } Object.assign(professor, payload); await professor.save(); return professor; }; const deleteProfessor = async (id) => { const professor = await Professor.findById(id); if (!professor) { throw new AppError('PROFESSOR_NOT_FOUND'); } await Professor.findByIdAndDelete(id); return null; }; const searchProfessors = async (queryParams) => { return getAllProfessors(queryParams); }; module.exports = { createProfessor, getProfessorById, getAllProfessors, updateProfessor, deleteProfessor, searchProfessors };