import 'reflect-metadata';
import { DataSource, DataSourceOptions } from 'typeorm';
import { Player } from '../src/players/entities/player.entity';
import { Team } from '../src/teams/entities/team.entity';
import { Transaction } from '../src/transactions/entitites/transaction.entity';
import { TransactionTypeEnum } from '../src/transactions/transaction-type.enum';
import { DEACTIVATION_ADJUSTMENT_NOTE_PREFIX } from '../src/teams/team-members.service';

// Eigene DataSource statt der geteilten `AppDataSource`: deren `migrations`-Glob
// (`src/database/migrations/**/*`) matcht auch `.spec.ts`-Dateien in diesem Ordner
// (z.B. `AddPlayerLookupIndexes.spec.ts`) und TypeORM requiret sie beim
// Metadaten-Aufbau -- das registriert `describe`/`it` NACH Testlauf-Start und lässt
// Jest mit "Cannot add a test after tests have started running" abbrechen. Diese
// Ausgleichsbuchung braucht keine Migrationen, nur Entities -- migrations: [] umgeht das.
describe('TeamMembers rollback atomicity (real MySQL)', () => {
  let dataSource: DataSource;
  let team: Team;
  let player: Player;
  const marker = `rollback-test-${Date.now()}`;

  beforeAll(async () => {
    dataSource = new DataSource({
      type: process.env.DATABASE_TYPE,
      url: process.env.DATABASE_URL,
      host: process.env.DATABASE_HOST,
      port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
      username: process.env.DATABASE_USERNAME,
      password: process.env.DATABASE_PASSWORD,
      database: process.env.DATABASE_NAME,
      synchronize: false,
      entities: [__dirname + '/../src/**/*.entity{.ts,.js}'],
    } as DataSourceOptions);
    await dataSource.initialize();
    team = await dataSource.getRepository(Team).save({
      name: marker,
      alias: marker,
    } as Team);
    player = await dataSource.getRepository(Player).save({
      firstName: 'Rollback',
      lastName: 'Test',
      team,
      teamRole: { id: 4 }, // treasurer
      balance: 42,
      active: true,
    } as unknown as Player);
  });

  afterAll(async () => {
    await dataSource
      .getRepository(Transaction)
      .delete({ player: { id: player.id } });
    await dataSource.getRepository(Player).delete({ id: player.id });
    await dataSource.getRepository(Team).delete({ id: team.id });
    await dataSource.destroy();
  });

  it('rolls back BOTH the inserted adjustment transaction AND the explicit balance write when a later error is thrown in the same manager transaction', async () => {
    await expect(
      dataSource.transaction(async (manager) => {
        await manager.getRepository(Transaction).insert({
          amount: -42,
          date: new Date().toISOString(),
          note: `${DEACTIVATION_ADJUSTMENT_NOTE_PREFIX} #${player.id}`,
          player: { id: player.id } as Player,
          type: { id: TransactionTypeEnum.credit },
        });
        await manager
          .getRepository(Player)
          .save({ ...player, balance: 0, active: false });
        throw new Error('forced failure after adjustment transaction would have been created');
      }),
    ).rejects.toThrow('forced failure');

    const persistedTransaction = await dataSource.getRepository(Transaction).findOne({
      where: { player: { id: player.id } },
    });
    expect(persistedTransaction).toBeNull();

    const reloadedPlayer = await dataSource.getRepository(Player).findOne({
      where: { id: player.id },
    });
    expect(reloadedPlayer?.active).toBe(true);
    expect(Number(reloadedPlayer?.balance)).toBe(42);
  });
});
