import {
  INestApplication,
  UnauthorizedException,
  ValidationPipe,
  VersioningType,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import validationOptions from '../utils/validation-options';
import { NotificationsController } from './notifications.controller';
import { NotificationsService } from './notifications.service';
import { TeamAccessService } from '../teams/team-access.service';

describe('notifications HTTP boundary', () => {
  let app: INestApplication;
  const service = {
    listForUser: jest.fn(),
    getUnreadCount: jest.fn(),
    markRead: jest.fn(),
    markAllRead: jest.fn(),
  };
  const access = { assertMember: jest.fn() };

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      controllers: [NotificationsController],
      providers: [
        { provide: NotificationsService, useValue: service },
        { provide: TeamAccessService, useValue: access },
      ],
    })
      .overrideGuard(AuthGuard('jwt'))
      .useValue({
        canActivate(context) {
          const httpRequest = context.switchToHttp().getRequest();
          if (httpRequest.headers.authorization !== 'Bearer user') {
            throw new UnauthorizedException();
          }
          httpRequest.user = { id: 42, role: { id: 2 } };
          return true;
        },
      })
      .compile();
    app = module.createNestApplication();
    app.setGlobalPrefix('api');
    app.enableVersioning({ type: VersioningType.URI });
    app.useGlobalPipes(new ValidationPipe(validationOptions));
    await app.init();
  });

  afterAll(() => app.close());
  beforeEach(() => jest.clearAllMocks());

  it('requires authentication', async () => {
    await request(app.getHttpServer()).get('/api/v1/teams/10/notifications').expect(401);
  });

  it('lists notifications for the authenticated user after checking membership', async () => {
    access.assertMember.mockResolvedValue(undefined);
    service.listForUser.mockResolvedValue({ data: [], page: 2, limit: 5, total: 0, hasNextPage: false });

    await request(app.getHttpServer())
      .get('/api/v1/teams/10/notifications?page=2&limit=5')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(access.assertMember).toHaveBeenCalledWith(42, 10);
    expect(service.listForUser).toHaveBeenCalledWith(42, 10, 2, 5);
  });

  it('defaults to page 1 and limit 20 when not provided', async () => {
    access.assertMember.mockResolvedValue(undefined);
    service.listForUser.mockResolvedValue({ data: [], page: 1, limit: 20, total: 0, hasNextPage: false });

    await request(app.getHttpServer())
      .get('/api/v1/teams/10/notifications')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(service.listForUser).toHaveBeenCalledWith(42, 10, 1, 20);
  });

  it('returns the unread count', async () => {
    access.assertMember.mockResolvedValue(undefined);
    service.getUnreadCount.mockResolvedValue(4);

    const response = await request(app.getHttpServer())
      .get('/api/v1/teams/10/notifications/unread-count')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(response.body).toEqual({ count: 4 });
  });

  it('marks a single notification as read', async () => {
    access.assertMember.mockResolvedValue(undefined);
    service.markRead.mockResolvedValue(undefined);

    await request(app.getHttpServer())
      .patch('/api/v1/teams/10/notifications/7/read')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(service.markRead).toHaveBeenCalledWith(7, 42);
  });

  it('marks all notifications as read', async () => {
    access.assertMember.mockResolvedValue(undefined);
    service.markAllRead.mockResolvedValue(undefined);

    await request(app.getHttpServer())
      .patch('/api/v1/teams/10/notifications/read-all')
      .set('Authorization', 'Bearer user')
      .expect(200);

    expect(service.markAllRead).toHaveBeenCalledWith(42, 10);
  });
});
