Source: controllers/analyze.controller.js

/**
 * Analyze controller
 * 
 * Implements document and RFP analysis endpoints
 */

import { analyzeService } from '../services/analyze.service.js';
import { APIError } from '../middleware/errorHandler.js';
import { logger } from '../utils/logger.js';

export const analyzeController = {
  /**
   * Analyze a document
   */
  analyzeDocument: async (req, res, next) => {
    try {
      const { document, options } = req.body;
      
      if (!document || !document.content) {
        return next(new APIError('Missing required document content', 400, 'MISSING_DOCUMENT_CONTENT'));
      }
      
      const startTime = Date.now();
      const analysis = await analyzeService.analyzeDocument(document, options);
      const duration = Date.now() - startTime;
      
      logger.info(`Document analysis completed in ${duration}ms`);
      
      res.json({
        analysisId: analysis.id,
        results: analysis.results,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`Document analysis failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Analyze multiple documents in batch
   */
  analyzeDocumentBatch: async (req, res, next) => {
    try {
      const { documents, options } = req.body;
      
      if (!documents || !Array.isArray(documents) || documents.length === 0) {
        return next(new APIError('Missing or invalid documents', 400, 'INVALID_DOCUMENTS'));
      }
      
      const startTime = Date.now();
      const batchResults = await analyzeService.analyzeDocumentBatch(documents, options);
      const duration = Date.now() - startTime;
      
      logger.info(`Batch document analysis completed in ${duration}ms, processed ${documents.length} documents`);
      
      res.json({
        batchId: batchResults.batchId,
        results: batchResults.results,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString(),
          documentsProcessed: documents.length
        }
      });
    } catch (error) {
      next(new APIError(`Batch document analysis failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Get document analysis results
   */
  getDocumentAnalysisResults: async (req, res, next) => {
    try {
      const { analysisId } = req.params;
      const analysis = await analyzeService.getDocumentAnalysisResults(analysisId);
      
      if (!analysis) {
        return next(new APIError(`Analysis not found: ${analysisId}`, 404, 'ANALYSIS_NOT_FOUND'));
      }
      
      res.json(analysis);
    } catch (error) {
      next(new APIError(`Failed to retrieve analysis results: ${error.message}`, 500));
    }
  },
  
  /**
   * Analyze an RFP document
   */
  analyzeRFP: async (req, res, next) => {
    try {
      const { rfpDocument, options } = req.body;
      
      if (!rfpDocument || !rfpDocument.content) {
        return next(new APIError('Missing required RFP document content', 400, 'MISSING_RFP_CONTENT'));
      }
      
      const startTime = Date.now();
      const analysis = await analyzeService.analyzeRFP(rfpDocument, options);
      const duration = Date.now() - startTime;
      
      logger.info(`RFP analysis completed in ${duration}ms`);
      
      res.json({
        analysisId: analysis.id,
        results: analysis.results,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`RFP analysis failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Get RFP analysis results
   */
  getRFPAnalysisResults: async (req, res, next) => {
    try {
      const { analysisId } = req.params;
      const analysis = await analyzeService.getRFPAnalysisResults(analysisId);
      
      if (!analysis) {
        return next(new APIError(`RFP analysis not found: ${analysisId}`, 404, 'ANALYSIS_NOT_FOUND'));
      }
      
      res.json(analysis);
    } catch (error) {
      next(new APIError(`Failed to retrieve RFP analysis results: ${error.message}`, 500));
    }
  },
  
  /**
   * Generate RFP response
   */
  generateRFPResponse: async (req, res, next) => {
    try {
      const { analysisId } = req.params;
      const { sections, template, options } = req.body;
      
      if (!sections || !Array.isArray(sections) || sections.length === 0) {
        return next(new APIError('Missing or invalid response sections', 400, 'INVALID_SECTIONS'));
      }
      
      const startTime = Date.now();
      const response = await analyzeService.generateRFPResponse(analysisId, sections, template, options);
      const duration = Date.now() - startTime;
      
      logger.info(`RFP response generation completed in ${duration}ms`);
      
      res.json({
        responseId: response.id,
        content: response.content,
        sections: response.sections,
        metadata: {
          processingTime: duration,
          analysisId,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`RFP response generation failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Extract entities from text
   */
  extractEntities: async (req, res, next) => {
    try {
      const { text, entityTypes, options } = req.body;
      
      if (!text) {
        return next(new APIError('Missing required text for entity extraction', 400, 'MISSING_TEXT'));
      }
      
      const startTime = Date.now();
      const entities = await analyzeService.extractEntities(text, entityTypes, options);
      const duration = Date.now() - startTime;
      
      res.json({
        entities,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`Entity extraction failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Extract topics from text
   */
  extractTopics: async (req, res, next) => {
    try {
      const { text, options } = req.body;
      
      if (!text) {
        return next(new APIError('Missing required text for topic extraction', 400, 'MISSING_TEXT'));
      }
      
      const startTime = Date.now();
      const topics = await analyzeService.extractTopics(text, options);
      const duration = Date.now() - startTime;
      
      res.json({
        topics,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`Topic extraction failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Extract sections from document
   */
  extractSections: async (req, res, next) => {
    try {
      const { document, options } = req.body;
      
      if (!document || !document.content) {
        return next(new APIError('Missing required document content', 400, 'MISSING_DOCUMENT_CONTENT'));
      }
      
      const startTime = Date.now();
      const sections = await analyzeService.extractSections(document, options);
      const duration = Date.now() - startTime;
      
      res.json({
        sections,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`Section extraction failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Summarize content
   */
  summarizeContent: async (req, res, next) => {
    try {
      const { text, maxLength, options } = req.body;
      
      if (!text) {
        return next(new APIError('Missing required text for summarization', 400, 'MISSING_TEXT'));
      }
      
      const startTime = Date.now();
      const summary = await analyzeService.summarizeContent(text, maxLength, options);
      const duration = Date.now() - startTime;
      
      res.json({
        summary,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString(),
          originalLength: text.length,
          summaryLength: summary.length
        }
      });
    } catch (error) {
      next(new APIError(`Summarization failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Classify content
   */
  classifyContent: async (req, res, next) => {
    try {
      const { text, categories, options } = req.body;
      
      if (!text) {
        return next(new APIError('Missing required text for classification', 400, 'MISSING_TEXT'));
      }
      
      const startTime = Date.now();
      const classification = await analyzeService.classifyContent(text, categories, options);
      const duration = Date.now() - startTime;
      
      res.json({
        classification,
        metadata: {
          processingTime: duration,
          timestamp: new Date().toISOString()
        }
      });
    } catch (error) {
      next(new APIError(`Classification failed: ${error.message}`, 500));
    }
  },
  
  /**
   * Get analysis history
   */
  getAnalysisHistory: async (req, res, next) => {
    try {
      const history = await analyzeService.getAnalysisHistory(req.query);
      res.json(history);
    } catch (error) {
      next(new APIError(`Failed to retrieve analysis history: ${error.message}`, 500));
    }
  },
  
  /**
   * Get analysis history details
   */
  getAnalysisHistoryDetails: async (req, res, next) => {
    try {
      const { analysisId } = req.params;
      const historyDetails = await analyzeService.getAnalysisHistoryDetails(analysisId);
      
      if (!historyDetails) {
        return next(new APIError(`Analysis history not found: ${analysisId}`, 404, 'HISTORY_NOT_FOUND'));
      }
      
      res.json(historyDetails);
    } catch (error) {
      next(new APIError(`Failed to retrieve analysis history details: ${error.message}`, 500));
    }
  },
  
  /**
   * Delete analysis history
   */
  deleteAnalysisHistory: async (req, res, next) => {
    try {
      const { analysisId } = req.params;
      
      const success = await analyzeService.deleteAnalysisHistory(analysisId);
      
      if (!success) {
        return next(new APIError(`Analysis history not found: ${analysisId}`, 404, 'HISTORY_NOT_FOUND'));
      }
      
      res.status(204).end();
    } catch (error) {
      next(new APIError(`Failed to delete analysis history: ${error.message}`, 500));
    }
  }
};

export default analyzeController;