/**
 * Smoke tests for OAuthBootstrapSeeder.
 *
 * SYNTHESIZED — establishes first coverage for the OAuth bootstrap seeder.
 * Validates gate behaviour and 2nd-boot idempotency only; the full OAuth
 * seeding path is trivial (create app → create client → create pivot) and
 * is covered indirectly by the full integration boot test (live proof).
 */

import { EventEmitter2 } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';

import {
  OAuthCreateApplicationHandler,
  OAuthFindApplicationService,
  OAuthUpdateApplicationByIdService,
} from '@app/o-auth/application';
import {
  OAuthCreateApplicationClientService,
  OAuthFindApplicationClientByIdService,
} from '@app/o-auth/application-client';
import { OAuthCreateClientHandler, OAuthFindClientService } from '@app/o-auth/client';

import {
  BOOTSTRAP_OAUTH_APP_CODE,
  BOOTSTRAP_OAUTH_APP_ID,
  BOOTSTRAP_OAUTH_APP_SECRET,
  BOOTSTRAP_OAUTH_CLIENT_ID,
  OAuthBootstrapSeeder,
} from './o-auth-bootstrap.seeder';

describe('OAuthBootstrapSeeder', () => {
  let seeder: OAuthBootstrapSeeder;

  const findApp = { main: jest.fn() };
  const createApp = { main: jest.fn() };
  const updateApp = { main: jest.fn() };
  const findClient = { main: jest.fn() };
  const createClient = { main: jest.fn() };
  const findAppClient = { main: jest.fn() };
  const createAppClient = { main: jest.fn() };
  const eventEmitter = { emit: jest.fn() };

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        OAuthBootstrapSeeder,
        { provide: OAuthFindApplicationService, useValue: findApp },
        { provide: OAuthCreateApplicationHandler, useValue: createApp },
        { provide: OAuthUpdateApplicationByIdService, useValue: updateApp },
        { provide: OAuthFindClientService, useValue: findClient },
        { provide: OAuthCreateClientHandler, useValue: createClient },
        { provide: OAuthFindApplicationClientByIdService, useValue: findAppClient },
        { provide: OAuthCreateApplicationClientService, useValue: createAppClient },
        { provide: EventEmitter2, useValue: eventEmitter },
      ],
    }).compile();

    seeder = module.get<OAuthBootstrapSeeder>(OAuthBootstrapSeeder);
  });

  beforeEach(() => {
    jest.clearAllMocks();
    process.env.NODE_ENV = 'test';
    delete process.env.BOOTSTRAP_SEED;
  });

  afterAll(() => {
    delete process.env.BOOTSTRAP_SEED;
  });

  it('does nothing when NODE_ENV=production and BOOTSTRAP_SEED is not set', async () => {
    process.env.NODE_ENV = 'production';

    await seeder.onApplicationBootstrap();

    expect(findApp.main).not.toHaveBeenCalled();
    expect(createApp.main).not.toHaveBeenCalled();
  });

  it('dispatches NO create calls when all OAuth entities already exist (2nd-boot idempotency)', async () => {
    // secret must match BOOTSTRAP_OAUTH_APP_SECRET to avoid triggering the reconcile-secret path
    findApp.main.mockResolvedValue({
      code: BOOTSTRAP_OAUTH_APP_CODE,
      secret: BOOTSTRAP_OAUTH_APP_SECRET,
    });
    findClient.main.mockResolvedValue({ id: BOOTSTRAP_OAUTH_CLIENT_ID });
    findAppClient.main.mockResolvedValue({
      applicationId: BOOTSTRAP_OAUTH_APP_ID,
      clientId: BOOTSTRAP_OAUTH_CLIENT_ID,
    });

    await seeder.onApplicationBootstrap();

    expect(createApp.main).not.toHaveBeenCalled();
    expect(updateApp.main).not.toHaveBeenCalled();
    expect(createClient.main).not.toHaveBeenCalled();
    expect(createAppClient.main).not.toHaveBeenCalled();
  });

  it('creates all three entities on a fresh boot', async () => {
    findApp.main.mockResolvedValue(null);
    findClient.main.mockResolvedValue(null);
    findAppClient.main.mockResolvedValue(null);
    createApp.main.mockResolvedValue(undefined);
    createClient.main.mockResolvedValue(undefined);
    createAppClient.main.mockResolvedValue(undefined);

    await seeder.onApplicationBootstrap();

    expect(createApp.main).toHaveBeenCalledTimes(1);
    expect(createClient.main).toHaveBeenCalledTimes(1);
    expect(createAppClient.main).toHaveBeenCalledTimes(1);
  });

  it('updates the application secret when it drifts from the frontend-aligned value', async () => {
    // Application exists but with a stale secret
    findApp.main.mockResolvedValue({
      id: BOOTSTRAP_OAUTH_APP_ID,
      code: BOOTSTRAP_OAUTH_APP_CODE,
      secret: 'stale-secret',
    });
    findClient.main.mockResolvedValue({ id: BOOTSTRAP_OAUTH_CLIENT_ID });
    findAppClient.main.mockResolvedValue({
      applicationId: BOOTSTRAP_OAUTH_APP_ID,
      clientId: BOOTSTRAP_OAUTH_CLIENT_ID,
    });
    updateApp.main.mockResolvedValue(undefined);

    await seeder.onApplicationBootstrap();

    expect(createApp.main).not.toHaveBeenCalled();
    expect(updateApp.main).toHaveBeenCalledTimes(1);
    expect(updateApp.main).toHaveBeenCalledWith(
      expect.objectContaining({ id: BOOTSTRAP_OAUTH_APP_ID, secret: BOOTSTRAP_OAUTH_APP_SECRET }),
    );
  });
});
