import { Test, TestingModule } from '@nestjs/testing';
import { NotificationTriggersService } from './notification-triggers.service';
import { NotificationsService } from './notifications.service';
import { NotificationSenderService } from './notification-sender.service';
import { NotificationType } from '@prisma/client';

describe('NotificationTriggersService', () => {
  let service: NotificationTriggersService;
  let notificationsService: NotificationsService;
  let senderService: NotificationSenderService;

  const mockNotificationsService = {
    create: jest.fn(),
  };

  const mockSenderService = {
    shouldSend: jest.fn(),
    getCustomText: jest.fn(),
  };

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

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        NotificationTriggersService,
        { provide: NotificationsService, useValue: mockNotificationsService },
        { provide: NotificationSenderService, useValue: mockSenderService },
      ],
    }).compile();

    service = module.get<NotificationTriggersService>(NotificationTriggersService);
    notificationsService = module.get<NotificationsService>(NotificationsService);
    senderService = module.get<NotificationSenderService>(NotificationSenderService);
  });

  describe('handleFunnelAssigned', () => {
    it('should call onClientAssigned with correct params', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service.handleFunnelAssigned(event);

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.CLIENT_ASSIGNED,
      );
      expect(mockNotificationsService.create).toHaveBeenCalledWith(
        'club-1',
        expect.objectContaining({
          userId: 'trainer-1',
          type: NotificationType.CLIENT_ASSIGNED,
        }),
      );
    });

    it('should return early if no trainer in event data', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: null,
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
        },
      };

      await service.handleFunnelAssigned(event);

      expect(mockSenderService.shouldSend).not.toHaveBeenCalled();
      expect(mockNotificationsService.create).not.toHaveBeenCalled();
    });

    it('should return early if no client in event data', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: null,
        },
      };

      await service.handleFunnelAssigned(event);

      expect(mockSenderService.shouldSend).not.toHaveBeenCalled();
      expect(mockNotificationsService.create).not.toHaveBeenCalled();
    });
  });

  describe('onClientAssigned', () => {
    it('should create notification when sending is enabled', async () => {
      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service['onClientAssigned']({
        clubId: 'club-1',
        trainerId: 'trainer-1',
        clientId: 'client-1',
        clientName: 'Иван Иванов',
      });

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.CLIENT_ASSIGNED,
      );
      expect(mockNotificationsService.create).toHaveBeenCalledWith(
        'club-1',
        expect.objectContaining({
          userId: 'trainer-1',
          type: NotificationType.CLIENT_ASSIGNED,
        }),
      );
    });

    it('should skip notification when sending is disabled', async () => {
      mockSenderService.shouldSend.mockResolvedValue(false);

      await service['onClientAssigned']({
        clubId: 'club-1',
        trainerId: 'trainer-1',
        clientId: 'client-1',
        clientName: 'Иван Иванов',
      });

      expect(mockNotificationsService.create).not.toHaveBeenCalled();
    });

    it('should use custom text when available', async () => {
      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue('Вам назначен новый клиент!');
      mockNotificationsService.create.mockResolvedValue({});

      await service['onClientAssigned']({
        clubId: 'club-1',
        trainerId: 'trainer-1',
        clientId: 'client-1',
        clientName: 'Иван Иванов',
      });

      expect(mockNotificationsService.create).toHaveBeenCalledWith(
        'club-1',
        expect.objectContaining({
          body: 'Вам назначен новый клиент!',
        }),
      );
    });
  });

  describe('handleTrainingCreated / onServiceBooked', () => {
    it('should create notification with correct service name', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
          service: { name: 'Персональная тренировка' },
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service.handleTrainingCreated(event);

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.SERVICE_BOOKED,
      );
      expect(mockNotificationsService.create).toHaveBeenCalledWith(
        'club-1',
        expect.objectContaining({
          userId: 'trainer-1',
          type: NotificationType.SERVICE_BOOKED,
        }),
      );
    });
  });

  describe('handleSalePaid', () => {
    it('should create notification with amount', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
          amount: 5000,
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service.handleSalePaid(event);

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.SERVICE_PAID,
      );
      expect(mockNotificationsService.create).toHaveBeenCalledWith(
        'club-1',
        expect.objectContaining({
          type: NotificationType.SERVICE_PAID,
        }),
      );
    });
  });

  describe('handleTrainingCancelled', () => {
    it('should create notification when enabled', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
          scheduledAt: '2026-02-20T10:00:00Z',
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service.handleTrainingCancelled(event);

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.BOOKING_CANCELLED,
      );
      expect(mockNotificationsService.create).toHaveBeenCalled();
    });
  });

  describe('handleTrainingCompleted', () => {
    it('should create notification when enabled', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(true);
      mockSenderService.getCustomText.mockResolvedValue(null);
      mockNotificationsService.create.mockResolvedValue({});

      await service.handleTrainingCompleted(event);

      expect(mockSenderService.shouldSend).toHaveBeenCalledWith(
        'club-1',
        NotificationType.TRAINING_DEDUCTED,
      );
      expect(mockNotificationsService.create).toHaveBeenCalled();
    });

    it('should skip notification when disabled', async () => {
      const event = {
        clubId: 'club-1',
        data: {
          trainer: { id: 'trainer-1' },
          client: { id: 'client-1', firstName: 'Иван', lastName: 'Иванов' },
        },
      };

      mockSenderService.shouldSend.mockResolvedValue(false);

      await service.handleTrainingCompleted(event);

      expect(mockNotificationsService.create).not.toHaveBeenCalled();
    });
  });
});
