import { captureException } from '@sentry/browser';
import {
  deleteToken,
  getMessaging,
  getToken,
  isSupported,
  type MessagePayload,
  type Messaging,
  onMessage as onMessageFCM,
} from 'firebase/messaging';
import qs from 'qs';

import api from 'src/config/api';
import { messaging } from 'src/config/firebase';
import {
  type FeedNotificationConfigurationMode,
  type GetChannelsPreferencesResponse,
  type GetFeedNotificationConfigurationResponse,
  type GetModulesPreferencesResponse,
  type NotificationsStatsData,
} from './types';

import {
  type NotificationChannelCodes,
  type NotificationModuleCodes,
  type NotificationTypeCodes,
} from './constants';

export const registerNotificationToken = (token: string | null = null) =>
  api.post('/notifications/tokens', { token });

export const removeNotificationToken = (token: string, accessToken: string) =>
  api.delete('/notifications/tokens', {
    data: { token },
    headers: { Authorization: `Bearer ${accessToken}` },
  });

const getFBToken = (
  m: Messaging | undefined,
  serviceWorkerRegistration: ServiceWorkerRegistration,
): Promise<string | null> => {
  if (!m) {
    throw new Error('Firebase Messaging is not available');
  }

  let sw: ServiceWorker | undefined;

  if (serviceWorkerRegistration.installing) {
    sw = serviceWorkerRegistration.installing;
  } else if (serviceWorkerRegistration.waiting) {
    sw = serviceWorkerRegistration.waiting;
  } else if (serviceWorkerRegistration.active) {
    sw = serviceWorkerRegistration.active;
  }

  if (!sw) {
    const err = new Error('There is no available Service Worker');
    captureException(err, {
      extra: {
        permission: window?.Notification || null,
      },
    });
    throw err;
  }

  const tryGetToken = async () => {
    try {
      return await getToken(m, {
        vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY,
        serviceWorkerRegistration,
      });
    } catch (err) {
      captureException(err, {
        extra: {
          permission: window?.Notification || null,
          swState: sw?.state,
        },
      });
      return null;
    }
  };

  if (sw.state === 'activated') {
    return tryGetToken();
  }

  return new Promise<string | null>(resolve => {
    const onStateChange = async (e: Event) => {
      const target = e.target as ServiceWorker;
      if (target.state === 'activated') {
        const token = await tryGetToken();
        resolve(token);
        sw?.removeEventListener('statechange', onStateChange);
      }
    };
    sw.addEventListener('statechange', onStateChange);
  });
};

export const getMessagingToken = async () => {
  // Check if service worker is already registered to avoid re-registration
  // which causes Firebase token subscription/update failures
  let registration = await navigator.serviceWorker.getRegistration(
    '/firebase-cloud-messaging-push-scope',
  );

  if (!registration) {
    registration = await navigator.serviceWorker.register(
      '/firebase-messaging-sw.js',
      { scope: '/firebase-cloud-messaging-push-scope' },
    );
  }

  return getFBToken(messaging, registration);
};

export const deleteMessagingToken = async () => {
  try {
    const newMessaging = getMessaging();
    // Check if service worker is available before attempting to delete token
    // If not available, we will not be able to delete the token
    // This prevents token-unsubscribe-failed errors when SW is unavailable
    const registration = await navigator.serviceWorker.getRegistration(
      '/firebase-cloud-messaging-push-scope',
    );
    if (!registration || !registration.active) {
      return;
    }
    return await deleteToken(newMessaging);
  } catch (_err) {
    // Silently handle errors - token might already be deleted or SW unavailable
    return;
  }
};

export const isMessagingSupported = () => isSupported();

export const onMessage = (callback: (payload: MessagePayload) => void) => {
  const newMessaging = getMessaging();
  onMessageFCM(newMessaging, callback);
};

export const checkAndRequestPermission = async (): Promise<boolean> => {
  if (!window?.Notification) return false;
  if (window.Notification.permission === 'default') {
    const permission = await window.Notification.requestPermission();

    return permission === 'granted';
  }
  return window.Notification.permission === 'granted';
};

export const getNotifications = (
  showUnread: boolean | undefined = undefined,
  cursor: string = '',
  limit: number = 5,
) =>
  api.get('notification-center/notifications', {
    params: {
      cursor: cursor || undefined,
      limit,
      isRead: showUnread ? false : undefined,
    },
    paramsSerializer: params => qs.stringify(params),
  });

export const markAsRead = (id: string, isRead: boolean) =>
  api.patch(`/notification-center/notification/${id}`, { isRead });

export const getCountNotifications = () =>
  api.get<NotificationsStatsData>('/notification-center/notifications/stats');

export const readAllNotifications = () =>
  api.put('/notification-center/notifications/read-all');

export const clearNotificationsBuble = () =>
  api.put('/notification-center/notifications/clear-bubble');

export const getChannelsPreferences = () =>
  api.get<GetChannelsPreferencesResponse>('/notification-preferences/channels');

export const updateChannelsPreferences = (
  channelCode: NotificationChannelCodes,
  moduleCode: NotificationModuleCodes,
  enabled: boolean,
) =>
  api.post(`/notification-preferences/toggle`, {
    moduleCode,
    channelCode,
    enabled,
  });

export const getModulesPreferences = () =>
  api.get<GetModulesPreferencesResponse>('/notification-preferences/modules');

export const getDisabledModules = () =>
  api.get<NotificationModuleCodes[]>(
    '/notification-preferences/modules/disabled',
  );

export const getFeedNotificationConfiguration = (
  mode: FeedNotificationConfigurationMode,
) =>
  api.get<GetFeedNotificationConfigurationResponse>(
    '/posts/notification-configuration',
    {
      params: {
        modes: mode,
      },
    },
  );

export const updateFeedNotificationConfiguration = (
  mode: FeedNotificationConfigurationMode,
  enabled: boolean,
) =>
  api.put('/posts/notification-configuration', {
    mode,
    enabled,
  });

export const markNotificationAsRead = (
  module: NotificationModuleCodes,
  type: NotificationTypeCodes,
  params?: {
    refId?: string;
    groupId?: string;
  },
) =>
  api.patch(`/notification-center/notifications/mark-notification-read`, {
    module,
    type,
    ...(params ?? {}),
  });
