/**
 * Integration Tests - Exercise API
 * 
 * Tests for:
 * - Fetching exercises
 * - Submitting answers
 * - Progress tracking
 * - Concurrent submission handling
 */

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import request from 'supertest';
import express, { ErrorRequestHandler } from 'express';
import { prisma } from '../../src/shared/database/prisma.client';
import exerciseRoutes from '../../src/modules/exercise/exercise.routes';
import { authRoutes } from '../../src/modules/auth/auth.routes';
import { randomUUID } from 'crypto';

// Simple error handler for tests
const testErrorHandler: ErrorRequestHandler = (err, req, res, _next) => {
  const statusCode = err.statusCode || err.status || 500;
  const code = err.code || 'INTERNAL_ERROR';
  const message = err.message || 'An unexpected error occurred';
  
  res.status(statusCode).json({
    success: false,
    error: {
      code,
      message,
    },
    meta: {
      timestamp: new Date().toISOString(),
    },
  });
};

describe('Exercise API Integration', () => {
  let app: express.Application;
  let authToken: string;
  let userId: string;
  let moduleId: string;
  let exerciseId: string;

  beforeAll(async () => {
    await prisma.$connect();
  });

  afterAll(async () => {
    await prisma.$disconnect();
  });

  beforeEach(async () => {
    // Clean up test data in correct order (respecting FK constraints)
    await prisma.exerciseAttempt.deleteMany({});
    await prisma.progress.deleteMany({});
    await prisma.exercise.deleteMany({
      where: {
        statement: {
          contains: 'Test Exercise',
        },
      },
    });
    // Delete test modules to avoid unique constraint violations
    await prisma.modules.deleteMany({
      where: {
        name: {
          contains: 'Test Module',
        },
      },
    });
    await prisma.refreshToken.deleteMany({});
    await prisma.user.deleteMany({
      where: {
        email: {
          contains: 'test@',
        },
      },
    });

    // Create fresh app
    app = express();
    app.use(express.json());
    app.use('/api/auth', authRoutes);
    app.use('/api/exercises', exerciseRoutes);
    
    // Add error handler last
    app.use(testErrorHandler);

    // Create test user
    const registerResponse = await request(app)
      .post('/api/auth/register')
      .send({
        email: `test-${randomUUID()}@example.com`,
        password: 'SecurePass123!',
        username: `testuser-${randomUUID()}`,
      });

    authToken = registerResponse.body.data.token;
    userId = registerResponse.body.data.user.id;

    // Create test module with unique order (random to avoid collisions)
    const testModule = await prisma.modules.create({
      data: {
        id: randomUUID(),
        name: 'Test Module',
        description: 'Test module for exercises',
        order: Math.floor(Math.random() * 1000000), // Random unique order
        type: 'FUNDAMENTOS',
        updatedAt: new Date(),
      },
    });
    moduleId = testModule.id;

    // Create test exercise
    const exercise = await prisma.exercise.create({
      data: {
        moduleId: moduleId,
        topicId: null,
        type: 'CALCULATION',
        difficulty: 'BASIC',
        order: 1,
        statement: 'Test Exercise: What is 2+2?',
        correctAnswer: '4',
        points: 10,
        timeLimitSeconds: 120,
        hints: [],
        solutionSteps: [],
        updatedAt: new Date(),
      },
    });
    exerciseId = exercise.id;
  });

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

  describe('GET /api/exercises/:id', () => {
    it('should get exercise details without exposing correct answer', async () => {
      const response = await request(app)
        .get(`/api/exercises/${exerciseId}`)
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe(exerciseId);
      expect(response.body.data.statement).toBe('Test Exercise: What is 2+2?');
      expect(response.body.data.correctAnswer).toBe('');
    });

    it('should return 404 for non-existent exercise', async () => {
      const response = await request(app)
        .get('/api/exercises/non-existent-id')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(404);
    });
  });

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

  describe('POST /api/exercises/:id/attempt', () => {
    it('should submit correct answer successfully', async () => {
      const response = await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '4',
          timeSpent: 30,
          hintsUsed: 0,
        });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.isCorrect).toBe(true);
      expect(response.body.data.points).toBeGreaterThan(0);
    });

    it('should handle incorrect answer', async () => {
      const response = await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '5',
          timeSpent: 30,
          hintsUsed: 0,
        });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.isCorrect).toBe(false);
      expect(response.body.data.points).toBe(0);
    });

    it('should validate LaTeX answer format', async () => {
      // Create exercise with LaTeX answer
      const latexExercise = await prisma.exercise.create({
        data: {
          moduleId: moduleId,
          type: 'CALCULATION',
          difficulty: 'BASIC',
          order: 2,
          statement: 'Test LaTeX: What is the fraction?',
          correctAnswer: '\\frac{1}{2}',
          points: 15,
          timeLimitSeconds: 120,
        },
      });

      const response = await request(app)
        .post(`/api/exercises/${latexExercise.id}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '\\frac{1}{2}',
          timeSpent: 30,
          hintsUsed: 0,
        });

      expect(response.status).toBe(200);
      expect(response.body.data.isCorrect).toBe(true);
    });

    it('should detect and reject XSS attempts in LaTeX', async () => {
      const response = await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '\\href{javascript:alert(1)}{x}',
          timeSpent: 30,
          hintsUsed: 0,
        });

      expect(response.status).toBe(400);
      expect(response.body.success).toBe(false);
    });

    it('should handle skipped exercises', async () => {
      const response = await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '',
          timeSpent: 0,
          skipped: true,
        });

      expect(response.status).toBe(200);
      expect(response.body.data.isCorrect).toBe(false);
      expect(response.body.data.skipped).toBe(true);
    });
  });

  // ============================================
  // CONCURRENT SUBMISSION TESTS
  // ============================================

  describe('Concurrent submission handling', () => {
    it('should handle multiple rapid submissions gracefully', async () => {
      const submissions = Array(5).fill(null).map(() =>
        request(app)
          .post(`/api/exercises/${exerciseId}/attempt`)
          .set('Authorization', `Bearer ${authToken}`)
          .send({
            answer: '4',
            timeSpent: 30,
            hintsUsed: 0,
          })
      );

      const results = await Promise.all(submissions);

      // All requests should complete without server errors
      results.forEach(result => {
        expect(result.status).toBeLessThan(500);
      });
    });
  });

  // ============================================
  // PROGRESS TESTS
  // ============================================

  describe('GET /api/exercises/:id/attempts', () => {
    it('should get user attempts for exercise', async () => {
      // Submit a few attempts first
      await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '5',
          timeSpent: 30,
          hintsUsed: 0,
        });

      await request(app)
        .post(`/api/exercises/${exerciseId}/attempt`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          answer: '4',
          timeSpent: 30,
          hintsUsed: 0,
        });

      const response = await request(app)
        .get(`/api/exercises/${exerciseId}/attempts`)
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.length).toBeGreaterThanOrEqual(2);
      expect(response.body.meta.hasCompleted).toBe(true);
    });
  });
});
