import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../shared/prisma/prisma.service';
import { PresenceService } from '../../domains/presence/presence.service';
import { Decimal } from '@prisma/client/runtime/library';

function dec(v: Decimal | null | undefined) {
  return v?.toString() ?? '0';
}

function sub(a: Decimal | null | undefined, b: Decimal | null | undefined) {
  return new Decimal(a ?? 0).sub(b ?? 0).toString();
}

@Injectable()
export class AdminDashboardService {
  constructor(
    private prisma: PrismaService,
    private presence: PresenceService,
  ) {}

  async getOverview() {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const trend7d = await Promise.all(
      Array.from({ length: 7 }, (_, i) => {
        const dayStart = new Date(today);
        dayStart.setDate(dayStart.getDate() - (6 - i));
        const dayEnd = new Date(dayStart);
        dayEnd.setDate(dayEnd.getDate() + 1);
        return this.prisma.bet
          .aggregate({
            where: { placedAt: { gte: dayStart, lt: dayEnd } },
            _sum: { stake: true, actualReturn: true },
            _count: true,
          })
          .then((agg) => ({
            date: dayStart.toISOString().slice(0, 10),
            label: `${dayStart.getMonth() + 1}/${dayStart.getDate()}`,
            betCount: agg._count,
            stake: dec(agg._sum.stake),
            payout: dec(agg._sum.actualReturn),
            ggr: sub(agg._sum.stake, agg._sum.actualReturn),
          }));
      }),
    );

    const playerWhere = { userType: 'PLAYER', deletedAt: null };

    const [
      todayBets,
      yesterdayBets,
      pendingBets,
      betStatusToday,
      matchGroups,
      matchTotal,
      playerTotal,
      playerActive,
      playerSuspended,
      playerDirect,
      newPlayersToday,
      agentProfiles,
      agentsActive,
      walletAgg,
      recentBets,
      recentPlayers,
      playersOnlineNow,
    ] = await Promise.all([
      this.prisma.bet.aggregate({
        where: { placedAt: { gte: today } },
        _sum: { stake: true, actualReturn: true },
        _count: true,
      }),
      this.prisma.bet.aggregate({
        where: {
          placedAt: {
            gte: new Date(today.getTime() - 86400000),
            lt: today,
          },
        },
        _sum: { stake: true, actualReturn: true },
        _count: true,
      }),
      this.prisma.bet.count({ where: { status: 'PENDING' } }),
      this.prisma.bet.groupBy({
        by: ['status'],
        where: { placedAt: { gte: today } },
        _count: { _all: true },
        _sum: { stake: true },
      }),
      this.prisma.match.groupBy({
        by: ['status'],
        where: { deletedAt: null },
        _count: { _all: true },
      }),
      this.prisma.match.count({ where: { deletedAt: null } }),
      this.prisma.user.count({ where: playerWhere }),
      this.prisma.user.count({ where: { ...playerWhere, status: 'ACTIVE' } }),
      this.prisma.user.count({ where: { ...playerWhere, status: 'SUSPENDED' } }),
      this.prisma.user.count({
        where: { ...playerWhere, parentId: null },
      }),
      this.prisma.user.count({
        where: { ...playerWhere, createdAt: { gte: today } },
      }),
      this.prisma.agentProfile.aggregate({
        _sum: { creditLimit: true, usedCredit: true },
        _count: { _all: true },
      }),
      this.prisma.agentProfile.count({ where: { status: 'ACTIVE' } }),
      this.prisma.wallet.aggregate({
        where: { user: playerWhere },
        _sum: { availableBalance: true, frozenBalance: true },
        _count: { _all: true },
      }),
      this.prisma.bet.findMany({
        take: 8,
        orderBy: { placedAt: 'desc' },
        include: { user: { select: { username: true } } },
      }),
      this.prisma.user.findMany({
        where: playerWhere,
        take: 6,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          username: true,
          status: true,
          createdAt: true,
          parent: { select: { username: true } },
        },
      }),
      this.presence.getOnlineCount(),
    ]);

    const matchByStatus: Record<string, number> = {};
    for (const g of matchGroups) {
      matchByStatus[g.status] = g._count._all;
    }

    const todayBetByStatus: Record<string, { count: number; stake: string }> = {};
    for (const g of betStatusToday) {
      todayBetByStatus[g.status] = {
        count: g._count._all,
        stake: dec(g._sum.stake),
      };
    }

    const creditLimit = agentProfiles._sum.creditLimit ?? new Decimal(0);
    const usedCredit = agentProfiles._sum.usedCredit ?? new Decimal(0);

    return {
      generatedAt: new Date().toISOString(),
      trend7d,
      today: {
        betCount: todayBets._count,
        stake: dec(todayBets._sum.stake),
        payout: dec(todayBets._sum.actualReturn),
        ggr: sub(todayBets._sum.stake, todayBets._sum.actualReturn),
        newPlayers: newPlayersToday,
      },
      yesterday: {
        betCount: yesterdayBets._count,
        stake: dec(yesterdayBets._sum.stake),
        payout: dec(yesterdayBets._sum.actualReturn),
        ggr: sub(yesterdayBets._sum.stake, yesterdayBets._sum.actualReturn),
      },
      users: {
        playersTotal: playerTotal,
        playersActive: playerActive,
        playersSuspended: playerSuspended,
        playersDirect: playerDirect,
        playersOnlineNow,
        agentsTotal: agentProfiles._count._all,
        agentsActive,
      },
      wallets: {
        totalAvailable: dec(walletAgg._sum.availableBalance),
        totalFrozen: dec(walletAgg._sum.frozenBalance),
        playerWalletCount: walletAgg._count._all,
      },
      agents: {
        totalCreditLimit: dec(creditLimit),
        totalUsedCredit: dec(usedCredit),
        totalAvailableCredit: creditLimit.sub(usedCredit).toString(),
      },
      matches: {
        total: matchTotal,
        draft: matchByStatus.DRAFT ?? 0,
        published: matchByStatus.PUBLISHED ?? 0,
        closed: matchByStatus.CLOSED ?? 0,
        cancelled: matchByStatus.CANCELLED ?? 0,
        pendingSettlement: matchByStatus.PENDING_SETTLEMENT ?? 0,
        settled: matchByStatus.SETTLED ?? 0,
      },
      bets: {
        pendingTotal: pendingBets,
        todayByStatus: todayBetByStatus,
      },
      recentBets: recentBets.map((b) => ({
        betNo: b.betNo,
        username: b.user.username,
        stake: dec(b.stake),
        status: b.status,
        placedAt: b.placedAt,
      })),
      recentPlayers: recentPlayers.map((p) => ({
        id: p.id.toString(),
        username: p.username,
        status: p.status,
        parentUsername: p.parent?.username ?? null,
        createdAt: p.createdAt,
      })),
    };
  }
}
