import { Expo } from "expo-server-sdk";

import { Notification } from "./notification.model.js";
import { User } from "../users/user.model.js";

const expo = new Expo();

function toStringId(value) {
  if (!value) return "";
  return typeof value === "string" ? value : value.toString();
}

async function sendPushToUsers(userIds, { title, body, data }) {
  const uniqueIds = Array.from(new Set((userIds || []).map(toStringId))).filter(
    Boolean
  );
  if (uniqueIds.length === 0) return;

  const users = await User.find({ _id: { $in: uniqueIds } }, "expoPushTokens")
    .lean()
    .catch(() => []);

  const tokens = users
    .flatMap((u) => u.expoPushTokens || [])
    .filter((t) => Expo.isExpoPushToken(t));

  if (tokens.length === 0) return;

  const messages = tokens.map((token) => ({
    to: token,
    sound: "default",
    title,
    body,
    data: data || undefined,
  }));

  const chunks = expo.chunkPushNotifications(messages);
  for (const chunk of chunks) {
    try {
      await expo.sendPushNotificationsAsync(chunk);
    } catch {
      // Ignore push failures; in-app notifications still work.
    }
  }
}

export async function createAndSendNotifications({
  userIds,
  type,
  title,
  body,
  data,
  createdBy,
  alsoPush = true,
}) {
  const uniqueIds = Array.from(new Set((userIds || []).map(toStringId))).filter(
    Boolean
  );
  if (uniqueIds.length === 0) return { created: 0 };

  const docs = uniqueIds.map((userId) => ({
    userId,
    type: type || "other",
    title,
    body,
    data,
    createdBy,
  }));

  await Notification.insertMany(docs);

  if (alsoPush) {
    await sendPushToUsers(uniqueIds, { title, body, data });
  }

  return { created: docs.length };
}
