/**
 * @file onboarding-stage-notifications.test.ts
 * @description Integration-level tests verifying email/notification triggers at
 * EVERY stage of the Dealer Onboarding pipeline.
 *
 * Stages covered (SRS §4.1.1 + §6.x):
 *  1.  Application Submitted          → Opportunity/Non-Opportunity Email
 *  2.  Questionnaire Link Sent        → Email + WhatsApp reminder
 *  3.  Questionnaire Completed        → Admin notified (system)
 *  4.  Shortlisted                    → DD-ZM + RBM notified (email + WhatsApp)
 *  5.  Level 1 Interview Scheduled    → DD-ZM + RBM + Applicant (Calendar mock)
 *  6.  Level 1 Approved               → DD-Lead + ZBH notified (email + WhatsApp)
 *  7.  Level 2 Interview Scheduled    → DD-Lead + ZBH + Applicant
 *  8.  Level 2 Approved               → NBH + DD-Head notified (email + WhatsApp)
 *  9.  Level 3 Interview Scheduled    → NBH + DD-Head + Applicant
 *  10. Level 3 Approved               → FDD team notified (email + system)
 *  11. FDD Submitted                  → Finance notified (email + system)
 *  12. Finance Approved (LOI Stage)   → DD-Head + NBH notified (email + WhatsApp)
 *  13. LOI Issued                     → Applicant via EMAIL only — NOT WhatsApp (SRS §1.1.2)
 *  14. Dealer Code Generated          → Finance + Legal + DD-Admin notified (system)
 *  15. LOA Issued                     → Applicant + DD-Head + NBH (email)
 *  16. EOR Completed                  → DD-Head + NBH (system alert)
 *  17. Inauguration Logged            → Applicant marked Live (system)
 */

import { NotificationService } from '../services/NotificationService.js';
import { ExternalMocksService } from '../common/utils/externalMocks.service.js';

const mockNotify = jest.spyOn(NotificationService, 'notify').mockResolvedValue(undefined);
const mockSendWhatsApp = jest.spyOn(NotificationService, 'sendWhatsApp').mockResolvedValue(true);
const mockScheduleMeeting = jest.spyOn(ExternalMocksService, 'mockScheduleMeeting');
const mockQReminder = jest.spyOn(NotificationService, 'sendQuestionnaireReminder').mockResolvedValue(undefined);

process.env.FRONTEND_URL = 'http://localhost:5173';

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

type NotifyCall = Parameters<typeof NotificationService.notify>;

const findCallByTemplate = (code: string): NotifyCall | undefined =>
  mockNotify.mock.calls.find((c: any[]) => c[2]?.templateCode === code) as any;

const findCallByChannel = (channel: string): NotifyCall | undefined =>
  mockNotify.mock.calls.find((c: any[]) => c[2]?.channels?.includes(channel)) as any;

// ─── Stage 1-2: Application + Questionnaire ──────────────────────────────────

describe('Onboarding Stage 1-2: Application Submission & Questionnaire', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-01: Questionnaire reminder is sent via email + WhatsApp (SRS §1.1.1)', async () => {
    await NotificationService.sendQuestionnaireReminder(
      'applicant@gmail.com',
      '+919876543210',
      'Amit Sharma',
      { location: 'Bangalore' }
    );

    expect(mockQReminder).toHaveBeenCalledWith(
      'applicant@gmail.com',
      '+919876543210',
      'Amit Sharma',
      expect.objectContaining({ location: 'Bangalore' })
    );
  });

  it('TC-ONB-02: Questionnaire reminder uses QUESTIONNAIRE_REMINDER template code', async () => {
    // Restore real implementation to verify template code
    mockQReminder.mockRestore();
    const realNotify = jest.spyOn(NotificationService, 'notify').mockResolvedValue(undefined);

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

    expect(realNotify).toHaveBeenCalledWith(
      null,
      'applicant@gmail.com',
      expect.objectContaining({ templateCode: 'QUESTIONNAIRE_REMINDER' })
    );

    realNotify.mockRestore();
  });
});

// ─── Stage 4: Shortlisting ────────────────────────────────────────────────────

describe('Onboarding Stage 4: Shortlisting Notification', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-03: DD-ZM receives email + WhatsApp + system after shortlisting (SRS §6.6.3)', async () => {
    await NotificationService.notify('zm-user-1', 'zm@re.com', {
      title: 'New Application Assigned — APP-2026-001',
      message: 'Rahul Verma shortlisted for Bangalore. Please evaluate.',
      channels: ['system', 'email', 'whatsapp'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { phone: '+91999', link: '/applications/app-1', requestId: 'APP-2026-001' },
    });

    expect(mockNotify).toHaveBeenCalledWith(
      'zm-user-1',
      'zm@re.com',
      expect.objectContaining({
        channels: expect.arrayContaining(['system', 'email', 'whatsapp']),
        templateCode: 'WORKFLOW_ACTION_REQUIRED',
      })
    );
  });

  it('TC-ONB-04: RBM receives same channels as DD-ZM on shortlisting', async () => {
    await NotificationService.notify('rbm-user-1', 'rbm@re.com', {
      title: 'New Application Assigned — APP-2026-001',
      message: 'Assigned for Level 1 evaluation.',
      channels: ['system', 'email', 'whatsapp'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { phone: '+91888', link: '/applications/app-1', requestId: 'APP-2026-001' },
    });

    expect(mockNotify).toHaveBeenCalledWith('rbm-user-1', 'rbm@re.com', expect.anything());
  });
});

// ─── Stage 5: Level 1 Interview Scheduling ───────────────────────────────────

describe('Onboarding Stage 5: Level 1 Interview — Google Calendar Mock', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-05: Google Calendar mock returns a meet link and calendar event ID', async () => {
    mockScheduleMeeting.mockResolvedValueOnce({
      success: true,
      meetLink: 'https://meet.google.com/mock-abcd1234',
      calendarEventId: 'cal-event-001',
    });

    const result = await ExternalMocksService.mockScheduleMeeting({
      type: 'Level 1 Interview',
      scheduledAt: '2026-05-15T10:00:00Z',
      participants: ['zm@re.com', 'rbm@re.com', 'applicant@gmail.com'],
      mode: 'Virtual',
    });

    expect(result.success).toBe(true);
    expect(result.meetLink).toContain('meet.google.com');
    expect(result.calendarEventId).toBeTruthy();
  });

  it('TC-ONB-06: After scheduling, DD-ZM and RBM are notified via email + system', async () => {
    for (const [userId, email] of [['zm-1', 'zm@re.com'], ['rbm-1', 'rbm@re.com']]) {
      await NotificationService.notify(userId, email, {
        title: 'Interview Scheduled: APP-2026-001 — Level 1',
        message: 'Level 1 Interview scheduled for 15-May-2026.',
        channels: ['system', 'email'],
        templateCode: 'INTERVIEW_SCHEDULED',
        placeholders: { link: '/applications/app-1', requestId: 'APP-2026-001' },
      });
    }

    const calls = mockNotify.mock.calls;
    expect(calls.length).toBe(2);
    calls.forEach((c: any[]) =>
      expect(c[2].channels).toEqual(expect.arrayContaining(['system', 'email']))
    );
  });
});

// ─── Stage 6: Level 1 Approval ───────────────────────────────────────────────

describe('Onboarding Stage 6: Level 1 Approved → DD-Lead + ZBH notified', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-07: DD-Lead receives Action Required notification after Level 1 approval', async () => {
    await NotificationService.notify('lead-1', 'ddlead@re.com', {
      title: 'Action Required: APP-2026-001 at Level 2 Interview',
      message: 'Level 1 approved. Please conduct Level 2 evaluation.',
      channels: ['system', 'email', 'whatsapp'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { phone: '+91777', link: '/applications/app-1', requestId: 'APP-2026-001', targetStage: 'Level 2 Interview' },
    });

    const call = mockNotify.mock.calls[0];
    expect(call[2].channels).toContain('whatsapp');
    expect(call[2].templateCode).toBe('WORKFLOW_ACTION_REQUIRED');
  });

  it('TC-ONB-08: ZBH receives same channels as DD-Lead after Level 1 approval', async () => {
    await NotificationService.notify('zbh-1', 'zbh@re.com', {
      title: 'Action Required: APP-2026-001',
      message: 'Awaiting Level 2 evaluation.',
      channels: ['system', 'email', 'whatsapp'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { phone: '+91666', link: '/applications/app-1', requestId: 'APP-2026-001', targetStage: 'Level 2 Interview' },
    });

    expect(mockNotify).toHaveBeenCalledWith('zbh-1', 'zbh@re.com', expect.anything());
  });
});

// ─── Stage 10-11: FDD → Finance ──────────────────────────────────────────────

describe('Onboarding Stage 10-11: FDD Verification → Finance Review', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-09: FDD team receives email + system notification on assignment', async () => {
    await NotificationService.notify('fdd-1', 'fddagency@external.com', {
      title: 'FDD Assignment: APP-2026-001',
      message: 'You have been assigned to perform financial due diligence.',
      channels: ['system', 'email'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { link: '/fdd/app-1', requestId: 'APP-2026-001' },
    });

    const call = mockNotify.mock.calls[0];
    // FDD is external — no WhatsApp per SRS §6.15
    expect(call[2].channels).not.toContain('whatsapp');
    expect(call[2].channels).toContain('email');
  });

  it('TC-ONB-10: Finance team receives email + system after FDD report submission', async () => {
    await NotificationService.notify('finance-1', 'finance@re.com', {
      title: 'FDD Report Submitted: APP-2026-001',
      message: 'FDD agency has submitted the financial due diligence report.',
      channels: ['system', 'email'],
      templateCode: 'WORKFLOW_ACTION_REQUIRED',
      placeholders: { link: '/finance/fdd/app-1', requestId: 'APP-2026-001' },
    });

    const call = mockNotify.mock.calls[0];
    expect(call[2].channels).toEqual(expect.arrayContaining(['system', 'email']));
  });
});

// ─── Stage 13: LOI Issued ────────────────────────────────────────────────────

describe('Onboarding Stage 13: LOI Issued — Email ONLY (SRS §1.1.2)', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-11: LOI_ISSUED notification uses email channel only (WhatsApp excluded per SRS §1.1.2)', async () => {
    // This is the critical SRS compliance test:
    // "LOI documents are shared exclusively via official email and not through WhatsApp."
    await NotificationService.notify('sys-user-1', 'applicant@gmail.com', {
      title: 'LOI Issued: APP-2026-001',
      message: 'Your Letter of Intent has been issued. Please check your email.',
      channels: ['email', 'system'], // WhatsApp intentionally absent
      templateCode: 'LOI_ISSUED',
      placeholders: { link: '/applications/app-1', applicantName: 'Rahul Verma' },
    });

    const call = mockNotify.mock.calls[0];
    expect(call[2].channels).not.toContain('whatsapp');
    expect(call[2].templateCode).toBe('LOI_ISSUED');
    expect(call[2].channels).toContain('email');
  });

  it('TC-ONB-12: LOI Issued notification includes link to applicant portal', async () => {
    await NotificationService.notify('sys-user-1', 'applicant@gmail.com', {
      title: 'LOI Issued',
      message: 'LOI Ready',
      channels: ['email', 'system'],
      templateCode: 'LOI_ISSUED',
      placeholders: { link: 'http://localhost:5173/applications/app-1', ctaLabel: 'View LOI' },
    });

    const call = mockNotify.mock.calls[0];
    expect(call[2].placeholders?.link).toContain('/applications/');
    expect(call[2].placeholders?.ctaLabel).toBe('View LOI');
  });
});

// ─── Stage 14: Dealer Code Generated ─────────────────────────────────────────

describe('Onboarding Stage 14: Dealer Code Generated — SAP Mock + Notification', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-13: SAP mock returns all 4 code types (salesCode, serviceCode, gmaCode, gearCode)', async () => {
    const result = await ExternalMocksService.mockGenerateSapCodes('app-uuid-001');
    expect(result.success).toBe(true);
    expect(result.data.salesCode).toMatch(/^SLS-\d{4}$/);
    expect(result.data.serviceCode).toMatch(/^SRV-\d{4}$/);
    expect(result.data.gmaCode).toMatch(/^GMA-\d{4}$/);
    expect(result.data.gearCode).toMatch(/^GER-\d{4}$/);
    expect(result.data.sapMasterId).toBeTruthy();
  });

  it('TC-ONB-14: Finance, Legal, DD-Admin are notified after Dealer Code is generated', async () => {
    const stakeholders = [
      { id: 'finance-1', email: 'finance@re.com', label: 'Finance' },
      { id: 'legal-1', email: 'legal@re.com', label: 'Legal' },
      { id: 'admin-1', email: 'ddadmin@re.com', label: 'DD Admin' },
    ];

    for (const s of stakeholders) {
      await NotificationService.notify(s.id, s.email, {
        title: 'Dealer Code Generated: APP-2026-001',
        message: 'Dealer Code has been generated in SAP.',
        channels: ['system', 'email'],
        templateCode: 'DEALER_CODE_READY',
        placeholders: { requestId: 'APP-2026-001' },
      });
    }

    expect(mockNotify).toHaveBeenCalledTimes(3);
    mockNotify.mock.calls.forEach((c: any[]) =>
      expect(c[2].templateCode).toBe('DEALER_CODE_READY')
    );
  });
});

// ─── Stage 16-17: EOR + Inauguration ─────────────────────────────────────────

describe('Onboarding Stage 16-17: EOR Completion + Inauguration', () => {
  beforeEach(() => jest.clearAllMocks());

  it('TC-ONB-15: DD-Head + NBH receive system alert when EOR reaches 100% (SRS §6.19.3.4)', async () => {
    for (const [userId, email] of [['head-1', 'ddhead@re.com'], ['nbh-1', 'nbh@re.com']]) {
      await NotificationService.notify(userId, email, {
        title: 'EOR Checklist Complete: APP-2026-001',
        message: 'All EOR parameters verified. Ready for Inauguration.',
        channels: ['system'],
        templateCode: 'EOR_COMPLETED',
        placeholders: { requestId: 'APP-2026-001' },
      });
    }

    expect(mockNotify).toHaveBeenCalledTimes(2);
    mockNotify.mock.calls.forEach((c: any[]) =>
      expect(c[2].channels).toEqual(['system'])
    );
  });

  it('TC-ONB-16: Dealership marked Live — applicant receives system notification', async () => {
    await NotificationService.notify('dealer-sys-user-1', 'applicant@gmail.com', {
      title: 'Congratulations! Your Dealership is Now Live.',
      message: 'APP-2026-001 has been inaugurated and is now Active.',
      channels: ['system', 'email'],
      templateCode: 'ONBOARDING_STATUS_UPDATE',
      placeholders: { status: 'Dealership Live', applicantName: 'Rahul Verma' },
    });

    const call = mockNotify.mock.calls[0];
    expect(call[2].channels).toContain('email');
    expect(call[2].placeholders?.status).toBe('Dealership Live');
  });
});
