import { NextRequest } from "next/server";
import { LedgerDirection, LedgerEntryType, Prisma, UserBonusStatus, WalletAccountType } from "@prisma/client";
import { z } from "zod";
import { prisma } from "@/server/db";
import { fail, ok, parseJson } from "@/server/http";
import { AppError, ConflictError } from "@/server/errors";
import { requireUser } from "@/server/security/auth";
import { ensureWallet, postLedgerEntry } from "@/server/wallet/ledger";

const actionSchema = z.object({
  action: z.enum(["accept", "refuse"])
});

export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
  try {
    const { id } = await params;
    const { user } = await requireUser(request);
    const input = await parseJson(request, actionSchema);

    const result = await prisma.$transaction(async (tx) => {
      const bonus = await tx.userBonus.findFirst({
        where: { id, userId: user.id },
        include: { promotion: true }
      });

      if (!bonus) {
        throw new AppError("Bonus not found", 404, "BONUS_NOT_FOUND");
      }

      if (bonus.status !== UserBonusStatus.OFFERED) {
        throw new ConflictError("Bonus offer is no longer actionable", "BONUS_NOT_ACTIONABLE");
      }

      if (input.action === "refuse") {
        return tx.userBonus.update({
          where: { id },
          data: { status: UserBonusStatus.CANCELLED },
          include: { promotion: true }
        });
      }

      const wallet = await ensureWallet(user.id, bonus.promotion.currency, tx);
      await postLedgerEntry(tx, {
        walletId: wallet.id,
        accountType: WalletAccountType.BONUS,
        direction: LedgerDirection.CREDIT,
        type: LedgerEntryType.BONUS_CREDIT,
        amount: bonus.bonusAmount,
        currency: bonus.promotion.currency,
        referenceType: "user_bonus",
        referenceId: bonus.id,
        idempotencyKey: `user_bonus:${bonus.id}:accept`,
        reason: "Bonus accepted by player",
        metadata: {
          promotionId: bonus.promotionId,
          wageringMultiplier: bonus.promotion.wageringMultiplier.toString()
        } as Prisma.InputJsonValue
      });

      return tx.userBonus.update({
        where: { id },
        data: { status: UserBonusStatus.ACTIVE, remainingAmount: bonus.bonusAmount },
        include: { promotion: true }
      });
    });

    return ok({ bonus: result });
  } catch (error) {
    return fail(error);
  }
}
