/**
 * Integration Tests - Auth API
 * 
 * Tests for:
 * - User registration
 * - User login
 * - JWT token validation
 * - Protected routes
 * - Password strength validation
 */

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 { authRoutes } from '../../src/modules/auth/auth.routes';

// 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('Auth API Integration', () => {
  let app: express.Application;

  beforeAll(async () => {
    // Ensure test database is clean
    await prisma.$connect();
  });

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

  beforeEach(async () => {
    // Clean up test data before each test
    await prisma.refreshToken.deleteMany({});
    await prisma.passwordResetToken.deleteMany({});
    await prisma.exerciseAttempt.deleteMany({});
    await prisma.progress.deleteMany({});
    await prisma.user.deleteMany({
      where: {
        email: {
          contains: 'test@',
        },
      },
    });

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

  // ============================================
  // REGISTRATION TESTS
  // ============================================

  describe('POST /api/auth/register', () => {
    it('should register a new user successfully', async () => {
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'test@example.com',
          password: 'SecurePass123!',
          username: 'testuser',
        });

      expect(response.status).toBe(201);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('token');
      expect(response.body.data.user).toHaveProperty('id');
      expect(response.body.data.user.email).toBe('test@example.com');
      expect(response.body.data.user).not.toHaveProperty('passwordHash');
    });

    it('should reject weak passwords', async () => {
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'test2@example.com',
          password: '123',
          username: 'testuser2',
        });

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

    it('should reject duplicate emails', async () => {
      // Create first user
      await request(app)
        .post('/api/auth/register')
        .send({
          email: 'duplicate@example.com',
          password: 'SecurePass123!',
          username: 'user1',
        });

      // Try to create second user with same email
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'duplicate@example.com',
          password: 'SecurePass123!',
          username: 'user2',
        });

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

    it('should reject duplicate usernames', async () => {
      // Create first user
      await request(app)
        .post('/api/auth/register')
        .send({
          email: 'user1@example.com',
          password: 'SecurePass123!',
          username: 'uniqueuser',
        });

      // Try to create second user with same username
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'user2@example.com',
          password: 'SecurePass123!',
          username: 'uniqueuser',
        });

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

  // ============================================
  // LOGIN TESTS
  // ============================================

  describe('POST /api/auth/login', () => {
    beforeEach(async () => {
      // Register a user for login tests
      await request(app)
        .post('/api/auth/register')
        .send({
          email: 'login@example.com',
          password: 'SecurePass123!',
          username: 'loginuser',
        });
    });

    it('should login with correct credentials', async () => {
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          email: 'login@example.com',
          password: 'SecurePass123!',
        });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('token');
      expect(response.body.data.user.email).toBe('login@example.com');
    });

    it('should reject incorrect password', async () => {
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          email: 'login@example.com',
          password: 'WrongPass123!',
        });

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

    it('should reject non-existent user', async () => {
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          email: 'nonexistent@example.com',
          password: 'SecurePass123!',
        });

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

  // ============================================
  // PROFILE TESTS
  // ============================================

  describe('GET /api/auth/profile', () => {
    let authToken: string;
    let userId: string;

    beforeEach(async () => {
      // Clean up any existing profile test user
      await prisma.refreshToken.deleteMany({});
      await prisma.exerciseAttempt.deleteMany({});
      await prisma.progress.deleteMany({});
      await prisma.user.deleteMany({
        where: {
          email: 'profile@example.com',
        },
      });

      const registerResponse = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'profile@example.com',
          password: 'SecurePass123!',
          username: 'profileuser',
        });

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

    it('should get user profile with valid token', async () => {
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe(userId);
      expect(response.body.data.email).toBe('profile@example.com');
    });

    it('should reject request without token', async () => {
      const response = await request(app)
        .get('/api/auth/me');

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

    it('should reject request with invalid token', async () => {
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', 'Bearer invalid-token');

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