import { prisma } from './db.js';
import { applyCatalogLuck, normalizeRates } from './weighted.js';
import { toCentsBigInt } from './cents.js';

/**
 * Count how many active cases the user has fully collected.
 * @param {import('@prisma/client').Prisma.TransactionClient | typeof prisma} db
 */
export async function countCompletedChapters(db, userId) {
  const cases = await db.case.findMany({
    where: { active: true },
    include: { items: { select: { itemId: true } } },
  });
  if (!cases.length) return 0;

  const entries = await db.catalogEntry.findMany({
    where: { userId },
    select: { caseId: true, itemId: true },
  });
  const filledByCase = new Map();
  for (const e of entries) {
    if (!filledByCase.has(e.caseId)) filledByCase.set(e.caseId, new Set());
    filledByCase.get(e.caseId).add(e.itemId);
  }

  let completed = 0;
  for (const c of cases) {
    if (!c.items.length) continue;
    const filled = filledByCase.get(c.id);
    if (!filled) continue;
    if (c.items.every((ci) => filled.has(ci.itemId))) completed += 1;
  }
  return completed;
}

/**
 * Fill catalog slots for newly dropped items. Idempotent upserts.
 * @returns {{ newlyFilled: object[], chapterJustCompleted: boolean, luckPercent: number }}
 */
export async function fillCatalogSlots(db, userId, caseId, itemIds) {
  const uniqueIds = [...new Set(itemIds.filter((id) => Number.isFinite(id)))];
  if (!uniqueIds.length) {
    const luckPercent = await countCompletedChapters(db, userId);
    return { newlyFilled: [], chapterJustCompleted: false, luckPercent };
  }

  const existing = await db.catalogEntry.findMany({
    where: {
      userId,
      caseId,
      itemId: { in: uniqueIds },
    },
    select: { itemId: true },
  });
  const already = new Set(existing.map((e) => e.itemId));
  const toCreate = uniqueIds.filter((id) => !already.has(id));

  const newlyFilled = [];
  for (const itemId of toCreate) {
    const entry = await db.catalogEntry.create({
      data: { userId, caseId, itemId },
      include: { item: true },
    });
    newlyFilled.push({
      itemId: entry.itemId,
      name: entry.item.name,
      imageUrl: entry.item.imageUrl,
      rarity: entry.item.rarity,
      filledAt: entry.filledAt,
    });
  }

  const caseItems = await db.caseItem.findMany({
    where: { caseId },
    select: { itemId: true },
  });
  const allFilled = await db.catalogEntry.findMany({
    where: { userId, caseId },
    select: { itemId: true },
  });
  const filledSet = new Set(allFilled.map((e) => e.itemId));
  const chapterComplete =
    caseItems.length > 0 && caseItems.every((ci) => filledSet.has(ci.itemId));

  const filledBefore = new Set(already);
  const completeBefore =
    caseItems.length > 0 && caseItems.every((ci) => filledBefore.has(ci.itemId));
  const chapterJustCompleted = chapterComplete && !completeBefore;

  const luckPercent = await countCompletedChapters(db, userId);
  return {
    newlyFilled,
    chapterJustCompleted,
    luckPercent,
  };
}

export async function getCatalogState(userId) {
  const cases = await prisma.case.findMany({
    where: { active: true },
    include: {
      items: {
        include: { item: true },
        orderBy: { dropRate: 'asc' },
      },
    },
  });
  cases.sort((a, b) => {
    const pa = toCentsBigInt(a.price);
    const pb = toCentsBigInt(b.price);
    if (pa < pb) return -1;
    if (pa > pb) return 1;
    return a.id - b.id;
  });

  const entries = await prisma.catalogEntry.findMany({
    where: { userId },
    select: { caseId: true, itemId: true, filledAt: true },
  });
  const filledMap = new Map();
  for (const e of entries) {
    const key = `${e.caseId}:${e.itemId}`;
    filledMap.set(key, e.filledAt);
  }

  const chapters = cases.map((c) => {
    const slots = c.items.map((ci) => {
      const filledAt = filledMap.get(`${c.id}:${ci.itemId}`) || null;
      return {
        itemId: ci.item.id,
        name: ci.item.name,
        imageUrl: ci.item.imageUrl,
        rarity: ci.item.rarity,
        marketValue: ci.item.marketValue,
        dropRate: ci.dropRate,
        filled: Boolean(filledAt),
        filledAt,
      };
    });
    const filled = slots.filter((s) => s.filled).length;
    const total = slots.length;
    const completed = total > 0 && filled === total;
    return {
      caseId: c.id,
      name: c.name,
      imageUrl: c.imageUrl,
      price: c.price,
      filled,
      total,
      completed,
      slots,
    };
  });

  const luckPercent = chapters.filter((ch) => ch.completed).length;
  return { chapters, luckPercent, completedChapters: luckPercent };
}

/** Compact list of completed case chapters for profile badges. */
export async function getCompletedCatalogBadges(userId) {
  const { chapters, luckPercent } = await getCatalogState(userId);
  return {
    luckPercent,
    completed: chapters
      .filter((ch) => ch.completed)
      .map((ch) => ({
        caseId: ch.caseId,
        name: ch.name,
        imageUrl: ch.imageUrl || '',
      })),
  };
}

/** Format case items with optional catalog luck applied to displayed chances. */
export function formatCaseItemsWithLuck(items, luckPercent = 0) {
  const base = items.map((i) => ({ ...i, dropRate: i.dropRate }));
  const boosted = applyCatalogLuck(base, luckPercent);
  return normalizeRates(boosted).map(({ dropRate, chance, ...rest }) => ({
    ...rest,
    dropRate,
    chance: Number(chance.toFixed(4)),
  }));
}
