/**
 * @ai-summary Integration tests for auth API endpoints
 * @ai-context Tests complete request/response cycle with mocked Auth0
 */

import request from 'supertest';
import { buildApp } from '../../../../app';
import { pool } from '../../../../core/config/database';
import { auth0ManagementClient } from '../../../../core/auth/auth0-management.client';
import fastifyPlugin from 'fastify-plugin';
import { FastifyInstance } from 'fastify';

// Mock Auth0 Management client
jest.mock('../../../../core/auth/auth0-management.client');
const mockAuth0Client = jest.mocked(auth0ManagementClient);

// Mock auth plugin for protected routes
jest.mock('../../../../core/plugins/auth.plugin', () => {
  return {
    default: fastifyPlugin(async function (fastify) {
      fastify.decorate('authenticate', async function (request, _reply) {
        request.user = { sub: 'auth0|test-user-123' };
      });
    }, { name: 'auth-plugin' }),
  };
});

describe('Auth Integration Tests', () => {
  let app: FastifyInstance;

  beforeAll(async () => {
    app = await buildApp();
    await app.ready();

    // Ensure user_profiles table exists (should be created by migrations)
    // We don't need to run migration here as it should already exist
  });

  afterAll(async () => {
    await app.close();
    await pool.end();
  });

  beforeEach(async () => {
    jest.clearAllMocks();

    // Clean up test data before each test
    await pool.query('DELETE FROM user_profiles WHERE auth0_sub = $1', [
      'auth0|test-user-123',
    ]);
    await pool.query('DELETE FROM user_profiles WHERE email = $1', [
      'newuser@example.com',
    ]);
  });

  describe('POST /api/auth/signup', () => {
    it('should create a new user successfully', async () => {
      const email = 'newuser@example.com';
      const password = 'Password123';
      const auth0UserId = 'auth0|new-user-456';

      mockAuth0Client.createUser.mockResolvedValue(auth0UserId);

      const response = await request(app.server)
        .post('/api/auth/signup')
        .send({ email, password })
        .expect(201);

      expect(response.body).toMatchObject({
        userId: auth0UserId,
        email,
        message: expect.stringContaining('check your email'),
      });

      expect(mockAuth0Client.createUser).toHaveBeenCalledWith({ email, password });

      // Verify user was created in local database
      const userResult = await pool.query(
        'SELECT * FROM user_profiles WHERE auth0_sub = $1',
        [auth0UserId]
      );
      expect(userResult.rows).toHaveLength(1);
      expect(userResult.rows[0].email).toBe(email);
      expect(userResult.rows[0].email_verified).toBe(false);
    });

    it('should reject signup with invalid email', async () => {
      const response = await request(app.server)
        .post('/api/auth/signup')
        .send({ email: 'invalid-email', password: 'Password123' })
        .expect(400);

      expect(response.body.message).toContain('validation');
    });

    it('should reject signup with weak password', async () => {
      const response = await request(app.server)
        .post('/api/auth/signup')
        .send({ email: 'test@example.com', password: 'weak' })
        .expect(400);

      expect(response.body.message).toContain('validation');
    });

    it('should reject signup when email already exists', async () => {
      const email = 'existing@example.com';
      const password = 'Password123';

      mockAuth0Client.createUser.mockRejectedValue(
        new Error('User already exists')
      );

      const response = await request(app.server)
        .post('/api/auth/signup')
        .send({ email, password })
        .expect(409);

      expect(response.body.error).toBe('Conflict');
      expect(response.body.message).toContain('already exists');
    });
  });

  describe('GET /api/auth/verify-status', () => {
    beforeEach(async () => {
      // Create a test user profile
      await pool.query(
        'INSERT INTO user_profiles (auth0_sub, email, email_verified) VALUES ($1, $2, $3)',
        ['auth0|test-user-123', 'test@example.com', false]
      );
    });

    it('should return verification status', async () => {
      const email = 'test@example.com';

      mockAuth0Client.getUser.mockResolvedValue({
        userId: 'auth0|test-user-123',
        email,
        emailVerified: false,
      });

      const response = await request(app.server)
        .get('/api/auth/verify-status')
        .expect(200);

      expect(response.body).toMatchObject({
        emailVerified: false,
        email,
      });

      expect(mockAuth0Client.getUser).toHaveBeenCalledWith('auth0|test-user-123');
    });

    it('should update local database when verification status changes', async () => {
      const email = 'test@example.com';

      mockAuth0Client.getUser.mockResolvedValue({
        userId: 'auth0|test-user-123',
        email,
        emailVerified: true,
      });

      const response = await request(app.server)
        .get('/api/auth/verify-status')
        .expect(200);

      expect(response.body.emailVerified).toBe(true);

      // Verify local database was updated
      const userResult = await pool.query(
        'SELECT email_verified FROM user_profiles WHERE auth0_sub = $1',
        ['auth0|test-user-123']
      );
      expect(userResult.rows[0].email_verified).toBe(true);
    });
  });

  describe('POST /api/auth/resend-verification', () => {
    beforeEach(async () => {
      // Create a test user profile
      await pool.query(
        'INSERT INTO user_profiles (auth0_sub, email, email_verified) VALUES ($1, $2, $3)',
        ['auth0|test-user-123', 'test@example.com', false]
      );
    });

    it('should resend verification email when user is not verified', async () => {
      mockAuth0Client.checkEmailVerified.mockResolvedValue(false);
      mockAuth0Client.resendVerificationEmail.mockResolvedValue(undefined);

      const response = await request(app.server)
        .post('/api/auth/resend-verification')
        .expect(200);

      expect(response.body.message).toContain('Verification email sent');
      expect(mockAuth0Client.checkEmailVerified).toHaveBeenCalledWith(
        'auth0|test-user-123'
      );
      expect(mockAuth0Client.resendVerificationEmail).toHaveBeenCalledWith(
        'auth0|test-user-123'
      );
    });

    it('should skip sending email when user is already verified', async () => {
      mockAuth0Client.checkEmailVerified.mockResolvedValue(true);

      const response = await request(app.server)
        .post('/api/auth/resend-verification')
        .expect(200);

      expect(response.body.message).toContain('already verified');
      expect(mockAuth0Client.resendVerificationEmail).not.toHaveBeenCalled();
    });
  });
});
