/**
 * @file notification-service.test.ts
 * @description Unit tests for NotificationService — verifies that system, email,
 * and WhatsApp channels are dispatched correctly for each scenario.
 *
 * SRS Coverage:
 *  §6.14.3 — Delivery Channels: in-system, email, WhatsApp
 *  §1.1.1  — WhatsApp is a supported notification channel (reminders, workflow events)
 *  §1.1.2  — LOI documents shared via email ONLY (not WhatsApp)
 */

import { NotificationService } from '../services/NotificationService.js';
import { sendEmail } from '../common/utils/email.service.js';

// ─── Mocks ───────────────────────────────────────────────────────────────────

jest.mock('../common/utils/email.service.js', () => ({
  sendEmail: jest.fn().mockResolvedValue(true),
}));

jest.mock('../database/models/index.js', () => ({
  default: {
    Notification: {
      create: jest.fn().mockResolvedValue({ id: 'notif-1', createdAt: new Date() }),
      count: jest.fn().mockResolvedValue(0),
    },
    PushSubscription: {
      findAll: jest.fn().mockResolvedValue([]),
    },
  },
}));

// Disable Redis so async channels fall through to console (no BullMQ needed in tests)
process.env.ENABLE_REDIS = 'false';
process.env.FRONTEND_URL = 'http://localhost:5173';

jest.mock('../common/utils/socket.js', () => ({
  getIO: jest.fn().mockReturnValue(null),
}));

const sendEmailMock = sendEmail as jest.Mock;

// ─── Helpers ─────────────────────────────────────────────────────────────────

const basePayload = {
  title: 'Test Notification',
  message: 'Test message body',
  channels: ['email', 'system'] as Array<'email' | 'whatsapp' | 'system' | 'push'>,
};

// ─── Tests ────────────────────────────────────────────────────────────────────

describe('NotificationService — channel dispatch', () => {
  beforeEach(() => jest.clearAllMocks());

  // ── System channel ─────────────────────────────────────────────────────────

  describe('system channel', () => {
    it('TC-NS-01: creates an in-app Notification record when system channel is included', async () => {
      const db = (await import('../database/models/index.js')).default as any;

      await NotificationService.notify('user-123', 'test@re.com', {
        ...basePayload,
        channels: ['system'],
      });

      expect(db.Notification.create).toHaveBeenCalledWith(
        expect.objectContaining({ userId: 'user-123', isRead: false })
      );
    });

    it('TC-NS-02: skips Notification.create when userId is null (applicant not yet a system user)', async () => {
      const db = (await import('../database/models/index.js')).default as any;

      await NotificationService.notify(null, 'applicant@gmail.com', {
        ...basePayload,
        channels: ['system'],
      });

      expect(db.Notification.create).not.toHaveBeenCalled();
    });
  });

  // ── Email channel ─────────────────────────────────────────────────────────

  describe('email channel', () => {
    it('TC-NS-03: does NOT call sendEmail synchronously (Redis disabled — logs and skips)', async () => {
      await NotificationService.notify('user-123', 'test@re.com', {
        ...basePayload,
        channels: ['email'],
      });
      // With ENABLE_REDIS=false, the async channel is skipped; no direct sendEmail call
      expect(sendEmailMock).not.toHaveBeenCalled();
    });

    it('TC-NS-04: processJob triggers sendEmail for email channel', async () => {
      await NotificationService.processJob({
        userId: 'user-abc',
        email: 'reviewer@re.com',
        title: 'Action Required',
        message: 'Please review the application.',
        channels: ['email'],
        templateCode: 'WORKFLOW_ACTION_REQUIRED',
        placeholders: { link: 'http://localhost:5173/apps/1' },
      });

      expect(sendEmailMock).toHaveBeenCalledWith(
        'reviewer@re.com',
        'Action Required',
        'WORKFLOW_ACTION_REQUIRED',
        expect.objectContaining({ link: 'http://localhost:5173/apps/1' })
      );
    });

    it('TC-NS-05: processJob does NOT call sendEmail when email is null/undefined', async () => {
      await NotificationService.processJob({
        userId: 'user-abc',
        email: null,
        title: 'Test',
        message: 'No email',
        channels: ['email'],
        templateCode: 'GENERIC_NOTIFICATION',
        placeholders: {},
      });

      expect(sendEmailMock).not.toHaveBeenCalled();
    });
  });

  // ── WhatsApp channel ─────────────────────────────────────────────────────

  describe('whatsapp channel', () => {
    it('TC-NS-06: processJob calls sendWhatsApp with phone from placeholders', async () => {
      const spy = jest
        .spyOn(NotificationService, 'sendWhatsApp')
        .mockResolvedValue(true);

      await NotificationService.processJob({
        userId: 'user-wa',
        email: null,
        title: 'WA Test',
        message: 'WhatsApp message',
        channels: ['whatsapp'],
        templateCode: 'QUESTIONNAIRE_REMINDER',
        placeholders: { phone: '+919876543210', applicantName: 'Ravi' },
      });

      expect(spy).toHaveBeenCalledWith(
        '+919876543210',
        'QUESTIONNAIRE_REMINDER',
        expect.objectContaining({ applicantName: 'Ravi' })
      );

      spy.mockRestore();
    });

    it('TC-NS-07: sendWhatsApp resolves without throwing (mock contract)', async () => {
      await expect(
        NotificationService.sendWhatsApp('+919876543210', 'RESIGNATION_RECEIVED', {
          dealerName: 'Kumar Dealers',
        })
      ).resolves.toBe(true);
    });
  });

  // ── Questionnaire reminder (SRS §1.1.1) ──────────────────────────────────

  describe('sendQuestionnaireReminder', () => {
    it('TC-NS-08: sends QUESTIONNAIRE_REMINDER via email + whatsapp channels', async () => {
      const notifySpy = jest
        .spyOn(NotificationService, 'notify')
        .mockResolvedValue(undefined);

      await NotificationService.sendQuestionnaireReminder(
        'applicant@gmail.com',
        '+919876543210',
        'Rahul Sharma',
        { location: 'Chennai' }
      );

      expect(notifySpy).toHaveBeenCalledWith(
        null,
        'applicant@gmail.com',
        expect.objectContaining({
          templateCode: 'QUESTIONNAIRE_REMINDER',
          channels: expect.arrayContaining(['email', 'whatsapp']),
          placeholders: expect.objectContaining({
            applicantName: 'Rahul Sharma',
            location: 'Chennai',
          }),
        })
      );

      notifySpy.mockRestore();
    });

    it('TC-NS-09: questionnaire reminder includes a CTA link to the applicant portal', async () => {
      const notifySpy = jest
        .spyOn(NotificationService, 'notify')
        .mockResolvedValue(undefined);

      await NotificationService.sendQuestionnaireReminder(
        'applicant@gmail.com',
        '+91000',
        'Test User'
      );

      const call = notifySpy.mock.calls[0][2];
      expect(call.placeholders?.link).toContain('localhost:5173');
      notifySpy.mockRestore();
    });
  });
});
