// src/tests/integration/notification.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 type { UserProfile, Notification } from '../../types';
import { getPool } from '../../services/db/connection.db';

/**
 * @vitest-environment node
 */

describe('Notification API Routes Integration Tests', () => {
  let request: ReturnType<typeof supertest>;
  let testUser: UserProfile;
  let authToken: string;
  const createdUserIds: string[] = [];

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

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

    // 2. Seed some notifications for this user directly in the DB for predictable testing
    const notificationsToCreate = [
      { content: 'Your first unread notification', is_read: false },
      { content: 'Your second unread notification', is_read: false },
      { content: 'An old, read notification', is_read: true },
    ];

    for (const n of notificationsToCreate) {
      await getPool().query(
        `INSERT INTO public.notifications (user_id, content, is_read, link_url) 
         VALUES ($1, $2, $3, '/dashboard')`,
        [testUser.user.user_id, n.content, n.is_read],
      );
    }
  });

  afterAll(async () => {
    vi.unstubAllEnvs();
    // Notifications are deleted via CASCADE when the user is deleted.
    await cleanupDb({
      userIds: createdUserIds,
    });
  });

  describe('GET /api/users/notifications', () => {
    it('should fetch unread notifications for the authenticated user by default', async () => {
      const response = await request
        .get('/api/users/notifications')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      const notifications: Notification[] = response.body.data;
      expect(notifications).toHaveLength(2); // Only the two unread ones
      expect(notifications.every((n) => !n.is_read)).toBe(true);
    });

    it('should fetch all notifications when includeRead=true', async () => {
      const response = await request
        .get('/api/users/notifications?includeRead=true')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      const notifications: Notification[] = response.body.data;
      expect(notifications).toHaveLength(3); // All three notifications
    });

    it('should respect pagination with limit and offset', async () => {
      // Fetch with limit=1, should get the latest unread notification
      const response1 = await request
        .get('/api/users/notifications?limit=1')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response1.status).toBe(200);
      const notifications1: Notification[] = response1.body.data;
      expect(notifications1).toHaveLength(1);
      expect(notifications1[0].content).toBe('Your second unread notification'); // Assuming DESC order

      // Fetch with limit=1 and offset=1, should get the older unread notification
      const response2 = await request
        .get('/api/users/notifications?limit=1&offset=1')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response2.status).toBe(200);
      const notifications2: Notification[] = response2.body.data;
      expect(notifications2).toHaveLength(1);
      expect(notifications2[0].content).toBe('Your first unread notification');
    });

    it('should return 401 if user is not authenticated', async () => {
      const response = await request.get('/api/users/notifications');
      expect(response.status).toBe(401);
    });
  });

  describe('POST /api/users/notifications/:notificationId/mark-read', () => {
    it('should mark a single notification as read', async () => {
      const pool = getPool();
      const unreadNotifRes = await pool.query(
        `SELECT notification_id FROM public.notifications WHERE user_id = $1 AND is_read = false ORDER BY created_at ASC LIMIT 1`,
        [testUser.user.user_id],
      );
      const notificationIdToMark = unreadNotifRes.rows[0].notification_id;

      const response = await request
        .post(`/api/users/notifications/${notificationIdToMark}/mark-read`)
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(204);

      // Verify in the database
      const verifyRes = await pool.query(
        `SELECT is_read FROM public.notifications WHERE notification_id = $1`,
        [notificationIdToMark],
      );
      expect(verifyRes.rows[0].is_read).toBe(true);
    });
  });

  describe('POST /api/users/notifications/mark-all-read', () => {
    it('should mark all unread notifications as read', async () => {
      const response = await request
        .post('/api/users/notifications/mark-all-read')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(204);

      // Verify in the database
      const finalUnreadCountRes = await getPool().query(
        `SELECT COUNT(*) FROM public.notifications WHERE user_id = $1 AND is_read = false`,
        [testUser.user.user_id],
      );
      expect(Number(finalUnreadCountRes.rows[0].count)).toBe(0);
    });
  });
});
