const { Context } = require('../models'); /** * Context Controller - Workflow-based status management * Status flow: 0 (Draft) -> 1 (Enriched) -> 2 (Prompt Ready) -> 3 (Generating) -> 4 (Image Ready) -> 5 (Approved) */ class ContextController { /** * Create new context - Status 0 (Draft) * Required fields: title, desc, grade */ async createContext(req, res, next) { try { const { title, desc, grade, type } = req.body; // Validate required fields if (!title || !desc || !grade) { return res.status(400).json({ success: false, message: 'Title, desc, and grade are required' }); } // Validate grade format (gradeX100 + unitX10 + lesson) const gradeNum = parseInt(grade); if (isNaN(gradeNum) || gradeNum < 100) { return res.status(400).json({ success: false, message: 'Grade must be in format: gradeX100 + unitX10 + lesson (e.g., 123 for Grade 1 Unit 2 Lesson 3)' }); } const context = await Context.create({ title, desc, grade: gradeNum, type: type || 'general', status: 0, // Draft context: '', knowledge: '' }); res.status(201).json({ success: true, message: 'Context created successfully', data: context }); } catch (error) { next(error); } } /** * Get contexts by status */ async getContextsByStatus(req, res, next) { try { const { status } = req.params; const { page = 1, limit = 50, type, grade } = req.query; const offset = (page - 1) * limit; const where = { status: parseInt(status) }; if (type) where.type = type; if (grade) where.grade = parseInt(grade); const { count, rows } = await Context.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['created_at', 'DESC']] }); res.json({ success: true, data: { contexts: rows, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit) } } }); } catch (error) { next(error); } } /** * Update knowledge - Status 0 -> 1 (Enriched) */ async enrichContext(req, res, next) { try { const { id } = req.params; const { knowledge } = req.body; if (!knowledge) { return res.status(400).json({ success: false, message: 'Knowledge is required' }); } const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } if (context.status !== 0) { return res.status(400).json({ success: false, message: 'Context must be in Draft status (0) to enrich' }); } await context.update({ knowledge, status: 1 }); res.json({ success: true, message: 'Context enriched successfully', data: context }); } catch (error) { next(error); } } /** * Update context and img_prompt - Status 1 -> 2 (Prompt Ready) */ async preparePrompt(req, res, next) { try { const { id } = req.params; const { context, img_prompt } = req.body; if (!context || !img_prompt) { return res.status(400).json({ success: false, message: 'Context and img_prompt are required' }); } const contextRecord = await Context.findByPk(id); if (!contextRecord) { return res.status(404).json({ success: false, message: 'Context not found' }); } if (contextRecord.status !== 1) { return res.status(400).json({ success: false, message: 'Context must be in Enriched status (1) to prepare prompt' }); } await contextRecord.update({ context, img_prompt, status: 2 }); res.json({ success: true, message: 'Prompt prepared successfully', data: contextRecord }); } catch (error) { next(error); } } /** * Update status to 3 (Generating) or back to 1 (Enriched) */ async updateStatusFromPromptReady(req, res, next) { try { const { id } = req.params; const { status } = req.body; if (![1, 3].includes(parseInt(status))) { return res.status(400).json({ success: false, message: 'Status must be 1 (Enriched) or 3 (Generating)' }); } const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } if (context.status !== 2) { return res.status(400).json({ success: false, message: 'Context must be in Prompt Ready status (2) to update' }); } await context.update({ status: parseInt(status) }); res.json({ success: true, message: `Status updated to ${status}`, data: context }); } catch (error) { next(error); } } /** * Add images - Status 3 -> 4 (Image Ready) */ async addImages(req, res, next) { try { const { id } = req.params; const { image } = req.body; if (!image || typeof image !== 'string' || image.trim().length === 0) { return res.status(400).json({ success: false, message: 'Image must be a non-empty string (URL)' }); } const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } if (context.status !== 3) { return res.status(400).json({ success: false, message: 'Context must be in Generating status (3) to add images' }); } await context.update({ image, status: 4 }); res.json({ success: true, message: 'Images added successfully', data: context }); } catch (error) { next(error); } } /** * Approve context - Status 4 -> 5 (Approved) */ async approveContext(req, res, next) { try { const { id } = req.params; const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } if (context.status !== 4) { return res.status(400).json({ success: false, message: 'Context must be in Image Ready status (4) to approve' }); } await context.update({ status: 5 }); res.json({ success: true, message: 'Context approved successfully', data: context }); } catch (error) { next(error); } } /** * Get context by UUID (for viewing details) */ async getContextById(req, res, next) { try { const { id } = req.params; const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } res.json({ success: true, data: context }); } catch (error) { next(error); } } /** * Get all contexts with pagination and filters */ async getAllContexts(req, res, next) { try { const { page = 1, limit = 50, type, grade, status } = req.query; const offset = (page - 1) * limit; const where = {}; if (type) where.type = type; if (grade) where.grade = parseInt(grade); if (status !== undefined) where.status = parseInt(status); const { count, rows } = await Context.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['created_at', 'DESC']] }); res.json({ success: true, data: { contexts: rows, pagination: { total: count, page: parseInt(page), limit: parseInt(limit), totalPages: Math.ceil(count / limit) } } }); } catch (error) { next(error); } } /** * Update context (general update - use with caution) */ async updateContext(req, res, next) { try { const { id } = req.params; const updates = req.body; const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } await context.update(updates); res.json({ success: true, message: 'Context updated successfully', data: context }); } catch (error) { next(error); } } /** * Delete context */ async deleteContext(req, res, next) { try { const { id } = req.params; const context = await Context.findByPk(id); if (!context) { return res.status(404).json({ success: false, message: 'Context not found' }); } await context.destroy(); res.json({ success: true, message: 'Context deleted successfully' }); } catch (error) { next(error); } } } module.exports = new ContextController();