import {getApps, initializeApp} from "firebase-admin/app";
import {getFirestore} from "firebase-admin/firestore";
import * as logger from "firebase-functions/logger";
import {HttpsError, onCall} from "firebase-functions/v2/https";

const adminApp = getApps().length > 0 ? getApps()[0] : initializeApp();
const db = getFirestore(adminApp);

type UserDocument = {
  expoPushTokens?: string[];
};

type ExpoPushMessage = {
  to: string;
  title: string;
  body: string;
  data?: Record<string, unknown>;
};

type ExpoPushResponse = {
  data?: unknown;
  errors?: unknown[];
};

function isExpoPushToken(token: string) {
  return token.startsWith("ExponentPushToken[") ||
    token.startsWith("ExpoPushToken[");
}

/**
 * Sends one or more push notifications through the Expo Push API.
 * @param {ExpoPushMessage[]} messages Notification payloads to send.
 * @return {Promise<ExpoPushResponse>} Expo push API response payload.
 */
async function sendExpoPushNotification(messages: ExpoPushMessage[]) {
  const response = await fetch("https://exp.host/--/api/v2/push/send", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Accept": "application/json",
      "Accept-Encoding": "gzip, deflate",
    },
    body: JSON.stringify(messages),
  });

  if (!response.ok) {
    throw new HttpsError(
      "internal",
      `Expo push API returned ${response.status}.`
    );
  }

  return (await response.json()) as ExpoPushResponse;
}

export const sendTestPushNotification = onCall(async (request) => {
  if (!request.auth?.uid) {
    throw new HttpsError("unauthenticated", "Authentication is required.");
  }

  const userSnapshot = await db.doc(`users/${request.auth.uid}`).get();
  const user = userSnapshot.data() as UserDocument | undefined;
  const expoPushTokens = (user?.expoPushTokens ?? []).filter(isExpoPushToken);

  if (expoPushTokens.length === 0) {
    throw new HttpsError(
      "failed-precondition",
      "No Expo push tokens found for this user."
    );
  }

  const result = await sendExpoPushNotification(
    expoPushTokens.map((token) => ({
      to: token,
      title: "Notification de test",
      body: "Le flux Expo Notifications est correctement configure.",
      data: {
        type: "test",
        sentAt: new Date(),
      },
    }))
  );

  if (result.errors?.length) {
    logger.error("Expo push API returned errors.", {errors: result.errors});
    throw new HttpsError("internal", "Expo push API returned errors.");
  }

  logger.info("Test push notification sent.", {
    uid: request.auth.uid,
    response: result.data,
  });

  return {success: true};
});
