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

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

describe('BootstrapModule demo user seed integration (unit)', () => {
  const originalEnv = { ...process.env };

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

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

  async function setup({
    persistence,
    nodeEnv,
    bootstrapEnabled,
    leaguesCount,
    forceReseed = false,
  }: SetupOptions): Promise<{
    seedRacingExecute: ReturnType<typeof vi.fn>;
    seedDemoUsersExecute: ReturnType<typeof vi.fn>;
    ensureExecute: ReturnType<typeof vi.fn>;
    leagueCountAll: ReturnType<typeof vi.fn>;
  }> {
    process.env.NODE_ENV = nodeEnv;
    process.env.GRIDPILOT_API_PERSISTENCE = persistence;
    process.env.GRIDPILOT_API_BOOTSTRAP = bootstrapEnabled ? 'true' : 'false';
    
    if (forceReseed) {
      process.env.GRIDPILOT_API_FORCE_RESEED = 'true';
    } else {
      delete process.env.GRIDPILOT_API_FORCE_RESEED;
    }

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

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

    vi.doMock('../../../../../adapters/bootstrap/SeedDemoUsers', () => {
      class SeedDemoUsers {
        execute = seedDemoUsersExecute;
      }
      return { SeedDemoUsers };
    });

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

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

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

    await bootstrapModule.onModuleInit();

    return { seedRacingExecute, seedDemoUsersExecute, ensureExecute, leagueCountAll };
  }

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

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(1);
  });

  it('seeds demo users when postgres + development + bootstrap enabled', async () => {
    const { seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: true,
      leaguesCount: 1,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(1);
  });

  it('does not seed demo users when postgres + production', async () => {
    const { seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'postgres',
      nodeEnv: 'production',
      bootstrapEnabled: true,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(0);
  });

  it('does not seed demo users when bootstrap disabled', async () => {
    const { seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'inmemory',
      nodeEnv: 'test',
      bootstrapEnabled: false,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(0);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(0);
  });

  it('seeds demo users with force reseed enabled', async () => {
    const { seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: true,
      leaguesCount: 1,
      forceReseed: true,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(1);
  });

  it('seeds both racing data and demo users when both are needed', async () => {
    const { seedRacingExecute, seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'inmemory',
      nodeEnv: 'test',
      bootstrapEnabled: true,
      leaguesCount: 0,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedRacingExecute).toHaveBeenCalledTimes(1);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(1);
  });

  it('skips racing seed but seeds demo users when database has data', async () => {
    const { seedRacingExecute, seedDemoUsersExecute, ensureExecute } = await setup({
      persistence: 'postgres',
      nodeEnv: 'development',
      bootstrapEnabled: true,
      leaguesCount: 120,
    });

    expect(ensureExecute).toHaveBeenCalledTimes(1);
    expect(seedRacingExecute).toHaveBeenCalledTimes(0);
    expect(seedDemoUsersExecute).toHaveBeenCalledTimes(1);
  });
});