// src/tests/integration/reactions.integration.test.ts
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
import supertest from 'supertest';
import { createAndLoginUser } from '../utils/testHelpers';
import { cleanupDb } from '../utils/cleanup';
import { getPool } from '../../services/db/connection.db';

/**
 * @vitest-environment node
 *
 * Integration tests for the Reactions API routes.
 * These routes were previously unmounted and are now available at /api/reactions.
 */

describe('Reactions API Routes Integration Tests', () => {
  let request: ReturnType<typeof supertest>;
  let authToken: string;
  let testRecipeId: number;
  const createdUserIds: string[] = [];
  const createdReactionIds: number[] = [];

  beforeAll(async () => {
    vi.stubEnv('FRONTEND_URL', 'https://example.com');
    const app = (await import('../../../server')).default;
    request = supertest(app);

    // Create a user for the tests
    const { user, token } = await createAndLoginUser({
      email: `reactions-user-${Date.now()}@example.com`,
      fullName: 'Reactions Test User',
      request,
    });
    authToken = token;
    createdUserIds.push(user.user.user_id);

    // Get an existing recipe ID from the seed data to use for reactions
    const recipeResult = await getPool().query(`SELECT recipe_id FROM public.recipes LIMIT 1`);
    if (recipeResult.rows.length > 0) {
      testRecipeId = recipeResult.rows[0].recipe_id;
    } else {
      // Create a minimal recipe if none exist
      const newRecipe = await getPool().query(
        `INSERT INTO public.recipes (title, description, instructions, prep_time_minutes, cook_time_minutes, servings)
         VALUES ('Test Recipe for Reactions', 'A test recipe', 'Test instructions', 10, 20, 4)
         RETURNING recipe_id`,
      );
      testRecipeId = newRecipe.rows[0].recipe_id;
    }
  });

  afterAll(async () => {
    vi.unstubAllEnvs();
    // Clean up reactions created during tests
    if (createdReactionIds.length > 0) {
      await getPool().query(
        'DELETE FROM public.user_reactions WHERE reaction_id = ANY($1::int[])',
        [createdReactionIds],
      );
    }
    await cleanupDb({
      userIds: createdUserIds,
    });
  });

  describe('GET /api/reactions', () => {
    it('should return reactions (public endpoint)', async () => {
      const response = await request.get('/api/reactions');

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toBeInstanceOf(Array);
    });

    it('should filter reactions by entityType', async () => {
      const response = await request.get('/api/reactions').query({ entityType: 'recipe' });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toBeInstanceOf(Array);
    });

    it('should filter reactions by entityId', async () => {
      const response = await request
        .get('/api/reactions')
        .query({ entityType: 'recipe', entityId: String(testRecipeId) });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toBeInstanceOf(Array);
    });
  });

  describe('GET /api/reactions/summary', () => {
    it('should return reaction summary for an entity', async () => {
      const response = await request
        .get('/api/reactions/summary')
        .query({ entityType: 'recipe', entityId: String(testRecipeId) });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      // Summary should have reaction counts
      expect(response.body.data).toBeDefined();
    });

    it('should return 400 when entityType is missing', async () => {
      const response = await request
        .get('/api/reactions/summary')
        .query({ entityId: String(testRecipeId) });

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

    it('should return 400 when entityId is missing', async () => {
      const response = await request.get('/api/reactions/summary').query({ entityType: 'recipe' });

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

  describe('POST /api/reactions/toggle', () => {
    it('should require authentication', async () => {
      const response = await request.post('/api/reactions/toggle').send({
        entity_type: 'recipe',
        entity_id: String(testRecipeId),
        reaction_type: 'like',
      });

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

    it('should add a reaction when none exists', async () => {
      const response = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          entity_id: String(testRecipeId),
          reaction_type: 'like',
        });

      expect(response.status).toBe(201);
      expect(response.body.success).toBe(true);
      expect(response.body.data.message).toBe('Reaction added.');
      expect(response.body.data.reaction).toBeDefined();

      // Track for cleanup
      if (response.body.data.reaction?.reaction_id) {
        createdReactionIds.push(response.body.data.reaction.reaction_id);
      }
    });

    it('should remove the reaction when toggled again', async () => {
      // First add the reaction
      const addResponse = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          entity_id: String(testRecipeId),
          reaction_type: 'love', // Use different type to not conflict
        });

      expect(addResponse.status).toBe(201);
      if (addResponse.body.data.reaction?.reaction_id) {
        createdReactionIds.push(addResponse.body.data.reaction.reaction_id);
      }

      // Then toggle it off
      const removeResponse = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          entity_id: String(testRecipeId),
          reaction_type: 'love',
        });

      expect(removeResponse.status).toBe(200);
      expect(removeResponse.body.success).toBe(true);
      expect(removeResponse.body.data.message).toBe('Reaction removed.');
    });

    it('should return 400 for missing entity_type', async () => {
      const response = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_id: String(testRecipeId),
          reaction_type: 'like',
        });

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

    it('should return 400 for missing entity_id', async () => {
      const response = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          reaction_type: 'like',
        });

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

    it('should return 400 for missing reaction_type', async () => {
      const response = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          entity_id: String(testRecipeId),
        });

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

    it('should accept entity_id as string (required format)', async () => {
      // entity_id must be a string per the Zod schema
      const response = await request
        .post('/api/reactions/toggle')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          entity_type: 'recipe',
          entity_id: String(testRecipeId),
          reaction_type: 'helpful',
        });

      // Should succeed (201 for add, 200 for remove)
      expect([200, 201]).toContain(response.status);
      expect(response.body.success).toBe(true);

      if (response.body.data.reaction?.reaction_id) {
        createdReactionIds.push(response.body.data.reaction.reaction_id);
      }
    });
  });
});
