import { db, shoppingLists, shoppingListMembers, eq } from "@epicure/db";
import { sendPushNotification } from "@/lib/push";
import { getMessages, formatMessage } from "@/lib/i18n/server";
import { isNotificationCategoryEnabled } from "@/lib/notification-prefs";

// formatMessage() is a plain {key} interpolator, not ICU-plural-aware, so
// pluralization has to be resolved to a plain string before it's passed in.
function itemsCountLabel(count: number, locale: string | null | undefined): string {
  if (locale === "fr") return count === 1 ? "1 article" : `${count} articles`;
  return count === 1 ? "1 item" : `${count} items`;
}

/** Other people with access to a shared shopping list — owner + members, excluding the actor. */
async function getOtherRecipients(listId: string, actorId: string) {
  const list = await db.query.shoppingLists.findFirst({
    where: eq(shoppingLists.id, listId),
    columns: { userId: true },
  });
  const members = await db.query.shoppingListMembers.findMany({
    where: eq(shoppingListMembers.listId, listId),
    columns: { userId: true },
  });

  const recipientIds = new Set<string>();
  if (list?.userId) recipientIds.add(list.userId);
  for (const m of members) recipientIds.add(m.userId);
  recipientIds.delete(actorId);
  if (recipientIds.size === 0) return [];

  return db.query.users.findMany({
    where: (t, { inArray }) => inArray(t.id, [...recipientIds]),
    columns: { id: true, locale: true },
  });
}

/**
 * Push-notifies every other member of a shared shopping list about an item
 * change. No-op for solo (unshared) lists — getOtherRecipients returns empty.
 * Deliberately skips the `notifications` table (no in-app entry) since this
 * is meant as a lightweight real-time "someone's shopping too" ping, same
 * pattern as the leftover-expiry cron — not a persistent notification-center
 * event.
 */
export async function notifyShoppingListMembers(
  listId: string,
  actorId: string,
  // null actorName = an anonymous edit via a public-editable share link — no
  // per-visitor identity exists, so this is rendered as a localized generic
  // label (per recipient's own locale) rather than a real name.
  actorName: string | null,
  event: { type: "checked" | "itemsAdded"; itemName?: string; count?: number; listName: string }
): Promise<void> {
  const recipients = await getOtherRecipients(listId, actorId);
  if (recipients.length === 0) return;

  const url = `/shopping-lists/${listId}`;

  await Promise.all(
    recipients.map(async (recipient) => {
      if (!(await isNotificationCategoryEnabled(recipient.id, "shoppingList"))) return;

      const messages = getMessages(recipient.locale);
      const displayName = actorName ?? messages.publicShoppingList.anonymousEditor;
      const title = messages.notifications.pushTitle.shoppingListUpdate;
      const body =
        event.type === "checked"
          ? formatMessage(messages.notifications.detail.shoppingListChecked, {
              name: displayName,
              item: event.itemName ?? "",
              list: event.listName,
            })
          : formatMessage(messages.notifications.detail.shoppingListItemsAdded, {
              name: displayName,
              countLabel: itemsCountLabel(event.count ?? 1, recipient.locale),
              list: event.listName,
            });

      await sendPushNotification(recipient.id, { title, body, url }).catch((err) => {
        console.error("[shopping-list-notify] push failed", err);
      });
    })
  );
}

