import { NextRequest } from "next/server";
import {
  BetTicketStatus,
  BonusStatus,
  CasinoRoundStatus,
  EventStatus,
  KycStatus,
  LedgerEntryType,
  MarketStatus,
  PaymentType,
  Prisma,
  RiskSeverity,
  Role,
  TransactionStatus,
  UserBonusStatus,
  UserStatus,
  WalletAccountType
} from "@prisma/client";
import { prisma } from "@/server/db";
import { fail, ok } from "@/server/http";
import { requireUser } from "@/server/security/auth";
import { requireRole } from "@/server/security/rbac";

export const dynamic = "force-dynamic";

export async function GET(request: NextRequest) {
  try {
    const { roles } = await requireUser(request);
    requireRole(roles, [Role.ADMIN, Role.FINANCE_MANAGER, Role.RISK_ANALYST, Role.KYC_ANALYST]);
    const now = new Date();
    const today = new Date(now);
    today.setHours(0, 0, 0, 0);
    const last24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);

    const [
      users,
      activeUsers,
      newUsersToday,
      suspendedUsers,
      selfExcludedUsers,
      pendingKyc,
      approvedKyc,
      rejectedKyc,
      openBets,
      betsToday,
      betWinsToday,
      pendingWithdrawals,
      withdrawalsToday,
      pendingDeposits,
      depositsToday,
      availableBalance,
      bonusBalance,
      liveEvents,
      scheduledEvents,
      openMarkets,
      suspendedMarkets,
      activePromotions,
      activeUserBonuses,
      riskEvents,
      highRiskEvents,
      criticalRiskEvents,
      ledgerEntries,
      auditLogs24h,
      providerFailures24h,
      providerLatency24h,
      casinoToday,
      casinoActiveRounds,
      casinoWonToday,
      casinoLostToday,
      casinoVoidToday
    ] = await Promise.all([
      prisma.user.count(),
      prisma.user.count({ where: { status: UserStatus.ACTIVE } }),
      prisma.user.count({ where: { createdAt: { gte: today } } }),
      prisma.user.count({ where: { status: UserStatus.SUSPENDED } }),
      prisma.user.count({ where: { status: UserStatus.SELF_EXCLUDED } }),
      prisma.kycCase.count({ where: { status: KycStatus.PENDING } }),
      prisma.kycCase.count({ where: { status: KycStatus.APPROVED } }),
      prisma.kycCase.count({ where: { status: KycStatus.REJECTED } }),
      prisma.betTicket.aggregate({
        where: { status: BetTicketStatus.OPEN },
        _count: { _all: true },
        _sum: { stake: true, potentialPayout: true }
      }),
      prisma.betTicket.aggregate({
        where: { placedAt: { gte: today } },
        _count: { _all: true },
        _sum: { stake: true }
      }),
      prisma.ledgerEntry.aggregate({
        where: { type: LedgerEntryType.BET_WIN, createdAt: { gte: today } },
        _sum: { amount: true }
      }),
      prisma.withdrawalRequest.aggregate({
        where: { status: TransactionStatus.PENDING },
        _count: { _all: true },
        _sum: { amount: true }
      }),
      prisma.withdrawalRequest.aggregate({
        where: { createdAt: { gte: today } },
        _count: { _all: true },
        _sum: { amount: true }
      }),
      prisma.paymentTransaction.aggregate({
        where: { type: PaymentType.DEPOSIT, status: TransactionStatus.PENDING },
        _count: { _all: true },
        _sum: { amount: true }
      }),
      prisma.paymentTransaction.aggregate({
        where: { type: PaymentType.DEPOSIT, status: TransactionStatus.APPROVED, createdAt: { gte: today } },
        _count: { _all: true },
        _sum: { amount: true }
      }),
      prisma.walletAccount.aggregate({
        where: { type: WalletAccountType.AVAILABLE },
        _sum: { balance: true }
      }),
      prisma.walletAccount.aggregate({
        where: { type: WalletAccountType.BONUS },
        _sum: { balance: true }
      }),
      prisma.event.count({ where: { isLive: true } }),
      prisma.event.count({ where: { status: EventStatus.SCHEDULED } }),
      prisma.market.count({ where: { status: MarketStatus.OPEN } }),
      prisma.market.count({ where: { status: MarketStatus.SUSPENDED } }),
      prisma.bonusPromotion.count({ where: { status: BonusStatus.ACTIVE } }),
      prisma.userBonus.count({ where: { status: { in: [UserBonusStatus.OFFERED, UserBonusStatus.ACTIVE] } } }),
      prisma.riskEvent.count({ where: { status: "open" } }),
      prisma.riskEvent.count({ where: { status: "open", severity: RiskSeverity.HIGH } }),
      prisma.riskEvent.count({ where: { status: "open", severity: RiskSeverity.CRITICAL } }),
      prisma.ledgerEntry.count(),
      prisma.auditLog.count({ where: { createdAt: { gte: last24h } } }),
      prisma.providerLog.count({
        where: {
          createdAt: { gte: last24h },
          status: { notIn: ["ok", "OK", "success", "SUCCESS", "200", "204"] }
        }
      }),
      prisma.providerLog.aggregate({
        where: { createdAt: { gte: last24h }, latencyMs: { not: null } },
        _avg: { latencyMs: true }
      }),
      prisma.casinoRound.aggregate({
        where: { createdAt: { gte: today } },
        _count: { _all: true },
        _sum: { stake: true, payout: true }
      }),
      prisma.casinoRound.count({ where: { status: CasinoRoundStatus.ACTIVE } }),
      prisma.casinoRound.count({ where: { status: CasinoRoundStatus.WON, createdAt: { gte: today } } }),
      prisma.casinoRound.count({ where: { status: CasinoRoundStatus.LOST, createdAt: { gte: today } } }),
      prisma.casinoRound.count({ where: { status: CasinoRoundStatus.VOID, createdAt: { gte: today } } })
    ]);
    const sportsbookGgrToday = difference(betsToday._sum.stake, betWinsToday._sum.amount);
    const casinoGgrToday = difference(casinoToday._sum.stake, casinoToday._sum.payout);
    const totalGgrToday = decimal(sportsbookGgrToday).plus(casinoGgrToday).toFixed(2);

    return ok({
      generatedAt: now.toISOString(),
      users: {
        total: users,
        active: activeUsers,
        newToday: newUsersToday,
        suspended: suspendedUsers,
        selfExcluded: selfExcludedUsers
      },
      kyc: {
        pending: pendingKyc,
        approved: approvedKyc,
        rejected: rejectedKyc
      },
      sportsbook: {
        openBets: openBets._count._all,
        openStake: money(openBets._sum.stake),
        openLiability: money(openBets._sum.potentialPayout),
        ticketsToday: betsToday._count._all,
        stakeToday: money(betsToday._sum.stake),
        payoutToday: money(betWinsToday._sum.amount),
        ggrToday: sportsbookGgrToday,
        liveEvents,
        scheduledEvents,
        openMarkets,
        suspendedMarkets
      },
      casino: {
        roundsToday: casinoToday._count._all,
        stakeToday: money(casinoToday._sum.stake),
        payoutToday: money(casinoToday._sum.payout),
        ggrToday: casinoGgrToday,
        activeRounds: casinoActiveRounds,
        wonToday: casinoWonToday,
        lostToday: casinoLostToday,
        voidToday: casinoVoidToday
      },
      revenue: {
        ggrToday: totalGgrToday
      },
      payments: {
        pendingWithdrawals: pendingWithdrawals._count._all,
        pendingWithdrawalAmount: money(pendingWithdrawals._sum.amount),
        withdrawalsToday: withdrawalsToday._count._all,
        withdrawalsTodayAmount: money(withdrawalsToday._sum.amount),
        pendingDeposits: pendingDeposits._count._all,
        pendingDepositAmount: money(pendingDeposits._sum.amount),
        depositsToday: depositsToday._count._all,
        depositsTodayAmount: money(depositsToday._sum.amount)
      },
      balances: {
        available: money(availableBalance._sum.balance),
        bonus: money(bonusBalance._sum.balance)
      },
      promotions: {
        active: activePromotions,
        activeUserBonuses
      },
      risk: {
        open: riskEvents,
        high: highRiskEvents,
        critical: criticalRiskEvents
      },
      platform: {
        ledgerEntries,
        auditLogs24h,
        providerFailures24h,
        avgProviderLatencyMs: Math.round(providerLatency24h._avg.latencyMs ?? 0)
      }
    });
  } catch (error) {
    return fail(error);
  }
}

function decimal(value: Prisma.Decimal | number | string | null | undefined) {
  return new Prisma.Decimal(value ?? 0);
}

function money(value: Prisma.Decimal | number | string | null | undefined) {
  return decimal(value).toFixed(2);
}

function difference(left: Prisma.Decimal | number | string | null | undefined, right: Prisma.Decimal | number | string | null | undefined) {
  return decimal(left).minus(decimal(right)).toFixed(2);
}
