/**
 * Platform referral settings.
 *
 * These settings moved from Referral Management (one document per company) to
 * Super Admin (one document for the whole platform). What is pinned here:
 *
 *   - there is exactly ONE settings document, under a reserved companyId
 *   - on first access it inherits an existing company's configuration, so values
 *     already in use survive the move rather than reverting to defaults
 *   - a read failure returns null so referral flows fall back to their own
 *     defaults instead of breaking
 */

const mockGetModels = jest.fn();

jest.mock('../../models', () => ({
  getModels: () => mockGetModels(),
}));

import {
  PLATFORM_REFERRAL_SETTINGS_ID,
  REFERRAL_SETTINGS_FIELDS,
  getPlatformReferralSettings,
  updatePlatformReferralSettings,
} from '../referralSettings';

/** Mongoose-style chain used by the seed lookup: findOne().sort().lean() */
function seedChain(result: any) {
  return { sort: () => ({ lean: () => Promise.resolve(result) }) };
}

function installModels(opts: { platform?: any; seed?: any } = {}) {
  const created: any[] = [];
  const findOne = jest.fn().mockImplementation((query: any) => {
    // The platform lookup is an exact-id query; the seed lookup uses $ne.
    if (query?.companyId === PLATFORM_REFERRAL_SETTINGS_ID) return Promise.resolve(opts.platform ?? null);
    return seedChain(opts.seed ?? null);
  });
  const model = {
    findOne,
    create: jest.fn().mockImplementation((doc: any) => { created.push(doc); return Promise.resolve(doc); }),
    findOneAndUpdate: jest.fn().mockImplementation((_q: any, update: any) => Promise.resolve({ ...update.$set })),
  };
  mockGetModels.mockReturnValue({ ReferralTrackingSettings: model });
  return { model, created };
}

beforeEach(() => jest.clearAllMocks());

describe('getPlatformReferralSettings', () => {
  it('returns the existing platform document without creating another', async () => {
    const platform = { companyId: PLATFORM_REFERRAL_SETTINGS_ID, referralCodePrefix: 'ACME' };
    const { model } = installModels({ platform });

    await expect(getPlatformReferralSettings()).resolves.toBe(platform);
    expect(model.create).not.toHaveBeenCalled();
  });

  it('creates the single platform document under the reserved id on first access', async () => {
    const { model, created } = installModels();

    await getPlatformReferralSettings();

    expect(model.create).toHaveBeenCalledTimes(1);
    expect(created[0].companyId).toBe(PLATFORM_REFERRAL_SETTINGS_ID);
    // Defaults match what the per-company endpoint used to create.
    expect(created[0].referralCodePrefix).toBe('REF');
    expect(created[0].maxReminders).toBe(3);
    expect(created[0].notifications.onRegistration).toBe(true);
  });

  it('inherits an existing company configuration so the move keeps its values', async () => {
    const seed = {
      companyId: 'company-1',
      referralCodePrefix: 'ACME',
      maxReminders: 9,
      expiryDays: 90,
      notifications: { onRegistration: false, onSubscriptionPurchase: true, onRewardEarned: false },
      defaultReferrerReward: { type: 'cash', value: 25, valueType: 'fixed' },
    };
    const { created } = installModels({ seed });

    await getPlatformReferralSettings();

    expect(created[0].companyId).toBe(PLATFORM_REFERRAL_SETTINGS_ID);
    expect(created[0].referralCodePrefix).toBe('ACME');
    expect(created[0].maxReminders).toBe(9);
    expect(created[0].expiryDays).toBe(90);
    expect(created[0].notifications.onRegistration).toBe(false);
    expect(created[0].defaultReferrerReward.type).toBe('cash');
  });

  it('returns null instead of throwing when the read fails', async () => {
    mockGetModels.mockReturnValue({
      ReferralTrackingSettings: { findOne: jest.fn().mockImplementation(() => { throw new Error('db down'); }) },
    });
    await expect(getPlatformReferralSettings()).resolves.toBeNull();
  });
});

describe('updatePlatformReferralSettings', () => {
  it('writes only to the reserved platform document', async () => {
    const { model } = installModels({ platform: { companyId: PLATFORM_REFERRAL_SETTINGS_ID } });

    await updatePlatformReferralSettings({ referralCodePrefix: 'NEW', maxReminders: 5 });

    expect(model.findOneAndUpdate).toHaveBeenCalledTimes(1);
    const [query, update] = model.findOneAndUpdate.mock.calls[0];
    expect(query).toEqual({ companyId: PLATFORM_REFERRAL_SETTINGS_ID });
    expect(update.$set).toEqual({ referralCodePrefix: 'NEW', maxReminders: 5 });
  });

  it('ignores fields outside the known settings list', async () => {
    const { model } = installModels({ platform: { companyId: PLATFORM_REFERRAL_SETTINGS_ID } });

    await updatePlatformReferralSettings({ referralCodePrefix: 'NEW', companyId: 'attacker', role: 'super-admin' } as any);

    const [, update] = model.findOneAndUpdate.mock.calls[0];
    expect(update.$set).toEqual({ referralCodePrefix: 'NEW' });
    expect(update.$set.companyId).toBeUndefined();
  });

  it('accepts every field the module editor exposed', async () => {
    const { model } = installModels({ platform: { companyId: PLATFORM_REFERRAL_SETTINGS_ID } });
    const body: Record<string, any> = {};
    for (const field of REFERRAL_SETTINGS_FIELDS) body[field] = 'x';

    await updatePlatformReferralSettings(body);

    const [, update] = model.findOneAndUpdate.mock.calls[0];
    expect(Object.keys(update.$set).sort()).toEqual([...REFERRAL_SETTINGS_FIELDS].sort());
  });
});
