/**
 * AI Exercise Generator
 *
 * Main service for generating mathematical exercises using AI.
 * Handles the complete flow from request to database persistence.
 */

import { prisma } from '../../../shared/database/prisma.client';
import { logger } from '../../../shared/utils/logger';
import { aiConfig, AIGeneratedExercise, AIExerciseRequest } from '../../../config/ai';
import { ExerciseType, ExerciseDifficulty, TopicType, ModuleType } from '@prisma/client';
import { ApplicationError } from '../../../shared/types';

/**
 * Progress Event Interface for SSE
 */
export interface ProgressEvent {
  step: 'validating' | 'building_prompt' | 'analyzing' | 'generating' | 'validating_output' | 'saving' | 'saving_progress' | 'complete' | 'error';
  message: string;
  progress: number;
  data?: Record<string, unknown>;
}

/**
 * Progress Callback Type
 */
export type ProgressCallback = (event: ProgressEvent) => void;

/**
 * Generation Result Interface
 */
export interface GenerationResult {
  success: boolean;
  exercisesGenerated: number;
  exercisesSaved: number;
  exerciseIds: string[];
  errors: string[];
  metadata: {
    topic: TopicType;
    difficulty: ExerciseDifficulty;
    modelUsed: string;
    tokensUsed?: number;
    generationTimeMs: number;
  };
}

/**
 * Exercise Generation Options
 */
export interface GenerationOptions {
  moduleId?: string | undefined;
  topicId?: string | undefined;
  saveToDatabase?: boolean | undefined;
  isPublished?: boolean | undefined;
  customPoints?: number | undefined;
  timeLimitSeconds?: number | undefined;
  validateOnly?: boolean | undefined;
}

/**
 * Batch Generation Request
 */
export interface BatchGenerationRequest {
  requests: Array<{
    topic: TopicType;
    moduleType: ModuleType;
    exerciseType: ExerciseType;
    difficulty: ExerciseDifficulty;
    count: number;
  }>;
  options?: GenerationOptions;
}

/**
 * AI Exercise Generator Service
 */
export class AIExerciseGenerator {
  private _notationPreserver: any;

  constructor() {
    // Lazy load dependencies
  }

  /**
   * Get notation preserver (lazy loading)
   */
  private async getNotationPreserver() {
    if (!this._notationPreserver) {
      const module = await import('./notation-preserver');
      this._notationPreserver = new module.NotationPreserver();
    }
    return this._notationPreserver;
  }

  /**
   * Generate a single exercise with progress callbacks for SSE
   */
  async generateExerciseWithProgress(
    request: AIExerciseRequest,
    options: GenerationOptions = {},
    onProgress?: ProgressCallback
  ): Promise<GenerationResult> {
    const startTime = Date.now();
    const errors: string[] = [];
    const exerciseIds: string[] = [];

    try {
      logger.info({ request, options }, 'Starting single exercise generation with progress');

      // Step 1: Building prompt (10%)
      if (onProgress) {
        onProgress({
          step: 'building_prompt',
          message: 'Preparando prompt para IA...',
          progress: 10,
        });
      }
      await this.delay(50);

      // Step 2: Analyzing topic (20%)
      if (onProgress) {
        onProgress({
          step: 'analyzing',
          message: `Analizando tema: ${request.topic}...`,
          progress: 20,
        });
      }
      await this.delay(50);

      // Set default count to 1 for single generation
      const enrichedRequest: AIExerciseRequest = {
        ...request,
        count: 1,
      };

      // Step 3: Generating with AI (30-70%)
      if (onProgress) {
        onProgress({
          step: 'generating',
          message: 'Generando ejercicio con IA...',
          progress: 30,
        });
      }

      // Generate exercises using AI with internal progress
      const generatedExercises = await aiConfig.generateExercisesWithProgress(
        enrichedRequest,
        (aiProgress) => {
          if (onProgress) {
            // Map AI progress (0-100) to our range (30-70)
            const mappedProgress = 30 + Math.floor(aiProgress * 0.4);
            onProgress({
              step: 'generating',
              message: `Generando ejercicio... ${aiProgress}%`,
              progress: mappedProgress,
            });
          }
        }
      );

      if (generatedExercises.length === 0) {
        throw new ApplicationError('No exercises generated', 'GENERATION_FAILED', 500, true);
      }

      const generatedExercise = generatedExercises[0];
      
      if (!generatedExercise) {
        throw new ApplicationError('Generated exercise is undefined', 'GENERATION_FAILED', 500, true);
      }

      // Step 4: Validating output (75%)
      if (onProgress) {
        onProgress({
          step: 'validating_output',
          message: 'Validando estructura del ejercicio...',
          progress: 75,
        });
      }
      await this.delay(50);

      // Step 5: Saving to database (80-95%)
      if (options.saveToDatabase !== false) {
        if (onProgress) {
          onProgress({
            step: 'saving',
            message: 'Guardando ejercicio en base de datos...',
            progress: 85,
          });
        }
        const exerciseId = await this.saveExerciseToDatabase(generatedExercise, request, options);
        exerciseIds.push(exerciseId);

        if (onProgress) {
          onProgress({
            step: 'saving',
            message: 'Ejercicio guardado correctamente',
            progress: 95,
          });
        }
      }

      const generationTime = Date.now() - startTime;

      // Step 6: Complete (100%)
      if (onProgress) {
        onProgress({
          step: 'complete',
          message: '¡Generación completada!',
          progress: 100,
        });
      }

      logger.info({
        exerciseIds,
        generationTimeMs: generationTime,
      }, 'Exercise generated successfully');

      return {
        success: true,
        exercisesGenerated: 1,
        exercisesSaved: exerciseIds.length,
        exerciseIds,
        errors,
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    } catch (error) {
      const generationTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';

      logger.error({ error, request }, 'Failed to generate exercise');

      if (onProgress) {
        onProgress({
          step: 'error',
          message: `Error: ${errorMessage}`,
          progress: 0,
          data: { error: errorMessage },
        });
      }

      return {
        success: false,
        exercisesGenerated: 0,
        exercisesSaved: 0,
        exerciseIds: [],
        errors: [errorMessage],
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    }
  }

  /**
   * Generate multiple exercises with progress callbacks for SSE
   */
  async generateExercisesWithProgress(
    request: AIExerciseRequest & { count: number },
    options: GenerationOptions = {},
    onProgress?: ProgressCallback
  ): Promise<GenerationResult> {
    const startTime = Date.now();
    const errors: string[] = [];
    const exerciseIds: string[] = [];

    try {
      logger.info({ request, options }, 'Starting multiple exercises generation with progress');

      const totalExercises = request.count;

      // Step 1: Building prompt (10%)
      if (onProgress) {
        onProgress({
          step: 'building_prompt',
          message: `Preparando prompts para ${totalExercises} ejercicios...`,
          progress: 10,
        });
      }
      await this.delay(50);

      // Step 2: Analyzing topic (15%)
      if (onProgress) {
        onProgress({
          step: 'analyzing',
          message: `Analizando tema: ${request.topic}...`,
          progress: 15,
        });
      }
      await this.delay(50);

      // Step 3: Generating with AI (20-60%)
      if (onProgress) {
        onProgress({
          step: 'generating',
          message: `Generando ${totalExercises} ejercicios con IA...`,
          progress: 20,
        });
      }

      // Generate exercises using AI with internal progress
      const generatedExercises = await aiConfig.generateExercisesWithProgress(
        request,
        (aiProgress) => {
          if (onProgress) {
            // Map AI progress (0-100) to our range (20-60)
            const mappedProgress = 20 + Math.floor(aiProgress * 0.4);
            onProgress({
              step: 'generating',
              message: `Generando ejercicios... ${aiProgress}%`,
              progress: mappedProgress,
            });
          }
        }
      );

      if (generatedExercises.length === 0) {
        throw new ApplicationError('No exercises generated', 'GENERATION_FAILED', 500, true);
      }

      // Step 4: Validating output (65%)
      if (onProgress) {
        onProgress({
          step: 'validating_output',
          message: `Validando ${generatedExercises.length} ejercicios...`,
          progress: 65,
        });
      }
      await this.delay(50);

      // Step 5: Saving exercises to database (70-95%)
      if (options.saveToDatabase !== false) {
        const saveProgressBase = 70;
        const saveProgressRange = 25; // 70 to 95
        const exercisesToSave = generatedExercises.length;

        for (let i = 0; i < exercisesToSave; i++) {
          const exerciseToSave = generatedExercises[i];
          if (!exerciseToSave) {
            errors.push(`Exercise at index ${i} is undefined`);
            continue;
          }
          
          try {
            if (onProgress) {
              const saveProgress = saveProgressBase + Math.floor((i / exercisesToSave) * saveProgressRange);
              onProgress({
                step: 'saving_progress',
                message: `Guardando ejercicio ${i + 1} de ${exercisesToSave}...`,
                progress: saveProgress,
              });
            }

            const exerciseId = await this.saveExerciseToDatabase(exerciseToSave, request, options);
            exerciseIds.push(exerciseId);
          } catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Unknown error';
            errors.push(`Exercise save failed: ${errorMessage}`);
            logger.error({ error, exercise: exerciseToSave }, 'Failed to save exercise to database');
          }
        }
      }

      const generationTime = Date.now() - startTime;

      // Step 6: Complete (100%)
      if (onProgress) {
        onProgress({
          step: 'complete',
          message: `¡Generación completada! ${exerciseIds.length} ejercicios guardados`,
          progress: 100,
        });
      }

      logger.info({
        totalGenerated: generatedExercises.length,
        totalSaved: exerciseIds.length,
        errors: errors.length,
        generationTimeMs: generationTime,
      }, 'Batch exercise generation completed');

      return {
        success: exerciseIds.length > 0 || errors.length === 0,
        exercisesGenerated: generatedExercises.length,
        exercisesSaved: exerciseIds.length,
        exerciseIds,
        errors,
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    } catch (error) {
      const generationTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';

      logger.error({ error, request }, 'Failed to generate exercises');

      if (onProgress) {
        onProgress({
          step: 'error',
          message: `Error: ${errorMessage}`,
          progress: 0,
          data: { error: errorMessage },
        });
      }

      return {
        success: false,
        exercisesGenerated: 0,
        exercisesSaved: 0,
        exerciseIds: [],
        errors: [errorMessage],
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    }
  }

  /**
   * Generate a single exercise
   */
  async generateExercise(
    request: AIExerciseRequest,
    options: GenerationOptions = {}
  ): Promise<GenerationResult> {
    const startTime = Date.now();
    const errors: string[] = [];
    const exerciseIds: string[] = [];

    try {
      logger.info({ request, options }, 'Starting single exercise generation');

      // Set default count to 1 for single generation
      const enrichedRequest: AIExerciseRequest = {
        ...request,
        count: 1,
      };

      // Generate exercises using AI
      const generatedExercises = await aiConfig.generateExercises(enrichedRequest);

      if (generatedExercises.length === 0) {
        throw new ApplicationError('No exercises generated', 'GENERATION_FAILED', 500, true);
      }

      const generatedExercise = generatedExercises[0];
      
      if (!generatedExercise) {
        throw new ApplicationError('Generated exercise is undefined', 'GENERATION_FAILED', 500, true);
      }

      // Save to database if requested
      if (options.saveToDatabase !== false) {
        const exerciseId = await this.saveExerciseToDatabase(generatedExercise, request, options);
        exerciseIds.push(exerciseId);
      }

      const generationTime = Date.now() - startTime;

      logger.info({
        exerciseIds,
        generationTimeMs: generationTime,
      }, 'Exercise generated successfully');

      return {
        success: true,
        exercisesGenerated: 1,
        exercisesSaved: exerciseIds.length,
        exerciseIds,
        errors,
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    } catch (error) {
      const generationTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';

      logger.error({ error, request }, 'Failed to generate exercise');

      return {
        success: false,
        exercisesGenerated: 0,
        exercisesSaved: 0,
        exerciseIds: [],
        errors: [errorMessage],
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    }
  }

  /**
   * Generate multiple exercises
   */
  async generateExercises(
    request: AIExerciseRequest & { count: number },
    options: GenerationOptions = {}
  ): Promise<GenerationResult> {
    const startTime = Date.now();
    const errors: string[] = [];
    const exerciseIds: string[] = [];

    try {
      logger.info({ request, options }, 'Starting multiple exercises generation');

      // Generate exercises using AI
      const generatedExercises = await aiConfig.generateExercises(request);

      if (generatedExercises.length === 0) {
        throw new ApplicationError('No exercises generated', 'GENERATION_FAILED', 500, true);
      }

      // Save exercises to database if requested
      if (options.saveToDatabase !== false) {
        for (const exercise of generatedExercises) {
          try {
            const exerciseId = await this.saveExerciseToDatabase(exercise, request, options);
            exerciseIds.push(exerciseId);
          } catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Unknown error';
            errors.push(`Exercise save failed: ${errorMessage}`);
            logger.error({ error, exercise }, 'Failed to save exercise to database');
          }
        }
      }

      const generationTime = Date.now() - startTime;

      logger.info({
        totalGenerated: generatedExercises.length,
        totalSaved: exerciseIds.length,
        errors: errors.length,
        generationTimeMs: generationTime,
      }, 'Batch exercise generation completed');

      return {
        success: exerciseIds.length > 0 || errors.length === 0,
        exercisesGenerated: generatedExercises.length,
        exercisesSaved: exerciseIds.length,
        exerciseIds,
        errors,
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    } catch (error) {
      const generationTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';

      logger.error({ error, request }, 'Failed to generate exercises');

      return {
        success: false,
        exercisesGenerated: 0,
        exercisesSaved: 0,
        exerciseIds: [],
        errors: [errorMessage],
        metadata: {
          topic: request.topic,
          difficulty: request.difficulty,
          modelUsed: aiConfig.getModelInfo().model,
          generationTimeMs: generationTime,
        },
      };
    }
  }

  /**
   * Generate exercises for a complete batch (multiple topics/difficulties)
   */
  async generateBatch(
    batchRequest: BatchGenerationRequest
  ): Promise<Record<string, GenerationResult>> {
    const results: Record<string, GenerationResult> = {};
    const options = batchRequest.options || {};

    logger.info({
      requestsCount: batchRequest.requests.length,
      options,
    }, 'Starting batch exercise generation');

    for (let i = 0; i < batchRequest.requests.length; i++) {
      const request = batchRequest.requests[i];
      if (!request) {
        continue;
      }
      const key = `${request.topic}_${request.difficulty}_${i}`;

      logger.info({
        index: i + 1,
        total: batchRequest.requests.length,
        topic: request.topic,
        difficulty: request.difficulty,
      }, 'Processing batch request');

      try {
        const result = await this.generateExercises(request, options);
        results[key] = result;

        // Small delay between requests to avoid rate limiting
        if (i < batchRequest.requests.length - 1) {
          await this.delay(1000);
        }
      } catch (error) {
        logger.error({ error, request }, 'Failed to process batch request');
        results[key] = {
          success: false,
          exercisesGenerated: 0,
          exercisesSaved: 0,
          exerciseIds: [],
          errors: [error instanceof Error ? error.message : 'Unknown error'],
          metadata: {
            topic: request.topic,
            difficulty: request.difficulty,
            modelUsed: aiConfig.getModelInfo().model,
            generationTimeMs: 0,
          },
        };
      }
    }

    const summary = {
      totalRequests: batchRequest.requests.length,
      successful: Object.values(results).filter(r => r.success).length,
      totalExercises: Object.values(results).reduce((sum, r) => sum + r.exercisesGenerated, 0),
      totalSaved: Object.values(results).reduce((sum, r) => sum + r.exercisesSaved, 0),
    };

    logger.info({ summary }, 'Batch generation completed');

    return results;
  }

  /**
   * Generate exercises for a specific module
   */
  async generateForModule(
    moduleId: string,
    configuration: {
      topics: Array<{
        topicType: TopicType;
        exerciseTypes: ExerciseType[];
        difficulties: ExerciseDifficulty[];
        countPerCombination: number;
      }>;
    }
  ): Promise<GenerationResult> {
    logger.info({ moduleId, configuration }, 'Generating exercises for module');

    // Get module info
    const module = await prisma.modules.findUnique({
      where: { id: moduleId },
    });

    if (!module) {
      throw new ApplicationError(`Module ${moduleId} not found`, 'NOT_FOUND', 404, true);
    }

    const batchRequest: BatchGenerationRequest = {
      requests: [],
      options: {
        moduleId,
        saveToDatabase: true,
      },
    };

    // Build requests from configuration
    for (const topicConfig of configuration.topics) {
      for (const exerciseType of topicConfig.exerciseTypes) {
        for (const difficulty of topicConfig.difficulties) {
          batchRequest.requests.push({
            topic: topicConfig.topicType,
            moduleType: module.type,
            exerciseType,
            difficulty,
            count: topicConfig.countPerCombination,
          });
        }
      }
    }

    const results = await this.generateBatch(batchRequest);

    // Aggregate results
    const allResults = Object.values(results);
    const aggregated: GenerationResult = {
      success: allResults.every(r => r.success),
      exercisesGenerated: allResults.reduce((sum, r) => sum + r.exercisesGenerated, 0),
      exercisesSaved: allResults.reduce((sum, r) => sum + r.exercisesSaved, 0),
      exerciseIds: allResults.flatMap(r => r.exerciseIds),
      errors: allResults.flatMap(r => r.errors),
      metadata: {
        topic: TopicType.VECTORES, // Placeholder
        difficulty: ExerciseDifficulty.BASIC, // Placeholder
        modelUsed: aiConfig.getModelInfo().model,
        generationTimeMs: 0,
      },
    };

    return aggregated;
  }

  /**
   * Save generated exercise to database
   */
  private async saveExerciseToDatabase(
    exercise: AIGeneratedExercise,
    request: AIExerciseRequest,
    options: GenerationOptions
  ): Promise<string> {
    // Get next order number for the module
    const order = options.moduleId
      ? await this.getNextExerciseOrder(options.moduleId)
      : 1;

    // Determine difficulty from response or request
    const difficulty = this.mapDifficulty(
      exercise.difficulty || request.difficulty
    );

    // Prepare exercise data
    const exerciseData: any = {
      moduleId: options.moduleId || (await this.getOrCreateModule(request.moduleType)),
      topicId: options.topicId || null,
      type: request.exerciseType,
      difficulty,
      order,
      statement: exercise.statement,
      correctAnswer: exercise.correctAnswer,
      solutionSteps: exercise.solutionSteps,
      formulas: exercise.formulas,
      hints: exercise.hints,
      isAIGenerated: true,
      isPublished: options.isPublished ?? false,
      points: options.customPoints || this.calculatePoints(exercise, difficulty),
      timeLimitSeconds: options.timeLimitSeconds || exercise.estimatedTimeSeconds,
    };

    // Add type-specific data
    if (request.exerciseType === ExerciseType.MULTIPLE_CHOICE && exercise.multipleChoiceOptions) {
      exerciseData.multipleChoiceOptions = exercise.multipleChoiceOptions;
    }

    if (request.exerciseType === ExerciseType.PROOF) {
      // For proof exercises, generate proof requirements
      exerciseData.proofRequirements = {
        givens: [],
        toProve: exercise.correctAnswer,
        theorems: [],
      };
    }

    if (request.exerciseType === ExerciseType.CALCULATION) {
      exerciseData.calculationSteps = {
        steps: exercise.solutionSteps?.map(s => ({
          step: s.step,
          operation: s.explanation,
          result: s.latexFormula || '',
        })) || [],
        finalResult: exercise.correctAnswer,
      };
    }

    // Save to database
    const created = await prisma.exercise.create({
      data: exerciseData,
    });

    logger.debug({
      exerciseId: created.id,
      type: request.exerciseType,
      difficulty,
    }, 'Exercise saved to database');

    return created.id;
  }

  /**
   * Get or create module for exercise
   */
  private async getOrCreateModule(moduleType: ModuleType): Promise<string> {
    let module = await prisma.modules.findFirst({
      where: { type: moduleType },
    });

    if (!module) {
      module = await prisma.modules.create({
        data: {
          id: crypto.randomUUID(),
          name: `${moduleType} - Auto Generated`,
          description: `Module for ${moduleType} exercises`,
          type: moduleType,
          order: 1,
          isPublished: false,
        },
      });
    }

    return module.id;
  }

  /**
   * Get next order number for exercises in a module
   */
  private async getNextExerciseOrder(moduleId: string): Promise<number> {
    const lastExercise = await prisma.exercise.findFirst({
      where: { moduleId },
      orderBy: { order: 'desc' },
      select: { order: true },
    });

    return (lastExercise?.order || 0) + 1;
  }

  /**
   * Map string difficulty to enum
   */
  private mapDifficulty(difficulty: string): ExerciseDifficulty {
    const normalized = difficulty.toUpperCase();
    if (Object.values(ExerciseDifficulty).includes(normalized as ExerciseDifficulty)) {
      return normalized as ExerciseDifficulty;
    }
    return ExerciseDifficulty.INTERMEDIATE;
  }

  /**
   * Calculate points based on difficulty and complexity
   */
  private calculatePoints(
    exercise: AIGeneratedExercise,
    difficulty: ExerciseDifficulty
  ): number {
    const basePoints = {
      [ExerciseDifficulty.BASIC]: 10,
      [ExerciseDifficulty.INTERMEDIATE]: 15,
      [ExerciseDifficulty.ADVANCED]: 25,
      [ExerciseDifficulty.EXPERT]: 40,
    };

    let points = basePoints[difficulty] || 15;

    // Adjust based on solution complexity
    if (exercise.solutionSteps && exercise.solutionSteps.length > 5) {
      points += Math.floor(exercise.solutionSteps.length / 3) * 5;
    }

    // Adjust based on hints
    if (exercise.hints && exercise.hints.length > 2) {
      points += (exercise.hints.length - 2) * 2;
    }

    return Math.min(points, 100); // Cap at 100 points
  }

  /**
   * Delay helper for rate limiting
   */
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Validate generated exercise quality
   */
  async validateExercise(exercise: AIGeneratedExercise): Promise<{
    isValid: boolean;
    issues: string[];
  }> {
    const issues: string[] = [];

    // Check statement
    if (!exercise.statement || exercise.statement.length < 20) {
      issues.push('Statement is too short or missing');
    }

    // Check answer
    if (!exercise.correctAnswer || exercise.correctAnswer.length < 3) {
      issues.push('Correct answer is too short or missing');
    }

    // Check solution steps
    if (!exercise.solutionSteps || exercise.solutionSteps.length === 0) {
      issues.push('Solution steps are missing');
    }

    // Check LaTeX syntax in formulas
    const notationPreserver = await this.getNotationPreserver();
    const notationCheck = notationPreserver.validateLatexSyntax(exercise);
    if (!notationCheck.isValid) {
      issues.push(...notationCheck.issues);
    }

    return {
      isValid: issues.length === 0,
      issues,
    };
  }

  /**
   * Regenerate exercise with feedback
   */
  async regenerateExercise(
    exerciseId: string,
    feedback: string
  ): Promise<GenerationResult> {
    // Get existing exercise
    const exercise = await prisma.exercise.findUnique({
      where: { id: exerciseId },
      include: { modules: true, topics: true },
    });

    if (!exercise) {
      throw new ApplicationError('Exercise not found', 'NOT_FOUND', 404, true);
    }

    if (!exercise.isAIGenerated) {
      throw new ApplicationError('Only AI-generated exercises can be regenerated', 'INVALID_OPERATION', 400, true);
    }

    logger.info({ exerciseId, feedback }, 'Regenerating exercise with feedback');

    // Prepare regeneration request
    const request: AIExerciseRequest = {
      topic: exercise.topics?.type || TopicType.VECTORES,
      moduleType: exercise.modules.type,
      exerciseType: exercise.type,
      difficulty: exercise.difficulty,
      count: 1,
      context: `Previous exercise feedback: ${feedback}`,
    };

    const result = await this.generateExercise(request, {
      moduleId: exercise.moduleId,
      topicId: exercise.topicId || undefined,
      saveToDatabase: true,
    });

    // If successful, delete old exercise
    if (result.success && result.exerciseIds.length > 0) {
      await prisma.exercise.delete({
        where: { id: exerciseId },
      });
      logger.info({ oldExerciseId: exerciseId, newExerciseId: result.exerciseIds[0] }, 'Exercise replaced');
    }

    return result;
  }
}

/**
 * Export singleton instance
 */
export const aiExerciseGenerator = new AIExerciseGenerator();

/**
 * Export convenience functions
 */
export async function generateExercise(
  request: AIExerciseRequest,
  options?: GenerationOptions
): Promise<GenerationResult> {
  return await aiExerciseGenerator.generateExercise(request, options);
}

export async function generateExercises(
  request: AIExerciseRequest & { count: number },
  options?: GenerationOptions
): Promise<GenerationResult> {
  return await aiExerciseGenerator.generateExercises(request, options);
}

export async function generateBatch(
  batchRequest: BatchGenerationRequest
): Promise<Record<string, GenerationResult>> {
  return await aiExerciseGenerator.generateBatch(batchRequest);
}

export default aiExerciseGenerator;
