import { Decimal } from '@prisma/client/runtime/library';
import type { PrismaService } from '../../../shared/prisma/prisma.service';
import type { WalletService } from '../../ledger/wallet.service';

export interface BetFlowFixtureIds {
  runId: string;
  operatorId: bigint;
  playerId: bigint;
  agentId?: bigint;
  matchId: bigint;
  marketId: bigint;
  homeSelectionId: bigint;
  homeOddsVersion: bigint;
  drawSelectionId: bigint;
  drawOddsVersion: bigint;
  awaySelectionId: bigint;
  awayOddsVersion: bigint;
  leagueId: bigint;
  homeTeamId: bigint;
  awayTeamId: bigint;
}

export async function createBetFlowFixture(
  prisma: PrismaService,
  wallet: WalletService,
  opts?: { initialBalance?: number; withAgent?: boolean },
): Promise<BetFlowFixtureIds> {
  const runId = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;

  const operator = await prisma.user.create({
    data: {
      username: `smoke_op_${runId}`,
      userType: 'ADMIN',
      auth: { create: { passwordHash: 'smoke-test' } },
    },
  });

  const league = await prisma.league.create({
    data: {
      code: `SMK_L_${runId}`,
      sportType: 'FOOTBALL',
    },
  });

  const homeTeam = await prisma.team.create({
    data: { code: `SMK_H_${runId}`, sportType: 'FOOTBALL' },
  });
  const awayTeam = await prisma.team.create({
    data: { code: `SMK_A_${runId}`, sportType: 'FOOTBALL' },
  });

  const startTime = new Date(Date.now() + 48 * 60 * 60 * 1000);
  const match = await prisma.match.create({
    data: {
      sportType: 'FOOTBALL',
      leagueId: league.id,
      homeTeamId: homeTeam.id,
      awayTeamId: awayTeam.id,
      startTime,
      status: 'PUBLISHED',
      publishTime: new Date(),
    },
  });

  const market = await prisma.market.create({
    data: {
      matchId: match.id,
      marketType: 'FT_1X2',
      period: 'FT',
      status: 'OPEN',
      selections: {
        create: [
          {
            selectionCode: 'HOME',
            selectionName: 'Home',
            odds: new Decimal('2.00'),
            oddsVersion: BigInt(1),
            status: 'OPEN',
            sortOrder: 0,
          },
          {
            selectionCode: 'DRAW',
            selectionName: 'Draw',
            odds: new Decimal('3.00'),
            oddsVersion: BigInt(1),
            status: 'OPEN',
            sortOrder: 1,
          },
          {
            selectionCode: 'AWAY',
            selectionName: 'Away',
            odds: new Decimal('4.00'),
            oddsVersion: BigInt(1),
            status: 'OPEN',
            sortOrder: 2,
          },
        ],
      },
    },
    include: { selections: true },
  });

  let agentId: bigint | undefined;
  if (opts?.withAgent) {
    const agent = await prisma.user.create({
      data: {
        username: `smoke_ag_${runId}`,
        userType: 'AGENT',
        auth: { create: { passwordHash: 'smoke-test' } },
        agentProfile: {
          create: { level: 1, creditLimit: new Decimal(100000) },
        },
      },
    });
    await prisma.agentClosure.create({
      data: { ancestorId: agent.id, descendantId: agent.id, depth: 0 },
    });
    agentId = agent.id;
  }

  const player = await prisma.user.create({
    data: {
      username: `smoke_pl_${runId}`,
      userType: 'PLAYER',
      parentId: agentId,
      auth: { create: { passwordHash: 'smoke-test' } },
      wallet: { create: { currency: 'USD' } },
    },
  });

  const balance = opts?.initialBalance ?? 1000;
  if (balance > 0) {
    await wallet.deposit(player.id, balance, operator.id, 'smoke test seed');
  }

  const home = market.selections.find((s) => s.selectionCode === 'HOME')!;
  const draw = market.selections.find((s) => s.selectionCode === 'DRAW')!;
  const away = market.selections.find((s) => s.selectionCode === 'AWAY')!;

  return {
    runId,
    operatorId: operator.id,
    playerId: player.id,
    agentId,
    matchId: match.id,
    marketId: market.id,
    homeSelectionId: home.id,
    homeOddsVersion: home.oddsVersion,
    drawSelectionId: draw.id,
    drawOddsVersion: draw.oddsVersion,
    awaySelectionId: away.id,
    awayOddsVersion: away.oddsVersion,
    leagueId: league.id,
    homeTeamId: homeTeam.id,
    awayTeamId: awayTeam.id,
  };
}

export async function teardownBetFlowFixture(
  prisma: PrismaService,
  fx: BetFlowFixtureIds,
): Promise<void> {
  const bets = await prisma.bet.findMany({
    where: { userId: fx.playerId },
    select: { id: true },
  });
  const betIds = bets.map((b) => b.id);

  const batches = await prisma.settlementBatch.findMany({
    where: { matchId: fx.matchId },
    select: { id: true },
  });
  const batchIds = batches.map((b) => b.id);

  if (batchIds.length) {
    await prisma.settlementItem.deleteMany({ where: { batchId: { in: batchIds } } });
    await prisma.settlementBatch.deleteMany({ where: { id: { in: batchIds } } });
  }
  if (betIds.length) {
    await prisma.betSelection.deleteMany({ where: { betId: { in: betIds } } });
    await prisma.bet.deleteMany({ where: { id: { in: betIds } } });
  }

  await prisma.walletTransaction.deleteMany({ where: { userId: fx.playerId } });
  await prisma.wallet.deleteMany({ where: { userId: fx.playerId } });
  await prisma.matchScore.deleteMany({ where: { matchId: fx.matchId } });

  const markets = await prisma.market.findMany({
    where: { matchId: fx.matchId },
    select: { id: true },
  });
  const marketIds = markets.map((m) => m.id);
  if (marketIds.length) {
    await prisma.marketSelection.deleteMany({ where: { marketId: { in: marketIds } } });
    await prisma.market.deleteMany({ where: { id: { in: marketIds } } });
  }

  await prisma.match.deleteMany({ where: { id: fx.matchId } });

  if (fx.agentId) {
    await prisma.agentClosure.deleteMany({
      where: { OR: [{ ancestorId: fx.agentId }, { descendantId: fx.agentId }] },
    });
    await prisma.agentProfile.deleteMany({ where: { userId: fx.agentId } });
    await prisma.userAuth.deleteMany({ where: { userId: fx.agentId } });
    await prisma.user.deleteMany({ where: { id: fx.agentId } });
  }

  await prisma.userAuth.deleteMany({
    where: { userId: { in: [fx.playerId, fx.operatorId] } },
  });
  await prisma.user.deleteMany({ where: { id: { in: [fx.playerId, fx.operatorId] } } });
  await prisma.team.deleteMany({ where: { id: { in: [fx.homeTeamId, fx.awayTeamId] } } });
  await prisma.league.deleteMany({ where: { id: fx.leagueId } });
}
