/**
 * Exercise Service Unit Tests
 *
 * Tests for:
 * - submitAttempt with correct answer
 * - submitAttempt with incorrect answer
 * - compareAnswers with exact matches
 * - compareAnswers with LaTeX expressions
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ExerciseDifficulty } from '@prisma/client';

// Mock dependencies FIRST before importing
vi.mock('../../src/shared/database/prisma.client', () => ({
  prisma: {
    exercise: {
      findUnique: vi.fn(),
      findMany: vi.fn(),
      findFirst: vi.fn(),
      count: vi.fn(),
    },
    exerciseAttempt: {
      create: vi.fn(),
      findMany: vi.fn(),
      findFirst: vi.fn(),
      count: vi.fn(),
      aggregate: vi.fn(),
    },
    progress: {
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      upsert: vi.fn(),
    },
    $transaction: vi.fn((callback) => callback(prisma)),
  },
}));

vi.mock('../../src/shared/utils/logger', () => ({
  logger: {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
  },
}));

vi.mock('../../src/modules/ranking/calculators/score.calculator', () => ({
  ScoreCalculator: {
    calculate: vi.fn().mockResolvedValue({
      basePoints: 10,
      streakMultiplier: 1.0,
      firstAttemptMultiplier: 1.2,
      speedMultiplier: 1.0,
      hintPenalty: 0,
      finalPoints: 12,
      breakdown: ['Base: 10 puntos (BASIC)', 'Primer intento: +2 puntos (+20%)'],
    }),
  },
}));

vi.mock('../../src/modules/ranking/ranking.service', () => ({
  RankingService: {
    processExerciseSubmission: vi.fn().mockResolvedValue(undefined),
  },
}));

// Import mocked modules and service - use the singleton export
import { prisma } from '../../src/shared/database/prisma.client';
import exerciseService from '../../src/modules/exercise/exercise.service';
import { NotFoundError } from '../../src/shared/types';
import { ScoreCalculator } from '../../src/modules/ranking/calculators/score.calculator';

describe('ExerciseService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  // ============================================
  // SUBMIT ATTEMPT TESTS
  // ============================================

  describe('submitAttempt', () => {
    const mockExercise = {
      id: 'exercise-123',
      moduleId: 'module-123',
      correctAnswer: '42',
      solutionSteps: [{ step: 1, explanation: 'Calculate the result' }],
      points: 10,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [{ level: 1, content: 'Use basic arithmetic' }],
      multipleChoiceOptions: null,
    };

    it('should submit correct attempt successfully', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
      (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
      (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
      (prisma.exercise.count as any).mockResolvedValueOnce(5);
      (prisma.progress.findUnique as any).mockResolvedValueOnce(null);

      const mockAttempt = {
        id: 'attempt-123',
        userId: 'user-123',
        exerciseId: 'exercise-123',
        status: 'CORRECT',
        pointsEarned: 12,
        timeSpentSeconds: 30,
        hintsUsed: 0,
        feedback: '¡Excelente! Respuesta correcta en el primer intento.',
        attemptNumber: 1,
        isPerfect: true,
        skipped: false,
        createdAt: new Date(),
      };

      (prisma.exerciseAttempt.create as any).mockResolvedValueOnce(mockAttempt);
      (prisma.progress.create as any).mockResolvedValueOnce({});

      const result = await exerciseService.submitAttempt(
        'exercise-123',
        'user-123',
        {
          answer: '42',
          timeSpent: 30,
          hintsUsed: 0,
        }
      );

      expect(result.isCorrect).toBe(true);
      expect(result.points).toBe(12);
      expect(result.message).toContain('Excelente');
      expect(result.correctAnswer).toBe('42');
    });

    it('should submit incorrect attempt successfully', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
      (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
      (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({
        status: 'INCORRECT',
        pointsEarned: 0,
        feedback: 'Respuesta incorrecta',
      });
      (prisma.exercise.count as any).mockResolvedValueOnce(5);
      (prisma.progress.upsert as any).mockResolvedValueOnce({});

      const result = await exerciseService.submitAttempt(
        'exercise-123',
        'user-123',
        {
          answer: '35', // Wrong answer
          timeSpent: 60,
          hintsUsed: 1,
        }
      );

      expect(result.isCorrect).toBe(false);
      expect(result.points).toBe(0);
      expect(result.correctAnswer).toBeUndefined();
    });

    it('should throw NotFoundError when exercise does not exist', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(null);

      await expect(
        exerciseService.submitAttempt('nonexistent-exercise', 'user-123', {
          answer: '42',
          timeSpent: 30,
        })
      ).rejects.toThrow(NotFoundError);
    });

    it('should handle skipped exercise', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
      (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
      (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({
        status: 'PENDING',
        pointsEarned: 0,
        feedback: 'Ejercicio omitido. Puedes volver a intentarlo más tarde.',
        skipped: true,
      });
      (prisma.exercise.count as any).mockResolvedValueOnce(5);
      (prisma.progress.upsert as any).mockResolvedValueOnce({});

      const result = await exerciseService.submitAttempt(
        'exercise-123',
        'user-123',
        {
          answer: '',
          timeSpent: 0,
          skipped: true,
        }
      );

      expect(result.isCorrect).toBe(false);
      expect(result.points).toBe(0);
      expect(result.message).toContain('omitido');
    });
  });

  // ============================================
  // GET EXERCISE BY ID TESTS
  // ============================================

  describe('getExerciseById', () => {
    it('should return exercise with details', async () => {
      const mockExercise = {
        id: 'exercise-123',
        moduleId: 'module-123',
        topicId: 'topic-123',
        type: 'CALCULATION',
        difficulty: ExerciseDifficulty.BASIC,
        order: 1,
        statement: 'What is 6 * 7?',
        correctAnswer: '42',
        solutionSteps: null,
        points: 10,
        timeLimitSeconds: 120,
        isPublished: true,
        hints: null,
        module: null,
        topic: null,
        attempts: [],
      };

      (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);

      const result = await exerciseService.getExerciseById('exercise-123');

      expect(result.id).toBe('exercise-123');
      expect(result.correctAnswer).toBe(''); // Hidden by default
    });

    it('should throw NotFoundError when exercise not found', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(null);

      await expect(exerciseService.getExerciseById('nonexistent')).rejects.toThrow(NotFoundError);
    });
  });

  // ============================================
  // GET USER ATTEMPTS TESTS
  // ============================================

  describe('getUserAttempts', () => {
    it('should return user attempts for exercise', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce({ id: 'exercise-123' });
      (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(15);
      (prisma.exerciseAttempt.findMany as any)
        .mockResolvedValueOnce([
          { status: 'CORRECT', pointsEarned: 12, createdAt: new Date() },
          { status: 'INCORRECT', pointsEarned: 0, createdAt: new Date() },
        ])
        // Second call for aggregate mock
        .mockResolvedValueOnce([{ pointsEarned: 12 }]);
      (prisma.exerciseAttempt.aggregate as any).mockResolvedValueOnce({
        _max: { pointsEarned: 12 },
      });

      const result = await exerciseService.getUserAttempts('exercise-123', 'user-123');

      expect(result.totalAttempts).toBe(15);
      expect(result.attempts.length).toBe(2);
      expect(result.hasCompleted).toBe(true);
    });

    it('should throw NotFoundError when exercise not found', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce(null);

      await expect(
        exerciseService.getUserAttempts('nonexistent', 'user-123')
      ).rejects.toThrow(NotFoundError);
    });
  });

  // ============================================
  // GET NEXT EXERCISE TESTS
  // ============================================

  describe('getNextExercise', () => {
    it('should return next exercise in order', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce({
        moduleId: 'module-123',
        order: 1,
      });

      const mockNextExercise = {
        id: 'exercise-124',
        order: 2,
        statement: 'Next question',
      };

      (prisma.exercise.findFirst as any).mockResolvedValueOnce(mockNextExercise);

      const result = await exerciseService.getNextExercise('exercise-123');

      expect(result?.id).toBe('exercise-124');
    });

    it('should return null when no next exercise', async () => {
      (prisma.exercise.findUnique as any).mockResolvedValueOnce({
        moduleId: 'module-123',
        order: 10,
      });
      (prisma.exercise.findFirst as any).mockResolvedValueOnce(null);

      const result = await exerciseService.getNextExercise('exercise-last');

      expect(result).toBeNull();
    });
  });
});

// ============================================
// COMPARE ANSWERS FUNCTION TESTS
// ============================================

describe('compareAnswers (via submitAttempt)', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should match exact answers', async () => {
    const mockExercise = {
      id: 'exercise-123',
      moduleId: 'module-123',
      correctAnswer: '42',
      points: 10,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
    (prisma.exercise.count as any).mockResolvedValueOnce(5);
    (prisma.progress.findUnique as any).mockResolvedValueOnce(null);
    (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({ status: 'CORRECT' });
    (prisma.progress.create as any).mockResolvedValueOnce({});
    (ScoreCalculator.calculate as any).mockResolvedValueOnce({ finalPoints: 12 });

    const result = await exerciseService.submitAttempt(
      'exercise-123',
      'user-123',
      { answer: '42', timeSpent: 30 }
    );

    expect(result.isCorrect).toBe(true);
  });

  it('should match answers with whitespace differences', async () => {
    const mockExercise = {
      id: 'exercise-123',
      moduleId: 'module-123',
      correctAnswer: 'matrix multiplication',
      points: 10,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
    (prisma.exercise.count as any).mockResolvedValueOnce(5);
    (prisma.progress.findUnique as any).mockResolvedValueOnce(null);
    (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({ status: 'CORRECT' });
    (prisma.progress.create as any).mockResolvedValueOnce({});
    (ScoreCalculator.calculate as any).mockResolvedValueOnce({ finalPoints: 12 });

    const result = await exerciseService.submitAttempt(
      'exercise-123',
      'user-123',
      { answer: '  matrix   multiplication  ', timeSpent: 30 }
    );

    expect(result.isCorrect).toBe(true);
  });

  it('should preserve LaTeX case sensitivity', async () => {
    const mockExercise = {
      id: 'exercise-latex',
      moduleId: 'module-123',
      correctAnswer: '\\Lambda',
      points: 20,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
    (prisma.exercise.count as any).mockResolvedValueOnce(5);
    (prisma.progress.findUnique as any).mockResolvedValueOnce(null);
    (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({ status: 'CORRECT' });
    (prisma.progress.create as any).mockResolvedValueOnce({});
    (ScoreCalculator.calculate as any).mockResolvedValueOnce({ finalPoints: 24 });

    const result = await exerciseService.submitAttempt(
      'exercise-latex',
      'user-123',
      { answer: '\\Lambda', timeSpent: 30 }
    );

    expect(result.isCorrect).toBe(true);
  });

  it('should normalize LaTeX delimiters', async () => {
    const mockExercise = {
      id: 'exercise-latex',
      moduleId: 'module-123',
      correctAnswer: '$$x^2 + y^2$$',
      points: 20,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
    (prisma.exercise.count as any).mockResolvedValueOnce(5);
    (prisma.progress.findUnique as any).mockResolvedValueOnce(null);
    (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({ status: 'CORRECT' });
    (prisma.progress.create as any).mockResolvedValueOnce({});
    (ScoreCalculator.calculate as any).mockResolvedValueOnce({ finalPoints: 24 });

    const result = await exerciseService.submitAttempt(
      'exercise-latex',
      'user-123',
      { answer: 'x^2 + y^2', timeSpent: 30 }
    );

    expect(result.isCorrect).toBe(true);
  });

  it('should handle \\left and \\right parentheses normalization', async () => {
    const mockExercise = {
      id: 'exercise-latex',
      moduleId: 'module-123',
      correctAnswer: '\\left(x + y\\right)',
      points: 20,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    (prisma.exerciseAttempt.findFirst as any).mockResolvedValueOnce(null);
    (prisma.exercise.count as any).mockResolvedValueOnce(5);
    (prisma.progress.findUnique as any).mockResolvedValueOnce(null);
    (prisma.exerciseAttempt.create as any).mockResolvedValueOnce({ status: 'CORRECT' });
    (prisma.progress.create as any).mockResolvedValueOnce({});
    (ScoreCalculator.calculate as any).mockResolvedValueOnce({ finalPoints: 24 });

    const result = await exerciseService.submitAttempt(
      'exercise-latex',
      'user-123',
      { answer: '(x + y)', timeSpent: 30 }
    );

    expect(result.isCorrect).toBe(true);
  });
});

// ============================================
// RACE CONDITION TESTS
// ============================================

describe('Exercise Progress Race Condition', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should exclude newly created attempt when checking for previous correct attempts', async () => {
    const mockExercise = {
      id: 'exercise-456',
      moduleId: 'module-123',
      correctAnswer: '42',
      points: 10,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    const newAttemptId = 'attempt-new-123';
    const newAttemptCreatedAt = new Date();
    
    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    
    // Mock the transaction behavior
    const mockTx = {
      exerciseAttempt: {
        create: vi.fn().mockResolvedValueOnce({
          id: newAttemptId,
          userId: 'user-123',
          exerciseId: 'exercise-456',
          status: 'CORRECT',
          createdAt: newAttemptCreatedAt,
          isPerfect: true,
        }),
        findFirst: vi.fn().mockResolvedValueOnce(null), // No previous correct attempts
      },
      exercise: {
        findUnique: vi.fn().mockResolvedValueOnce(mockExercise),
        count: vi.fn().mockResolvedValueOnce(10),
      },
      progress: {
        findUnique: vi.fn().mockResolvedValueOnce(null),
        create: vi.fn().mockResolvedValueOnce({}),
      },
    };

    (prisma.$transaction as any).mockImplementationOnce(async (callback: any) => {
      return await callback(mockTx);
    });

    const result = await exerciseService.submitAttempt(
      'exercise-456',
      'user-123',
      { answer: '42', timeSpent: 30 }
    );

    // Verify that findFirst was called with the exclusion of the new attempt
    expect(mockTx.exerciseAttempt.findFirst).toHaveBeenCalledWith({
      where: {
        userId: 'user-123',
        exerciseId: 'exercise-456',
        status: 'CORRECT',
        id: { not: newAttemptId },
        createdAt: { lt: newAttemptCreatedAt },
      },
      select: { id: true },
    });

    expect(result.isCorrect).toBe(true);
  });

  it('should not double count exercises when checking previous attempts', async () => {
    const mockExercise = {
      id: 'exercise-789',
      moduleId: 'module-456',
      correctAnswer: '100',
      points: 15,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    const existingProgress = {
      id: 'progress-123',
      userId: 'user-123',
      moduleId: 'module-456',
      exercisesCompleted: 3,
      points: 45,
      totalExercises: 10,
    };

    const newAttemptId = 'attempt-new-456';
    const newAttemptCreatedAt = new Date();
    
    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(1); // Previous attempts exist
    
    const mockTx = {
      exerciseAttempt: {
        create: vi.fn().mockResolvedValueOnce({
          id: newAttemptId,
          userId: 'user-123',
          exerciseId: 'exercise-789',
          status: 'CORRECT',
          createdAt: newAttemptCreatedAt,
          isPerfect: true,
        }),
        findFirst: vi.fn().mockResolvedValueOnce({ id: 'old-attempt-123' }), // Previous correct attempt exists
      },
      exercise: {
        findUnique: vi.fn().mockResolvedValueOnce(mockExercise),
        count: vi.fn().mockResolvedValueOnce(10),
      },
      progress: {
        findUnique: vi.fn().mockResolvedValueOnce(existingProgress),
        update: vi.fn().mockResolvedValueOnce({}),
      },
    };

    (prisma.$transaction as any).mockImplementationOnce(async (callback: any) => {
      return await callback(mockTx);
    });

    const result = await exerciseService.submitAttempt(
      'exercise-789',
      'user-123',
      { answer: '100', timeSpent: 25 }
    );

    // Verify progress was NOT incremented since it's not the first correct attempt
    expect(mockTx.progress.update).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          exercisesCompleted: 3, // Same as before, not incremented
          points: expect.any(Number),
        }),
      })
    );

    expect(result.isCorrect).toBe(true);
  });
});

describe('Exercise Progress Division by Zero', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should handle module with zero exercises gracefully', async () => {
    const mockExercise = {
      id: 'exercise-empty',
      moduleId: 'module-empty',
      correctAnswer: '42',
      points: 10,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    const newAttemptId = 'attempt-new-789';
    const newAttemptCreatedAt = new Date();
    
    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    
    const mockTx = {
      exerciseAttempt: {
        create: vi.fn().mockResolvedValueOnce({
          id: newAttemptId,
          userId: 'user-123',
          exerciseId: 'exercise-empty',
          status: 'CORRECT',
          createdAt: newAttemptCreatedAt,
          isPerfect: true,
        }),
        findFirst: vi.fn().mockResolvedValueOnce(null),
      },
      exercise: {
        findUnique: vi.fn().mockResolvedValueOnce(mockExercise),
        count: vi.fn().mockResolvedValueOnce(0), // Zero exercises
      },
      progress: {
        findUnique: vi.fn().mockResolvedValueOnce(null),
        create: vi.fn().mockResolvedValueOnce({}),
      },
    };

    (prisma.$transaction as any).mockImplementationOnce(async (callback: any) => {
      return await callback(mockTx);
    });

    const result = await exerciseService.submitAttempt(
      'exercise-empty',
      'user-123',
      { answer: '42', timeSpent: 30 }
    );

    // Verify that percentage is 0 when totalExercises is 0
    expect(mockTx.progress.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          percentage: 0, // Should be 0, not NaN
          exercisesCompleted: 1,
        }),
      })
    );

    expect(result.isCorrect).toBe(true);
  });

  it('should calculate correct percentage when module has exercises', async () => {
    const mockExercise = {
      id: 'exercise-normal',
      moduleId: 'module-normal',
      correctAnswer: '100',
      points: 20,
      timeLimitSeconds: 120,
      type: 'CALCULATION',
      hints: [],
      multipleChoiceOptions: null,
      solutionSteps: [],
    };

    const existingProgress = {
      id: 'progress-456',
      userId: 'user-123',
      moduleId: 'module-normal',
      exercisesCompleted: 4,
      points: 80,
      totalExercises: 10,
    };

    const newAttemptId = 'attempt-new-999';
    const newAttemptCreatedAt = new Date();
    
    (prisma.exercise.findUnique as any).mockResolvedValueOnce(mockExercise);
    (prisma.exerciseAttempt.count as any).mockResolvedValueOnce(0);
    
    const mockTx = {
      exerciseAttempt: {
        create: vi.fn().mockResolvedValueOnce({
          id: newAttemptId,
          userId: 'user-123',
          exerciseId: 'exercise-normal',
          status: 'CORRECT',
          createdAt: newAttemptCreatedAt,
          isPerfect: true,
        }),
        findFirst: vi.fn().mockResolvedValueOnce(null),
      },
      exercise: {
        findUnique: vi.fn().mockResolvedValueOnce(mockExercise),
        count: vi.fn().mockResolvedValueOnce(10), // 10 exercises
      },
      progress: {
        findUnique: vi.fn().mockResolvedValueOnce(existingProgress),
        update: vi.fn().mockResolvedValueOnce({}),
      },
    };

    (prisma.$transaction as any).mockImplementationOnce(async (callback: any) => {
      return await callback(mockTx);
    });

    const result = await exerciseService.submitAttempt(
      'exercise-normal',
      'user-123',
      { answer: '100', timeSpent: 20 }
    );

    // Verify that percentage is calculated correctly: (5/10) * 100 = 50%
    expect(mockTx.progress.update).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          percentage: 50, // (5/10) * 100
          exercisesCompleted: 5,
        }),
      })
    );

    expect(result.isCorrect).toBe(true);
  });
});