import { describe, expect, it } from 'vitest';
import { createRacingSeed } from '@adapters/bootstrap/racing/RacingSeed';

describe('Racing seed (bootstrap)', () => {
  it('creates a large, internally consistent seed', () => {
    const seed = createRacingSeed();

    expect(seed.drivers.length).toBeGreaterThanOrEqual(50);
    expect(seed.leagues.length).toBeGreaterThanOrEqual(10);
    expect(seed.races.length).toBeGreaterThan(0);

    const driverIds = new Set(seed.drivers.map((d) => d.id.toString()));
    const leagueIds = new Set(seed.leagues.map((l) => l.id.toString()));
    const teamIds = new Set(seed.teams.map((t) => t.id.toString()));
    const raceById = new Map(seed.races.map((r) => [r.id.toString(), r] as const));
    const sponsorIds = new Set(seed.sponsors.map((s) => s.id.toString()));
    const seasonById = new Map(seed.seasons.map((s) => [s.id, s] as const));
    const walletById = new Map(seed.leagueWallets.map((w) => [w.id.toString(), w] as const));
    const protestById = new Map(seed.protests.map((p) => [p.id, p] as const));

    // Deterministic demo IDs used by the website
    expect(seed.sponsors.some((s) => s.id.toString() === 'demo-sponsor-1')).toBe(true);

    // Seasons + sponsorship ecosystem
    expect(seed.seasons.some((s) => s.id === 'season-1')).toBe(true);
    expect(seed.seasons.some((s) => s.id === 'season-2')).toBe(true);

    // Referential integrity: seasons must reference real leagues
    for (const season of seed.seasons) {
      expect(leagueIds.has(season.leagueId)).toBe(true);
    }

    // Referential integrity: season sponsorships must reference real season + sponsor
    for (const sponsorship of seed.seasonSponsorships) {
      expect(seasonById.has(sponsorship.seasonId)).toBe(true);
      expect(sponsorIds.has(sponsorship.sponsorId)).toBe(true);

      const season = seasonById.get(sponsorship.seasonId);
      if (season && sponsorship.leagueId !== undefined) {
        expect(sponsorship.leagueId).toBe(season.leagueId);
      }
    }

    // Referential integrity: sponsorship requests must reference a real sponsor and target entity
    for (const request of seed.sponsorshipRequests) {
      expect(sponsorIds.has(request.sponsorId)).toBe(true);

      if (request.entityType === 'season') {
        expect(seasonById.has(request.entityId)).toBe(true);
      } else if (request.entityType === 'team') {
        expect(teamIds.has(request.entityId)).toBe(true);
      } else if (request.entityType === 'race') {
        expect(raceById.has(request.entityId)).toBe(true);
      } else if (request.entityType === 'driver') {
        expect(driverIds.has(request.entityId)).toBe(true);
      }
    }

    const season1PendingRequests = seed.sponsorshipRequests.filter(
      (r) => r.entityType === 'season' && r.entityId === 'season-1' && r.status === 'pending',
    );
    expect(season1PendingRequests.length).toBeGreaterThan(0);

    // Wallet edge cases:
    // - some leagues have no wallet at all
    expect(seed.leagueWallets.some((w) => w.leagueId.toString() === 'league-2')).toBe(false);
    expect(seed.leagueWallets.some((w) => w.leagueId.toString() === 'league-7')).toBe(false);

    // - some wallets have no transactions (league-3 is intentionally quiet)
    const league3Wallet = seed.leagueWallets.find((w) => w.leagueId.toString() === 'league-3');
    if (league3Wallet) {
      const league3TxCount = seed.leagueWalletTransactions.filter(
        (tx) => tx.walletId.toString() === league3Wallet.id.toString(),
      ).length;
      expect(league3TxCount).toBe(0);
    }

    // - demo wallet has a pending prize payout
    const league5Wallet = seed.leagueWallets.find((w) => w.leagueId.toString() === 'league-5');
    expect(league5Wallet).toBeDefined();
    if (league5Wallet) {
      const league5PendingPrizes = seed.leagueWalletTransactions.filter(
        (tx) =>
          tx.walletId.toString() === league5Wallet.id.toString() &&
          tx.type === 'prize_payout' &&
          tx.status === 'pending',
      );
      expect(league5PendingPrizes.length).toBeGreaterThan(0);
    }

    // Referential integrity: all transactions reference an existing wallet
    for (const tx of seed.leagueWalletTransactions) {
      expect(walletById.has(tx.walletId.toString())).toBe(true);
    }

    // Wallet invariant safety: no completed debit causes an overdraft when applied in factory order.
    for (const wallet of seed.leagueWallets) {
      const walletId = wallet.id.toString();
      const completed = seed.leagueWalletTransactions
        .filter((t) => t.walletId.toString() === walletId && t.status === 'completed')
        .filter((t) => t.type !== 'prize_payout');

      const sum = (values: number[]) => values.reduce((acc, v) => acc + v, 0);

      const credits = completed.filter((t) => t.type === 'membership_payment' || t.type === 'sponsorship_payment');
      const debits = completed.filter((t) => t.type === 'withdrawal' || t.type === 'refund');

      const creditNet = sum(credits.map((t) => t.netAmount.amount));
      const debitNet = sum(debits.map((t) => t.netAmount.amount));

      const baseline = wallet.balance.amount - creditNet + debitNet;
      expect(baseline).toBeGreaterThanOrEqual(0);

      const parseSeq = (id: string): number | undefined => {
        const match = id.match(/tx-league-\d+-(\d+)$/);
        if (!match) return undefined;
        const seq = Number(match[1]);
        return Number.isFinite(seq) ? seq : undefined;
      };

      const inFactoryOrder = [...completed].sort((a, b) => {
        const aSeq = parseSeq(a.id.toString());
        const bSeq = parseSeq(b.id.toString());
        if (aSeq !== undefined && bSeq !== undefined) return aSeq - bSeq;
        if (aSeq !== undefined) return -1;
        if (bSeq !== undefined) return 1;
        return a.createdAt.getTime() - b.createdAt.getTime();
      });

      let running = baseline;
      for (const tx of inFactoryOrder) {
        const net = tx.netAmount.amount;
        if (tx.type === 'withdrawal' || tx.type === 'refund') {
          running -= net;
          expect(running).toBeGreaterThanOrEqual(0);
        } else {
          running += net;
        }
      }

      expect(running).toBeCloseTo(wallet.balance.amount, 6);
    }

    // Membership edge cases:
    // - league-2 is intentionally empty
    expect(seed.leagueMemberships.some((m) => m.leagueId.toString() === 'league-2')).toBe(false);

    // - league-5 is "busy" (full/overbooked mix of statuses)
    const league5Memberships = seed.leagueMemberships.filter((m) => m.leagueId.toString() === 'league-5');
    expect(league5Memberships.length).toBeGreaterThan(0);
    expect(league5Memberships.some((m) => m.status.toString() === 'active')).toBe(true);
    expect(league5Memberships.some((m) => m.status.toString() === 'pending')).toBe(true);

    // Stewarding referential integrity
    for (const protest of seed.protests) {
      expect(raceById.has(protest.raceId)).toBe(true);
      expect(driverIds.has(protest.protestingDriverId)).toBe(true);
      expect(driverIds.has(protest.accusedDriverId)).toBe(true);

      const race = raceById.get(protest.raceId);
      if (race) {
        const leagueId = race.leagueId.toString();

        const hasMembership = (driverId: string) =>
          seed.leagueMemberships.some(
            (m) => m.leagueId.toString() === leagueId && m.driverId.toString() === driverId,
          );

        expect(hasMembership(protest.protestingDriverId)).toBe(true);
        expect(hasMembership(protest.accusedDriverId)).toBe(true);
      }
    }

    for (const penalty of seed.penalties) {
      expect(raceById.has(penalty.raceId)).toBe(true);
      expect(driverIds.has(penalty.driverId)).toBe(true);

      const race = raceById.get(penalty.raceId);
      if (race) {
        expect(penalty.leagueId).toBe(race.leagueId.toString());
      }

      if (penalty.protestId !== undefined) {
        expect(protestById.has(penalty.protestId)).toBe(true);
        const protest = protestById.get(penalty.protestId);
        if (protest) {
          expect(penalty.raceId).toBe(protest.raceId);
        }
      }
    }

    // Stewarding edge cases: demo league has multiple protest statuses
    const protestStatusesLeague5 = new Set(
      seed.protests
        .filter((p) =>
          seed.races.some((r) => r.id.toString() === p.raceId && r.leagueId.toString() === 'league-5'),
        )
        .map((p) => p.status.toString()),
    );
    expect(protestStatusesLeague5.has('pending')).toBe(true);
    expect(protestStatusesLeague5.has('under_review') || protestStatusesLeague5.has('upheld')).toBe(true);

    // Penalties should include at least one linked to a protest
    expect(seed.penalties.some((p) => (p as unknown as { protestId?: string }).protestId)).toBe(true);

    // Basic ID sanity (no duplicates per collection)
    const assertNoDuplicateIds = (ids: string[]) => {
      const unique = new Set(ids);
      expect(unique.size).toBe(ids.length);
    };

    assertNoDuplicateIds(seed.drivers.map((d) => d.id.toString()));
    assertNoDuplicateIds(seed.leagues.map((l) => l.id.toString()));
    assertNoDuplicateIds(seed.teams.map((t) => t.id.toString()));
    assertNoDuplicateIds(seed.sponsors.map((s) => s.id.toString()));
    assertNoDuplicateIds(seed.seasons.map((s) => s.id));
    assertNoDuplicateIds(seed.seasonSponsorships.map((s) => s.id));
    assertNoDuplicateIds(seed.sponsorshipRequests.map((r) => r.id));
    assertNoDuplicateIds(seed.protests.map((p) => p.id));
    assertNoDuplicateIds(seed.penalties.map((p) => p.id));
    assertNoDuplicateIds(seed.leagueWallets.map((w) => w.id.toString()));
    assertNoDuplicateIds(seed.leagueWalletTransactions.map((t) => t.id.toString()));
  });
});