import { NextRequest } from "next/server";
import { AffiliateCommissionStatus, AffiliatePayoutStatus, Prisma } from "@prisma/client";
import { prisma } from "@/server/db";
import { AppError, ConflictError } from "@/server/errors";
import { fail, ok } from "@/server/http";
import { requireAffiliate, monthPeriod, payoutWindowForPeriodEnd } from "@/server/affiliation/service";

export async function POST(request: NextRequest) {
  try {
    const { account } = await requireAffiliate(request);
    const now = new Date();
    const previousMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0, 23, 59, 59, 999));
    const period = monthPeriod(previousMonth);
    const window = payoutWindowForPeriodEnd(period.end);

    if (now < window.start || now > window.end) {
      throw new AppError("Les paiements sont ouverts entre le 10 et le 15 du mois.", 422, "PAYOUT_WINDOW_CLOSED", {
        nextWindowStart: window.start,
        nextWindowEnd: window.end
      });
    }

    const payout = await prisma.$transaction(async (tx) => {
      const existing = await tx.affiliatePayout.findFirst({
        where: { accountId: account.id, periodStart: period.start, periodEnd: period.end, status: { not: AffiliatePayoutStatus.CANCELLED } }
      });
      if (existing) {
        throw new ConflictError("Une demande de paiement existe déjà pour cette période.", "PAYOUT_ALREADY_EXISTS");
      }

      const commissions = await tx.affiliateCommission.findMany({
        where: {
          accountId: account.id,
          payoutId: null,
          status: { in: [AffiliateCommissionStatus.APPROVED, AffiliateCommissionStatus.PAYABLE] },
          payableFrom: { lte: now },
          periodStart: { gte: period.start },
          periodEnd: { lte: period.end }
        }
      });

      const amount = commissions.reduce((sum, commission) => sum.plus(commission.amount), new Prisma.Decimal(0));
      if (amount.lte(0) || commissions.length === 0) {
        throw new AppError("Aucune commission payable pour cette période.", 422, "NO_PAYABLE_COMMISSION");
      }

      const created = await tx.affiliatePayout.create({
        data: {
          accountId: account.id,
          periodStart: period.start,
          periodEnd: period.end,
          amount,
          currency: commissions[0]?.currency ?? "EUR",
          status: AffiliatePayoutStatus.REQUESTED,
          requestedAt: now,
          paymentMethod: account.payoutMethod
        }
      });

      await tx.affiliateCommission.updateMany({
        where: { id: { in: commissions.map((commission) => commission.id) } },
        data: { payoutId: created.id }
      });

      return created;
    });

    return ok({ payout: { id: payout.id, amount: payout.amount.toString(), status: payout.status } });
  } catch (error) {
    return fail(error);
  }
}
