/**
 * aiGenerationNotifications — who actually receives the completion email
 *
 * Two rules are pinned here.
 *
 * 1. The super-admin ops toggle governs the admin/ops copy ONLY. It ships
 *    disabled, and it used to be re-checked after the triggering user's address
 *    had been resolved, which threw that address away — the reason a completed
 *    bulk SEO run still produced no mail.
 * 2. Whether the person who ran the generation is emailed is decided by THEIR
 *    Notification Categories preference (AI generation → Email) and nothing
 *    else. It used to be decided by a module allow-list, which is why the same
 *    feature worked in some modules and not others; every module is treated
 *    identically now.
 */

// ---- Mocks -------------------------------------------------------------
const mockSendSystemEmail = jest.fn().mockResolvedValue({ success: true });

/** Whatever the super-admin panelSettings should look like for a given test. */
let superAdminSettings: any = null;
/** The user record `getTriggeringUserEmail` resolves. */
let triggeringUser: any = { email: 'runner@example.com' };
/** The runner's stored notification preference. Null = never opened the page. */
let userPreference: any = { channelsByCategory: { ai: { inApp: true, email: true } }, muteAll: false };

jest.mock('../email/transactionalMailer', () => ({
  sendSystemEmail: (...args: any[]) => mockSendSystemEmail(...args),
}));

jest.mock('../../models', () => ({
  getModels: () => ({
    User: {
      findOne: () => ({ lean: async () => superAdminSettings }),
      findById: () => ({ select: () => ({ lean: async () => triggeringUser }) }),
    },
    NotificationPreference: {
      findOne: async () => userPreference,
    },
  }),
}));

import { notifyAiGenerationCompleted } from '../aiGenerationNotifications';

/** The notifier is fire-and-forget, so let its async body run to completion. */
const flush = () => new Promise((resolve) => setImmediate(resolve));

const baseParams = {
  moduleSource: 'seo',
  moduleId: 'seo',
  companyId: 'company-1',
  userId: 'user-1',
  autoFillData: { name: '12 SEO records' },
  completedAt: 1_700_000_000_000,
};

describe('notifyAiGenerationCompleted', () => {
  beforeEach(() => {
    mockSendSystemEmail.mockClear();
    superAdminSettings = null;
    triggeringUser = { email: 'runner@example.com' };
    userPreference = { channelsByCategory: { ai: { inApp: true, email: true } }, muteAll: false };
    delete process.env.AI_GENERATION_NOTIFY_EMAIL;
  });

  it('emails the user who ran a bulk SEO generation even with the ops alert off', async () => {
    // The shipped default: no super-admin config at all.
    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:1' });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
    const [{ to, subject }] = mockSendSystemEmail.mock.calls[0];
    expect(to).toBe('runner@example.com');
    expect(subject).toContain('AI Generation Completed');
  });

  it('sends exactly one email per bulk run', async () => {
    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:2' });
    await flush();
    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:2' });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
  });

  it('also emails the configured admin recipients when the ops alert is on', async () => {
    superAdminSettings = {
      panelSettings: { aiGenerationNotifications: { enabled: true, recipients: ['ops@example.com'] } },
    };

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:3' });
    await flush();

    const addressed = mockSendSystemEmail.mock.calls.map((c: any[]) => c[0].to);
    expect(addressed).toEqual(['runner@example.com', 'ops@example.com']);
  });

  it('de-duplicates a user who is also on the admin recipient list', async () => {
    superAdminSettings = {
      panelSettings: { aiGenerationNotifications: { enabled: true, recipients: ['RUNNER@example.com'] } },
    };

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:4' });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
  });

  it('emails the runner for a module that was never on the old allow-list', async () => {
  // The sample module here was 'presentation-generator' until that module was
  // deliberately added to USER_EMAIL_MODULE_PREFIXES (Presentations & Pitches
  // was one of six reported as never emailing on completion). The assertion is
  // unchanged — it just needs a key that is genuinely still off the list.
  // 'sales-script' has no entry and no prefix that matches it.
  it('stays silent for a module that is not on the user allow-list while the ops alert is off', async () => {
    notifyAiGenerationCompleted({
      ...baseParams,
      moduleSource: 'sales-script',
      moduleId: 'sales-script',
      jobId: 'sales-script:company-1:1',
    });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
    expect(mockSendSystemEmail.mock.calls[0][0].to).toBe('runner@example.com');
  });

  it('sends nothing to the runner when they have Email switched off for AI generation', async () => {
    userPreference = { channelsByCategory: { ai: { inApp: true, email: false } }, muteAll: false };

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:pref-off' });
    await flush();

    expect(mockSendSystemEmail).not.toHaveBeenCalled();
  });

  it('sends nothing to the runner who has never opened the preferences page (email is opt-in)', async () => {
    userPreference = null;

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:pref-default' });
    await flush();

    expect(mockSendSystemEmail).not.toHaveBeenCalled();
  });

  it('sends nothing to the runner who has paused all notifications', async () => {
    userPreference = { channelsByCategory: { ai: { inApp: true, email: true } }, muteAll: true };

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:muted' });
    await flush();

    expect(mockSendSystemEmail).not.toHaveBeenCalled();
  });

  it('still sends the ops copy when the runner has email off but the ops alert is on', async () => {
    userPreference = { channelsByCategory: { ai: { inApp: true, email: false } }, muteAll: false };
    superAdminSettings = {
      panelSettings: { aiGenerationNotifications: { enabled: true, recipients: ['ops@example.com'] } },
    };

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:ops-only' });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
    expect(mockSendSystemEmail.mock.calls[0][0].to).toBe('ops@example.com');
  });

  it('sends nothing when the triggering user cannot be resolved and no admin list is configured', async () => {
    triggeringUser = null;

    notifyAiGenerationCompleted({ ...baseParams, jobId: 'seo-generate:bulk:company-1:all:5' });
    await flush();

    expect(mockSendSystemEmail).not.toHaveBeenCalled();
  });

  // A cross-section of modules, including several that were never on the old
  // allow-list ('blog', 'book', 'ads'): with the ops alert off, each still
  // emails the person who ran it, purely on their own preference.
  it.each([
    ['case-studies', 'Case Studies'],
    ['testimonials', 'Testimonials'],
    ['faq-bank', 'FAQ Bank'],
    ['website-planner', 'Website Planner'],
    ['website-generator', 'Website Generator'],
    ['newsletter', 'Newsletters'],
    ['social-media-os', 'Social Media'],
    ['blog', 'Blog Content'],
    ['book', 'Books'],
    ['pr', 'PR'],
    ['sales-script', 'Sales Scripts'],
    ['video-content', 'Video Content'],
    ['geo-optimization', 'AI Discoverability (GEO)'],
  ])('emails the user who ran a %s generation with the ops alert off', async (moduleId, label) => {
    notifyAiGenerationCompleted({
      ...baseParams,
      moduleSource: moduleId,
      moduleId,
      autoFillData: undefined,
      jobId: `${moduleId}:company-1:1`,
    });
    await flush();

    expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
    const [{ to, subject }] = mockSendSystemEmail.mock.calls[0];
    expect(to).toBe('runner@example.com');
    expect(subject).toContain(label);
  });

  it.each(['case-studies', 'testimonials', 'faq-bank', 'website-generator', 'newsletter', 'social-media-os'])(
    'sends exactly one email per completed %s job',
    async (moduleId) => {
      const params = { ...baseParams, moduleSource: moduleId, moduleId, jobId: `${moduleId}:company-1:2` };
      notifyAiGenerationCompleted(params);
      await flush();
      notifyAiGenerationCompleted(params);
      await flush();

      expect(mockSendSystemEmail).toHaveBeenCalledTimes(1);
    }
  );
});
