/** * Research Inquiry Controller * Academic research collaboration inquiry submission */ const ResearchInquiry = require('../models/ResearchInquiry.model'); const ModerationQueue = require('../models/ModerationQueue.model'); const logger = require('../utils/logger.util'); /** * Submit research inquiry (public) * POST /api/research-inquiry */ async function submitInquiry(req, res) { try { const { name, email, institution, researchQuestion, methodology, context, needs, otherNeeds, timeline } = req.body; // Validate required fields if (!name || !email || !institution) { return res.status(400).json({ error: 'Bad Request', message: 'Missing required contact information' }); } if (!researchQuestion || !methodology) { return res.status(400).json({ error: 'Bad Request', message: 'Missing required research information' }); } logger.info(`Research inquiry submitted: ${institution} - ${name}`); // Create inquiry const researchInquiry = await ResearchInquiry.create({ contact: { name, email, institution }, research: { research_question: researchQuestion, methodology, context: context || '', needs: Array.isArray(needs) ? needs : [], other_needs: otherNeeds || '', timeline: timeline || '' }, status: 'new' }); // Add to moderation queue for human review await ModerationQueue.create({ type: 'RESEARCH_INQUIRY', reference_collection: 'research_inquiries', reference_id: researchInquiry._id, quadrant: 'STRATEGIC', // Research collaborations are strategic data: { contact: { name, email, institution }, research: { question: researchQuestion, methodology, needs } }, priority: 'high', status: 'PENDING_APPROVAL', requires_human_approval: true, human_required_reason: 'Research collaborations require human review and strategic assessment' }); logger.info(`Research inquiry created: ${researchInquiry._id}`); res.status(201).json({ success: true, message: 'Thank you for your research inquiry. We will review and respond within 48-72 hours.', inquiry_id: researchInquiry._id, governance: { human_review: true, note: 'All research inquiries are reviewed by humans before response' } }); } catch (error) { logger.error('Submit research inquiry error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred while submitting your inquiry' }); } } /** * List all research inquiries (admin) * GET /api/research-inquiry?status=new */ async function listInquiries(req, res) { try { const { status, limit = 20, skip = 0 } = req.query; let inquiries, total; if (status) { inquiries = await ResearchInquiry.findByStatus(status, { limit: parseInt(limit), skip: parseInt(skip) }); total = await ResearchInquiry.countByStatus(status); } else { inquiries = await ResearchInquiry.findAll({ limit: parseInt(limit), skip: parseInt(skip) }); total = await ResearchInquiry.countAll(); } res.json({ success: true, status: status || 'all', inquiries, pagination: { total, limit: parseInt(limit), skip: parseInt(skip), hasMore: parseInt(skip) + inquiries.length < total } }); } catch (error) { logger.error('List research inquiries error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred' }); } } /** * Get research inquiry by ID (admin) * GET /api/research-inquiry/:id */ async function getInquiry(req, res) { try { const { id } = req.params; const inquiry = await ResearchInquiry.findById(id); if (!inquiry) { return res.status(404).json({ error: 'Not Found', message: 'Research inquiry not found' }); } res.json({ success: true, inquiry }); } catch (error) { logger.error('Get research inquiry error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred' }); } } /** * Assign inquiry to user (admin) * POST /api/research-inquiry/:id/assign */ async function assignInquiry(req, res) { try { const { id } = req.params; const { user_id } = req.body; const userId = user_id || req.user._id; const success = await ResearchInquiry.assign(id, userId); if (!success) { return res.status(404).json({ error: 'Not Found', message: 'Research inquiry not found' }); } logger.info(`Research inquiry ${id} assigned to ${userId} by ${req.user.email}`); res.json({ success: true, message: 'Inquiry assigned successfully' }); } catch (error) { logger.error('Assign research inquiry error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred' }); } } /** * Respond to inquiry (admin) * POST /api/research-inquiry/:id/respond */ async function respondToInquiry(req, res) { try { const { id } = req.params; const { content } = req.body; if (!content) { return res.status(400).json({ error: 'Bad Request', message: 'Response content is required' }); } const inquiry = await ResearchInquiry.findById(id); if (!inquiry) { return res.status(404).json({ error: 'Not Found', message: 'Research inquiry not found' }); } const success = await ResearchInquiry.respond(id, { content, responder: req.user.email }); if (!success) { return res.status(500).json({ error: 'Internal Server Error', message: 'Failed to update inquiry' }); } logger.info(`Research inquiry ${id} responded to by ${req.user.email}`); res.json({ success: true, message: 'Response recorded successfully', note: 'Remember to send actual email to researcher separately' }); } catch (error) { logger.error('Respond to research inquiry error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred' }); } } /** * Delete research inquiry (admin) * DELETE /api/research-inquiry/:id */ async function deleteInquiry(req, res) { try { const { id } = req.params; const success = await ResearchInquiry.delete(id); if (!success) { return res.status(404).json({ error: 'Not Found', message: 'Research inquiry not found' }); } logger.info(`Research inquiry deleted: ${id} by ${req.user.email}`); res.json({ success: true, message: 'Inquiry deleted successfully' }); } catch (error) { logger.error('Delete research inquiry error:', error); res.status(500).json({ error: 'Internal Server Error', message: 'An error occurred' }); } } module.exports = { submitInquiry, listInquiries, getInquiry, assignInquiry, respondToInquiry, deleteInquiry };