/**
 * @file external-integrations.test.ts
 * @description Contract/mock tests for all external integrations.
 * These tests validate the SHAPE and BEHAVIOUR of each mock so that
 * when real APIs are wired, only the mock needs to be swapped.
 *
 * Integrations covered:
 *  1. SAP OData — Dealer Code generation (mockGenerateSapCodes, mockSyncDealerStatusToSap)
 *  2. Google Calendar — Interview invite scheduling (mockScheduleMeeting)
 *  3. WhatsApp — Notification delivery (mockSendWhatsApp)
 *  4. Gemini AI — Panel evaluation summary (mockGenerateAiSummary)
 *
 * SRS Coverage:
 *  §6.17.3.1 — SAP OData API for Sales/Service/GMA/Gear codes
 *  §6.9.2    — Google Calendar invites for all participants
 *  §1.1.1    — WhatsApp as supported notification channel
 *  §6.10.4   — AI-assisted recommendation via Gemini API
 */

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

// ─── 1. SAP Dealer Code Generation ───────────────────────────────────────────

describe('SAP Mock — Dealer Code Generation (SRS §6.17.3.1)', () => {
  it('TC-SAP-01: returns success=true', async () => {
    const result = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(result.success).toBe(true);
  });

  it('TC-SAP-02: returns salesCode in SLS-XXXX format', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data.salesCode).toMatch(/^SLS-\d{4}$/);
  });

  it('TC-SAP-03: returns serviceCode in SRV-XXXX format', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data.serviceCode).toMatch(/^SRV-\d{4}$/);
  });

  it('TC-SAP-04: returns gmaCode in GMA-XXXX format (Genuine Motorcycle Accessories)', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data.gmaCode).toMatch(/^GMA-\d{4}$/);
  });

  it('TC-SAP-05: returns gearCode in GER-XXXX format', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data.gearCode).toMatch(/^GER-\d{4}$/);
  });

  it('TC-SAP-06: returns a non-empty sapMasterId', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data.sapMasterId).toBeTruthy();
    expect(typeof data.sapMasterId).toBe('string');
  });

  it('TC-SAP-07: each call generates unique codes (no collisions)', async () => {
    const r1 = await ExternalMocksService.mockGenerateSapCodes('app-001');
    const r2 = await ExternalMocksService.mockGenerateSapCodes('app-002');
    // Codes are random; while collision is statistically possible, sapMasterIds must differ
    expect(r1.data.sapMasterId).not.toBe(r2.data.sapMasterId);
  });

  it('TC-SAP-08: returns all 4 code types in a single call (no missing fields)', async () => {
    const { data } = await ExternalMocksService.mockGenerateSapCodes('app-001');
    expect(data).toHaveProperty('salesCode');
    expect(data).toHaveProperty('serviceCode');
    expect(data).toHaveProperty('gmaCode');
    expect(data).toHaveProperty('gearCode');
    expect(data).toHaveProperty('sapMasterId');
  });
});

// ─── SAP Status Sync ─────────────────────────────────────────────────────────

describe('SAP Mock — Dealer Status Synchronization', () => {
  it('TC-SAP-09: mockSyncDealerStatusToSap returns success=true', async () => {
    const result = await ExternalMocksService.mockSyncDealerStatusToSap('SLS-1234', 'Active');
    expect(result.success).toBe(true);
  });

  it('TC-SAP-10: sync result includes a sapTransactionId and ISO timestamp', async () => {
    const result = await ExternalMocksService.mockSyncDealerStatusToSap('SLS-1234', 'Terminated');
    expect(result.sapTransactionId).toMatch(/^SAP-TX-/);
    expect(new Date(result.timestamp).toISOString()).toBe(result.timestamp);
  });
});

// ─── SAP Financial Dues ───────────────────────────────────────────────────────

describe('SAP Mock — Financial Dues (F&F Context)', () => {
  it('TC-SAP-11: mockGetFinancialDuesFromSap returns financial data for a dealer', async () => {
    const result = await ExternalMocksService.mockGetFinancialDuesFromSap('SLS-5678');
    expect(result.success).toBe(true);
    expect(result.data).toHaveProperty('outstandingInvoices');
    expect(result.data).toHaveProperty('securityDeposit');
    expect(result.data).toHaveProperty('creditLimit');
    expect(result.data).toHaveProperty('pendingClaims');
  });

  it('TC-SAP-12: returned financial amounts are positive numbers', async () => {
    const { data } = await ExternalMocksService.mockGetFinancialDuesFromSap('SLS-5678');
    expect(data.securityDeposit).toBeGreaterThan(0);
    expect(data.creditLimit).toBeGreaterThan(0);
  });
});

// ─── 2. Google Calendar Mock ──────────────────────────────────────────────────

describe('Google Calendar Mock — Interview Scheduling (SRS §6.9.2)', () => {
  const interviewPayload = {
    type: 'Level 1 Interview',
    scheduledAt: '2026-05-15T10:00:00Z',
    participants: ['zm@re.com', 'rbm@re.com', 'applicant@gmail.com'],
    mode: 'Virtual',
    applicationId: 'app-uuid-001',
  };

  it('TC-CAL-01: mockScheduleMeeting returns success=true', async () => {
    const result = await ExternalMocksService.mockScheduleMeeting(interviewPayload);
    expect(result.success).toBe(true);
  });

  it('TC-CAL-02: returns a Google Meet link URL', async () => {
    const result = await ExternalMocksService.mockScheduleMeeting(interviewPayload);
    expect(result.meetLink).toContain('meet.google.com');
  });

  it('TC-CAL-03: returns a non-empty calendarEventId (UUID format)', async () => {
    const result = await ExternalMocksService.mockScheduleMeeting(interviewPayload);
    expect(result.calendarEventId).toMatch(
      /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
    );
  });

  it('TC-CAL-04: each scheduling call returns a unique meetLink (no duplicate links)', async () => {
    const r1 = await ExternalMocksService.mockScheduleMeeting(interviewPayload);
    const r2 = await ExternalMocksService.mockScheduleMeeting({ ...interviewPayload, type: 'Level 2 Interview' });
    expect(r1.meetLink).not.toBe(r2.meetLink);
  });

  it('TC-CAL-05: mock works for Level 1, Level 2, and Level 3 interview types', async () => {
    for (const type of ['Level 1 Interview', 'Level 2 Interview', 'Level 3 Interview']) {
      const result = await ExternalMocksService.mockScheduleMeeting({ ...interviewPayload, type });
      expect(result.success).toBe(true);
    }
  });

  it('TC-CAL-06: mock works for both Virtual and Physical interview modes', async () => {
    const virtualResult = await ExternalMocksService.mockScheduleMeeting({ ...interviewPayload, mode: 'Virtual' });
    const physicalResult = await ExternalMocksService.mockScheduleMeeting({ ...interviewPayload, mode: 'Physical' });
    expect(virtualResult.success).toBe(true);
    expect(physicalResult.success).toBe(true);
  });
});

// ─── 3. WhatsApp Mock ─────────────────────────────────────────────────────────

describe('WhatsApp Mock — Notification Delivery (SRS §1.1.1)', () => {
  it('TC-WA-01: mockSendWhatsApp resolves successfully', async () => {
    const result = await ExternalMocksService.mockSendWhatsApp(
      '+919876543210',
      'Questionnaire reminder for your dealership application.'
    );
    expect(result.success).toBe(true);
  });

  it('TC-WA-02: returned messageId starts with WA- prefix', async () => {
    const result = await ExternalMocksService.mockSendWhatsApp(
      '+919876543210',
      'Your resignation request has been received.'
    );
    expect(result.messageId).toMatch(/^WA-/);
  });

  it('TC-WA-03: LOI-related messages must NOT be sent via WhatsApp (SRS §1.1.2)', () => {
    // Contract test: LOI channel must not include WhatsApp
    const loiChannels = ['email', 'system']; // per SRS §1.1.2
    expect(loiChannels).not.toContain('whatsapp');
  });

  it('TC-WA-04: questionnaire reminders MUST include WhatsApp channel (SRS §1.1.1)', () => {
    const questionnaireChannels = ['email', 'whatsapp']; // per SRS §1.1.1
    expect(questionnaireChannels).toContain('whatsapp');
  });

  it('TC-WA-05: resignation submission acknowledgement includes WhatsApp channel (SRS §1.1.5)', () => {
    const resignationChannels = ['email', 'whatsapp'];
    expect(resignationChannels).toContain('whatsapp');
  });

  it('TC-WA-06: each WhatsApp call generates a unique messageId', async () => {
    const r1 = await ExternalMocksService.mockSendWhatsApp('+91111', 'Msg 1');
    const r2 = await ExternalMocksService.mockSendWhatsApp('+91222', 'Msg 2');
    expect(r1.messageId).not.toBe(r2.messageId);
  });
});

// ─── 4. Gemini AI Mock ────────────────────────────────────────────────────────

describe('Gemini AI Mock — Panel Evaluation Summary (SRS §6.10.4)', () => {
  const allApprovedFeedback = [
    { recommendation: 'Approve', score: 85 },
    { recommendation: 'Approve', score: 90 },
    { recommendation: 'Approve', score: 88 },
  ];

  const mixedFeedback = [
    { recommendation: 'Approve', score: 80 },
    { recommendation: 'Approve', score: 75 },
    { recommendation: 'Reject', score: 40 },
  ];

  const allRejectFeedback = [
    { recommendation: 'Reject', score: 30 },
    { recommendation: 'Reject', score: 25 },
  ];

  it('TC-AI-01: mockGenerateAiSummary returns success=true', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', allApprovedFeedback);
    expect(result.success).toBe(true);
  });

  it('TC-AI-02: returns a non-empty summary string', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', allApprovedFeedback);
    expect(typeof result.summary).toBe('string');
    expect(result.summary.length).toBeGreaterThan(10);
  });

  it('TC-AI-03: unanimous approval panel produces positive consensus summary', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', allApprovedFeedback);
    // Unanimous approval → strong consensus message
    expect(result.summary).toMatch(/strong consensus|strong candidate|exceptional/i);
  });

  it('TC-AI-04: majority approval produces cautiously positive summary', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', mixedFeedback);
    // Majority but not all → cautious message
    expect(result.summary).toMatch(/majority|recommend approval|monitored/i);
  });

  it('TC-AI-05: rejection-leaning panel produces concern-focused summary', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', allRejectFeedback);
    expect(result.summary).toMatch(/divided|rejection|concern/i);
  });

  it('TC-AI-06: empty feedback list produces a valid (non-crashing) summary', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', []);
    expect(result.success).toBe(true);
    expect(result.summary).toBeDefined();
  });

  it('TC-AI-07: summary is presentable to NBH — 2 to 3 sentences', async () => {
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', allApprovedFeedback);
    // SRS §6.10.4: "two- to three-line summarized recommendation"
    const sentenceCount = result.summary.split(/[.!?]/).filter(Boolean).length;
    expect(sentenceCount).toBeGreaterThanOrEqual(1);
    expect(sentenceCount).toBeLessThanOrEqual(5);
  });

  it('TC-AI-08: mixed feedback with exactly half approvals falls into majority branch', async () => {
    const halfHalf = [
      { recommendation: 'Approve', score: 75 },
      { recommendation: 'Reject', score: 40 },
    ];
    // 1 approve out of 2 = not > total/2, so should fall into rejection branch
    const result = await ExternalMocksService.mockGenerateAiSummary('app-001', halfHalf);
    expect(result.success).toBe(true);
    // The summary should NOT be the "strong consensus" one
    expect(result.summary).not.toMatch(/strong consensus|exceptional/i);
  });
});
