import Constants from 'expo-constants';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import { httpsCallable } from 'firebase/functions';
import {
  arrayUnion,
  serverTimestamp,
  setDoc,
} from 'firebase/firestore';
import { Platform } from 'react-native';

import { functions } from '@/lib/firebase';
import { userDocRef } from '@/lib/firestore';

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldPlaySound: true,
    shouldSetBadge: true,
    shouldShowBanner: true,
    shouldShowList: true,
  }),
});

type NotificationSettingsPayload = {
  expoPushToken: string | null;
  pushEnabled: boolean;
  platform: 'android' | 'ios'  | null;
};

type SendTestPushNotificationResult = {
  success: boolean;
};

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

async function saveExpoPushToken(
  uid: string,
  expoPushToken: string
) {
  if (!isExpoPushToken(expoPushToken)) {
    return;
  }

  await setDoc(
    userDocRef(uid),
    {
      expoPushTokens: arrayUnion(expoPushToken),
      updatedAt: serverTimestamp(),
    },
    { merge: true }
  );
}

function getProjectId() {
  const projectId =
    Constants.expoConfig?.extra?.eas?.projectId ?? Constants.easConfig?.projectId;

  if (!projectId) {
    throw new Error('EAS projectId introuvable pour Expo Notifications.');
  }

  return projectId;
}

export async function registerForPushNotifications(uid: string) {
  if (Platform.OS === 'web') {
    return null;
  }

  if (!Device.isDevice) {
    return null;
  }

  const existingPermissions = await Notifications.getPermissionsAsync();
  let finalStatus = existingPermissions.status;

  if (finalStatus !== 'granted') {
    const requestedPermissions = await Notifications.requestPermissionsAsync();
    finalStatus = requestedPermissions.status;
  }

  if (finalStatus !== 'granted') {
    return null;
  }


  const expoPushToken = (
    await Notifications.getExpoPushTokenAsync({
      projectId: getProjectId(),
    })
  ).data;

  await saveExpoPushToken(uid, expoPushToken);

  return expoPushToken;
}

export function subscribeToPushTokenRefresh(uid: string) {
  if (Platform.OS === 'web') {
    return () => undefined;
  }

  const subscription = Notifications.addPushTokenListener((token) => {
    if (!isExpoPushToken(token.data)) {
      return;
    }

    void saveExpoPushToken(uid, token.data);
  });

  return () => {
    subscription.remove();
  };
}

export async function sendTestPushNotification() {
  const callable = httpsCallable<undefined, SendTestPushNotificationResult>(
    functions,
    'sendTestPushNotification'
  );

  await callable();
}
