import type { PrismaClient } from '../generated/prisma/client.js';

type PrismaReader = Pick<PrismaClient, 'promotion'>;

export type LivePromotion = {
  id: number;
  title: string;
  slug: string;
  occasion: string | null;
  discountPercent: number;
  endsAt: Date;
  targetType: 'PRODUCTS' | 'CATEGORY' | 'STOREWIDE';
  productIds: Set<number>;
  categoryIds: Set<number>;
};

export async function livePromotions(prisma: PrismaReader): Promise<LivePromotion[]> {
  const now = new Date();
  const promotions = await prisma.promotion.findMany({
    where: { active: true, startsAt: { lte: now }, endsAt: { gt: now } },
    include: {
      products: { select: { productId: true } },
      categories: { select: { categoryId: true } },
    },
  });
  return promotions.map((promotion) => ({
    id: promotion.id,
    title: promotion.title,
    slug: promotion.slug,
    occasion: promotion.occasion,
    discountPercent: promotion.discountPercent,
    endsAt: promotion.endsAt,
    targetType: promotion.targetType,
    productIds: new Set(promotion.products.map(({ productId }) => productId)),
    categoryIds: new Set(promotion.categories.map(({ categoryId }) => categoryId)),
  }));
}

/**
 * Offers never stack: the biggest discount that covers the product wins.
 * A category offer also covers products in that category's sub-categories.
 */
export function bestPromotion(
  product: { id: number; categoryId: number; categoryParentId?: number | null },
  promotions: LivePromotion[],
): LivePromotion | undefined {
  let best: LivePromotion | undefined;
  for (const promotion of promotions) {
    const applies =
      promotion.targetType === 'STOREWIDE' ||
      (promotion.targetType === 'CATEGORY' &&
        (promotion.categoryIds.has(product.categoryId) ||
          (product.categoryParentId != null &&
            promotion.categoryIds.has(product.categoryParentId)))) ||
      (promotion.targetType === 'PRODUCTS' && promotion.productIds.has(product.id));
    if (applies && (!best || promotion.discountPercent > best.discountPercent)) {
      best = promotion;
    }
  }
  return best;
}

export function discounted(price: number, percent: number): number {
  return Math.round(price * (1 - percent / 100));
}
