import { Test, TestingModule } from '@nestjs/testing';
import { NotificationsController } from './NotificationsController';
import { NotificationsService } from './NotificationsService';
import { vi } from 'vitest';
import type { Request, Response } from 'express';

describe('NotificationsController', () => {
  let controller: NotificationsController;
  let service: ReturnType<typeof vi.mocked<NotificationsService>>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [NotificationsController],
      providers: [
        {
          provide: NotificationsService,
          useValue: {
            getUnreadNotifications: vi.fn(),
            getAllNotifications: vi.fn(),
            markAsRead: vi.fn(),
          },
        },
      ],
    }).compile();

    controller = module.get<NotificationsController>(NotificationsController);
    service = vi.mocked(module.get(NotificationsService));
  });

  describe('getUnreadNotifications', () => {
    it('should return unread notifications for authenticated user', async () => {
      const mockNotifications = [
        { id: '1', message: 'Test notification 1' },
        { id: '2', message: 'Test notification 2' },
      ];

      service.getUnreadNotifications.mockResolvedValue(mockNotifications);

      const mockReq = {
        user: { userId: 'user-123' },
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getUnreadNotifications(mockReq, mockRes);

      expect(service.getUnreadNotifications).toHaveBeenCalledWith('user-123');
      expect(mockRes.status).toHaveBeenCalledWith(200);
      expect(mockRes.json).toHaveBeenCalledWith({ notifications: mockNotifications });
    });

    it('should return 401 when user is not authenticated', async () => {
      const mockReq = {} as unknown as Request;
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getUnreadNotifications(mockReq, mockRes);

      expect(service.getUnreadNotifications).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });

    it('should return 401 when userId is missing', async () => {
      const mockReq = {
        user: {},
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getUnreadNotifications(mockReq, mockRes);

      expect(service.getUnreadNotifications).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });
  });

  describe('markAsRead', () => {
    it('should mark notification as read for authenticated user', async () => {
      service.markAsRead.mockResolvedValue(undefined);

      const mockReq = {
        user: { userId: 'user-123' },
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.markAsRead('notification-123', mockReq, mockRes);

      expect(service.markAsRead).toHaveBeenCalledWith('notification-123', 'user-123');
      expect(mockRes.status).toHaveBeenCalledWith(200);
      expect(mockRes.json).toHaveBeenCalledWith({ success: true });
    });

    it('should return 401 when user is not authenticated', async () => {
      const mockReq = {} as unknown as Request;
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.markAsRead('notification-123', mockReq, mockRes);

      expect(service.markAsRead).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });

    it('should return 401 when userId is missing', async () => {
      const mockReq = {
        user: {},
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.markAsRead('notification-123', mockReq, mockRes);

      expect(service.markAsRead).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });
  });

  describe('getAllNotifications', () => {
    it('should return all notifications for authenticated user', async () => {
      const mockNotifications = [
        { id: '1', message: 'Test notification 1' },
        { id: '2', message: 'Test notification 2' },
        { id: '3', message: 'Test notification 3' },
      ];

      service.getAllNotifications.mockResolvedValue(mockNotifications);

      const mockReq = {
        user: { userId: 'user-123' },
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getAllNotifications(mockReq, mockRes);

      expect(service.getAllNotifications).toHaveBeenCalledWith('user-123');
      expect(mockRes.status).toHaveBeenCalledWith(200);
      expect(mockRes.json).toHaveBeenCalledWith({ notifications: mockNotifications });
    });

    it('should return 401 when user is not authenticated', async () => {
      const mockReq = {} as unknown as Request;
      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getAllNotifications(mockReq, mockRes);

      expect(service.getAllNotifications).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });

    it('should return 401 when userId is missing', async () => {
      const mockReq = {
        user: {},
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getAllNotifications(mockReq, mockRes);

      expect(service.getAllNotifications).not.toHaveBeenCalled();
      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ error: 'Unauthorized' });
    });

    it('should handle empty notifications list', async () => {
      service.getAllNotifications.mockResolvedValue([]);

      const mockReq = {
        user: { userId: 'user-123' },
      } as unknown as Request;

      const mockRes = {
        status: vi.fn().mockReturnThis(),
        json: vi.fn(),
      } as unknown as Response;

      await controller.getAllNotifications(mockReq, mockRes);

      expect(service.getAllNotifications).toHaveBeenCalledWith('user-123');
      expect(mockRes.status).toHaveBeenCalledWith(200);
      expect(mockRes.json).toHaveBeenCalledWith({ notifications: [] });
    });
  });
});
