import 'reflect-metadata';

import { DataSource } from 'typeorm';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { MemberPaymentStatus } from '@core/payments/domain/entities/MemberPayment';
import { MembershipFeeType } from '@core/payments/domain/entities/MembershipFee';
import { PayerType, PaymentStatus, PaymentType } from '@core/payments/domain/entities/Payment';
import { PrizeType } from '@core/payments/domain/entities/Prize';
import { TransactionType } from '@core/payments/domain/entities/Wallet';

import { PaymentsMemberPaymentOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsMemberPaymentOrmEntity';
import { PaymentsMembershipFeeOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsMembershipFeeOrmEntity';
import { PaymentsPaymentOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsPaymentOrmEntity';
import { PaymentsPrizeOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsPrizeOrmEntity';
import { PaymentsTransactionOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsTransactionOrmEntity';
import { PaymentsWalletOrmEntity } from '@adapters/payments/persistence/typeorm/entities/PaymentsWalletOrmEntity';

import { PaymentsMemberPaymentOrmMapper } from '@adapters/payments/persistence/typeorm/mappers/PaymentsMemberPaymentOrmMapper';
import { PaymentsMembershipFeeOrmMapper } from '@adapters/payments/persistence/typeorm/mappers/PaymentsMembershipFeeOrmMapper';
import { PaymentsPaymentOrmMapper } from '@adapters/payments/persistence/typeorm/mappers/PaymentsPaymentOrmMapper';
import { PaymentsPrizeOrmMapper } from '@adapters/payments/persistence/typeorm/mappers/PaymentsPrizeOrmMapper';
import { PaymentsWalletOrmMapper } from '@adapters/payments/persistence/typeorm/mappers/PaymentsWalletOrmMapper';

import { TypeOrmMemberPaymentRepository, TypeOrmMembershipFeeRepository } from '@adapters/payments/persistence/typeorm/repositories/TypeOrmMembershipFeeRepository';
import { TypeOrmPaymentRepository } from '@adapters/payments/persistence/typeorm/repositories/TypeOrmPaymentRepository';
import { TypeOrmPrizeRepository } from '@adapters/payments/persistence/typeorm/repositories/TypeOrmPrizeRepository';
import { TypeOrmTransactionRepository, TypeOrmWalletRepository } from '@adapters/payments/persistence/typeorm/repositories/TypeOrmWalletRepository';

const databaseUrl = process.env.DATABASE_URL;
const describeIfDatabase = databaseUrl ? describe : describe.skip;

describeIfDatabase('TypeORM Payments repositories (postgres slice)', () => {
  let dataSource: DataSource;

  beforeAll(async () => {
    if (!databaseUrl) {
      throw new Error('DATABASE_URL is required to run postgres integration tests');
    }

    dataSource = new DataSource({
      type: 'postgres',
      url: databaseUrl,
      entities: [
        PaymentsWalletOrmEntity,
        PaymentsTransactionOrmEntity,
        PaymentsPaymentOrmEntity,
        PaymentsPrizeOrmEntity,
        PaymentsMembershipFeeOrmEntity,
        PaymentsMemberPaymentOrmEntity,
      ],
      synchronize: true,
    });

    await dataSource.initialize();
  });

  afterAll(async () => {
    if (dataSource?.isInitialized) {
      await dataSource.destroy();
    }
  });

  it('supports: wallet+tx+payment+prize+membership fee+member payment', async () => {
    const walletMapper = new PaymentsWalletOrmMapper();
    const walletRepo = new TypeOrmWalletRepository(dataSource, walletMapper);
    const txRepo = new TypeOrmTransactionRepository(dataSource, walletMapper);

    const paymentRepo = new TypeOrmPaymentRepository(dataSource, new PaymentsPaymentOrmMapper());
    const prizeRepo = new TypeOrmPrizeRepository(dataSource, new PaymentsPrizeOrmMapper());
    const feeRepo = new TypeOrmMembershipFeeRepository(dataSource, new PaymentsMembershipFeeOrmMapper());
    const memberPaymentRepo = new TypeOrmMemberPaymentRepository(dataSource, new PaymentsMemberPaymentOrmMapper());

    const now = Date.now();
    const leagueId = `league-it-${now}`;

    const wallet = {
      id: `wallet-it-${now}`,
      leagueId,
      balance: 0,
      totalRevenue: 0,
      totalPlatformFees: 0,
      totalWithdrawn: 0,
      currency: 'USD',
      createdAt: new Date(),
    };

    await walletRepo.create(wallet);

    const tx = {
      id: `txn-it-${now}`,
      walletId: wallet.id,
      type: TransactionType.DEPOSIT,
      amount: 25,
      description: 'Integration deposit',
      createdAt: new Date(),
    };

    await txRepo.create(tx);

    const loadedWallet = await walletRepo.findByLeagueId(wallet.leagueId);
    expect(loadedWallet).not.toBeNull();
    expect(loadedWallet!.leagueId).toBe(wallet.leagueId);

    const loadedTxs = await txRepo.findByWalletId(wallet.id);
    expect(loadedTxs.map(t => t.id)).toContain(tx.id);

    const payment = {
      id: `payment-it-${now}`,
      type: PaymentType.MEMBERSHIP_FEE,
      amount: 100,
      platformFee: 5,
      netAmount: 95,
      payerId: `driver-it-${now}`,
      payerType: PayerType.DRIVER,
      leagueId,
      status: PaymentStatus.PENDING,
      createdAt: new Date(),
    };

    await paymentRepo.create(payment);

    const loadedPayments = await paymentRepo.findByLeagueId(leagueId);
    expect(loadedPayments.map(p => p.id)).toContain(payment.id);

    const prize = {
      id: `prize-it-${now}`,
      leagueId,
      seasonId: `season-it-${now}`,
      position: 1,
      name: 'Winner',
      amount: 250,
      type: PrizeType.CASH,
      awarded: false,
      createdAt: new Date(),
    };

    await prizeRepo.create(prize);

    const loadedPrizes = await prizeRepo.findByLeagueIdAndSeasonId(leagueId, prize.seasonId);
    expect(loadedPrizes.map(p => p.id)).toContain(prize.id);

    const fee = {
      id: `fee-it-${now}`,
      leagueId,
      type: MembershipFeeType.SEASON,
      amount: 100,
      enabled: true,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    await feeRepo.create(fee);

    const loadedFee = await feeRepo.findByLeagueId(leagueId);
    expect(loadedFee?.id).toBe(fee.id);

    const memberPayment = {
      id: `member-payment-it-${now}`,
      feeId: fee.id,
      driverId: payment.payerId,
      amount: 100,
      platformFee: 5,
      netAmount: 95,
      status: MemberPaymentStatus.PAID,
      dueDate: new Date(),
      paidAt: new Date(),
    };

    await memberPaymentRepo.create(memberPayment);

    const loadedMemberPayments = await memberPaymentRepo.findByLeagueIdAndDriverId(
      leagueId,
      memberPayment.driverId,
      feeRepo,
    );
    expect(loadedMemberPayments.map(mp => mp.id)).toContain(memberPayment.id);
  });
});
