import { NextRequest } from "next/server";
import { AffiliateOfferStatus, AffiliateOfferType } from "@prisma/client";
import { z } from "zod";
import { prisma } from "@/server/db";
import { AppError } from "@/server/errors";
import { requireAffiliateAdmin } from "@/server/affiliation/service";
import { fail, ok, parseJson } from "@/server/http";

const offerSchema = z.object({
  id: z.string().uuid().optional(),
  code: z.string().min(2).max(40).transform((value) => value.toUpperCase()),
  name: z.string().min(3).max(120),
  type: z.nativeEnum(AffiliateOfferType),
  status: z.nativeEnum(AffiliateOfferStatus).default(AffiliateOfferStatus.ACTIVE),
  currency: z.string().min(3).max(8).default("EUR"),
  cpaAmount: z.coerce.number().min(0).max(100000).nullable().optional(),
  revenueShareBps: z.coerce.number().int().min(0).max(10000).nullable().optional(),
  hybridCpaAmount: z.coerce.number().min(0).max(100000).nullable().optional(),
  hybridRevenueShareBps: z.coerce.number().int().min(0).max(10000).nullable().optional(),
  minFtdAmount: z.coerce.number().min(0).max(100000).default(25),
  cookieDays: z.coerce.number().int().min(1).max(180).default(30),
  terms: z.string().min(10).max(1600)
});

export async function GET(request: NextRequest) {
  try {
    await requireAffiliateAdmin(request);
    const offers = await prisma.affiliateOffer.findMany({
      where: { status: { not: AffiliateOfferStatus.ARCHIVED } },
      orderBy: { createdAt: "desc" }
    });
    return ok({ offers: offers.map(serializeOffer) });
  } catch (error) {
    return fail(error);
  }
}

export async function POST(request: NextRequest) {
  try {
    await requireAffiliateAdmin(request);
    const input = await parseJson(request, offerSchema);
    validateOfferEconomics(input);

    const offer = await prisma.affiliateOffer.upsert({
      where: { code: input.code },
      create: offerData(input),
      update: offerData(input)
    });

    return ok({ offer: serializeOffer(offer) });
  } catch (error) {
    return fail(error);
  }
}

export async function PATCH(request: NextRequest) {
  try {
    await requireAffiliateAdmin(request);
    const input = await parseJson(request, offerSchema.extend({ id: z.string().uuid() }));
    validateOfferEconomics(input);
    const offer = await prisma.affiliateOffer.update({ where: { id: input.id }, data: offerData(input) });
    return ok({ offer: serializeOffer(offer) });
  } catch (error) {
    return fail(error);
  }
}

export async function DELETE(request: NextRequest) {
  try {
    await requireAffiliateAdmin(request);
    const id = request.nextUrl.searchParams.get("id");
    if (!id) throw new AppError("Offer id is required", 422, "OFFER_ID_REQUIRED");
    await prisma.affiliateOffer.update({ where: { id }, data: { status: AffiliateOfferStatus.ARCHIVED } });
    return ok({ id });
  } catch (error) {
    return fail(error);
  }
}

function validateOfferEconomics(input: z.infer<typeof offerSchema>) {
  if (input.type === AffiliateOfferType.CPA && !input.cpaAmount) {
    throw new AppError("CPA amount is required", 422, "CPA_AMOUNT_REQUIRED");
  }
  if (input.type === AffiliateOfferType.RS && !input.revenueShareBps) {
    throw new AppError("Revenue share is required", 422, "RS_REQUIRED");
  }
  if (input.type === AffiliateOfferType.HYBRID && (!input.hybridCpaAmount || !input.hybridRevenueShareBps)) {
    throw new AppError("Hybrid CPA and RS are required", 422, "HYBRID_REQUIRED");
  }
}

function offerData(input: z.infer<typeof offerSchema>) {
  return {
    code: input.code,
    name: input.name,
    type: input.type,
    status: input.status,
    currency: input.currency,
    cpaAmount: input.type === AffiliateOfferType.CPA ? input.cpaAmount : null,
    revenueShareBps: input.type === AffiliateOfferType.RS ? input.revenueShareBps : null,
    hybridCpaAmount: input.type === AffiliateOfferType.HYBRID ? input.hybridCpaAmount : null,
    hybridRevenueShareBps: input.type === AffiliateOfferType.HYBRID ? input.hybridRevenueShareBps : null,
    minFtdAmount: input.minFtdAmount,
    cookieDays: input.cookieDays,
    terms: input.terms
  };
}

function serializeOffer(offer: Awaited<ReturnType<typeof prisma.affiliateOffer.findMany>>[number]) {
  return {
    id: offer.id,
    code: offer.code,
    name: offer.name,
    type: offer.type,
    status: offer.status,
    currency: offer.currency,
    cpaAmount: offer.cpaAmount?.toString() ?? null,
    revenueShareBps: offer.revenueShareBps,
    hybridCpaAmount: offer.hybridCpaAmount?.toString() ?? null,
    hybridRevenueShareBps: offer.hybridRevenueShareBps,
    minFtdAmount: offer.minFtdAmount.toString(),
    cookieDays: offer.cookieDays,
    terms: offer.terms
  };
}
