import 'reflect-metadata';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

type SetupOptions = {
  persistence: 'postgres' | 'inmemory';
  nodeEnv: string | undefined;
  bootstrapEnabled: boolean;
  leaguesCount: number;
};

describe('BootstrapModule Postgres racing seed gating (unit)', () => {
  const originalEnv = { ...process.env };

  beforeEach(() => {
    vi.resetModules();
    process.env = { ...originalEnv };
  });

  afterEach(() => {
    process.env = originalEnv;
    vi.restoreAllMocks();
  });

  async function setup({
    persistence,
    nodeEnv,
    bootstrapEnabled,
    leaguesCount,
  }: SetupOptions): Promise<{
    seedExecute: ReturnType<typeof vi.fn>;
    ensureExecute: ReturnType<typeof vi.fn>;
    leagueCountAll: ReturnType<typeof vi.fn>;
  }> {
    process.env.NODE_ENV = nodeEnv;

    vi.doMock('../../env', async () => {
      const actual = await vi.importActual<typeof import('../../env')>('../../env');
      return {
        ...actual,
        getApiPersistence: () => persistence,
        getEnableBootstrap: () => bootstrapEnabled,
      };
    });

    const seedExecute = vi.fn(async () => undefined);
    vi.doMock('../../../../../adapters/bootstrap/SeedRacingData', () => {
      class SeedRacingData {
        execute = seedExecute;
      }
      return { SeedRacingData };
    });

    const { BootstrapModule } = await import('./BootstrapModule');

    const ensureExecute = vi.fn(async () => undefined);

    const leagueCountAll = vi.fn(async () => leaguesCount);

    const bootstrapModule = new BootstrapModule(
      { execute: ensureExecute } as any,
      { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
      {
        leagueRepository: { countAll: leagueCountAll },
      } as any,
    );

    await bootstrapModule.onModuleInit();

    return { seedExecute, ensureExecute, leagueCountAll };
  }

  it('seeds when inmemory + bootstrap enabled (existing behavior)', async () => {
    const { seedExecute, ensureExecute, leagueCountAll } = await setup({
      persistence: 'inmemory',
      nodeEnv: 'test',
      bootstrapEnabled: true,
      leaguesCount: 123,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(leagueCountAll).toHaveBeenCalledTimes(0);
    expect(seedExecute).toHaveBeenCalledTimes(1);
  });

  it('seeds when postgres + non-production + bootstrap enabled + empty', async () => {
    const { seedExecute, ensureExecute, leagueCountAll } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: true,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(leagueCountAll).toHaveBeenCalledTimes(1);
    expect(seedExecute).toHaveBeenCalledTimes(1);
  });

  it('does not seed when postgres + non-production + bootstrap enabled + not empty', async () => {
    const { seedExecute, ensureExecute, leagueCountAll } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: true,
      leaguesCount: 1,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(leagueCountAll).toHaveBeenCalledTimes(1);
    expect(seedExecute).toHaveBeenCalledTimes(0);
  });

  it('does not seed when postgres + production (even if empty)', async () => {
    const { seedExecute, ensureExecute, leagueCountAll } = await setup({
      persistence: 'postgres',
      nodeEnv: 'production',
      bootstrapEnabled: true,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(leagueCountAll).toHaveBeenCalledTimes(0);
    expect(seedExecute).toHaveBeenCalledTimes(0);
  });

  it('does not seed when postgres + non-production + bootstrap disabled (even if empty)', async () => {
    const { seedExecute, ensureExecute, leagueCountAll } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: false,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(0);
    expect(leagueCountAll).toHaveBeenCalledTimes(0);
    expect(seedExecute).toHaveBeenCalledTimes(0);
  });
});