import isNil from 'lodash-es/isNil';
import {
  IconBell,
  IconDeviceMobile,
  IconMail,
} from '@material-hu/icons/tabler';

import { type CommunityFeature } from 'src/types/communityFeatures';
import { type LokaliseTFunction } from 'src/utils/i18n';
import { hasAnyPermissions } from 'src/utils/permissions';

import {
  CommunityFeatureGuardedModules,
  NotificationChannelCodes,
  NotificationModuleCodes,
  PermissionGuardedModules,
} from './constants';
import {
  type ChannelPreferences,
  type NotificationChannelConfig,
  type NotificationModuleConfig,
} from './types';

export const getNotificationChannelsConfig = (
  channels: Record<NotificationChannelCodes, boolean>,
  moduleCode: NotificationModuleCodes,
  t: LokaliseTFunction,
): NotificationChannelConfig[] => {
  const channelsConfig: NotificationChannelConfig[] = [
    {
      id: NotificationChannelCodes.PUSH,
      title: t('notification_channels.push_notifications'),
      description: t('notification_channels.push_notifications_description'),
      isActive: channels[NotificationChannelCodes.PUSH],
      disabled: false,
      icon: IconDeviceMobile,
    },
    {
      id: NotificationChannelCodes.BELL,
      title: t('notification_channels.notification_center'),
      description: t('notification_channels.notification_center_description'),
      isActive: channels[NotificationChannelCodes.BELL],
      disabled: false,
      icon: IconBell,
    },
  ];

  if (moduleCode === NotificationModuleCodes.FEED) {
    channelsConfig.push({
      id: NotificationChannelCodes.MAIL,
      title: t('general:email'),
      description: t('post:notify_via_email_description'),
      isActive: Boolean(channels[NotificationChannelCodes.MAIL]),
      disabled: false,
      icon: IconMail,
    });
  }

  return channelsConfig;
};

/**
 * Generates a description text based on channel preferences.
 * Returns a comma-separated list of enabled channel descriptions.
 */
export const getChannelDescription = (
  channelsPreferences: ChannelPreferences,
  moduleCode: NotificationModuleCodes,
  t: LokaliseTFunction,
): string => {
  const enabledChannels: string[] = [];

  if (channelsPreferences[NotificationChannelCodes.BELL]) {
    enabledChannels.push(t('notification_channels.notification_center'));
  }

  if (channelsPreferences[NotificationChannelCodes.PUSH]) {
    enabledChannels.push(t('notification_channels.push_notifications_short'));
  }

  if (
    moduleCode === NotificationModuleCodes.FEED &&
    channelsPreferences[NotificationChannelCodes.MAIL]
  ) {
    enabledChannels.push(t('general:email'));
  }

  return enabledChannels.join(', ');
};

/**
 * Creates a default notification modules configuration object.
 * PUSH/BELL are true and MAIL is false by default unless explicitly set by backend.
 */
const createDefaultModulesConfig = () => {
  const defaultConfig: NotificationModuleConfig = {};

  Object.values(NotificationModuleCodes).forEach(moduleName => {
    defaultConfig[moduleName] = {
      channels: {
        BELL: true,
        PUSH: true,
        MAIL: false,
      },
    };
  });

  return defaultConfig;
};

/**
 * Merges backend module configuration with default configuration.
 * Backend response only contains modules that are explicitly disabled (set to false).
 * This function ensures all modules are present with proper defaults.
 */
export const mergeModulesConfig = (
  serverConfig: NotificationModuleConfig = {},
) => {
  const defaultConfig = createDefaultModulesConfig();

  Object.keys(defaultConfig).forEach(moduleName => {
    const serverModuleConfig = serverConfig[moduleName];

    if (!serverModuleConfig) return;

    const { channels: serverChannels } = serverModuleConfig;
    const { channels: defaultChannels } = defaultConfig[moduleName];

    if (serverChannels.BELL !== undefined) {
      defaultChannels.BELL = serverChannels.BELL;
    }

    if (serverChannels.PUSH !== undefined) {
      defaultChannels.PUSH = serverChannels.PUSH;
    }

    if (serverChannels.MAIL !== undefined) {
      defaultChannels.MAIL = serverChannels.MAIL;
    }
  });

  return defaultConfig;
};

export const nonNullable = <T>(x: T): x is NonNullable<T> =>
  x !== null && x !== undefined;

export const getConfigPageTitleAndDescription = (
  selectedModule: NotificationModuleCodes | undefined,
  t: LokaliseTFunction,
) => {
  if (nonNullable(selectedModule)) {
    return {
      title: t('notification_channels.title'),
      description: t('notification_channels.description'),
    };
  }
  return {
    title: t('apps:notifications'),
    description: t('notifications_modules_description'),
  };
};

export const getEnabledModules = (
  disabledModules: NotificationModuleCodes[],
) => {
  return Object.values(NotificationModuleCodes).filter(
    moduleName => !disabledModules.includes(moduleName),
  );
};

/**
 * Returns true if the module should be visible given the user's permissions
 * and the community's enabled features.
 */
export const isModuleVisible = (
  moduleName: NotificationModuleCodes,
  userPermissions: string[],
  communityFeatures: CommunityFeature[] | null,
) => {
  const requiredPermissions = PermissionGuardedModules[moduleName];
  const hasPermissions =
    !requiredPermissions ||
    hasAnyPermissions(userPermissions, requiredPermissions);

  const requiredFeatures = CommunityFeatureGuardedModules[moduleName];
  const hasFeatures =
    !requiredFeatures ||
    requiredFeatures.every(feature =>
      (communityFeatures ?? []).includes(feature),
    );

  return hasPermissions && hasFeatures;
};

export const hasUnreadNotifications = (
  unreadCount: number | null | undefined,
) => !isNil(unreadCount) && unreadCount > 0;

const NOTIFICATIONS_TITLE_PREFIX_REGEX = /^\(\+?\d+\)\s*/;

/**
 * Formats the notification count for display in the title (max 99, or "+99" when capped).
 */
export const formatNotificationCountForTitle = (
  newSinceLastSeen: number,
  capped: boolean,
): string => {
  if (newSinceLastSeen <= 0) return '';
  const displayCount = Math.min(newSinceLastSeen, 99);
  return capped ? '+99' : String(displayCount);
};

/**
 * Updates document.title and the Badging API with the given notification count.
 * Used when a new notification arrives (e.g. via socket) so the tab title and
 * badge update even when the tab is in background (browser defers React paint).
 * Max display is 99; when capped is true the title shows "+99".
 */
export const updateTitleAndBadgeFromCount = (
  newSinceLastSeen: number,
  capped: boolean = false,
): void => {
  if (newSinceLastSeen < 0) return;

  const baseTitle = document.title.replace(
    NOTIFICATIONS_TITLE_PREFIX_REGEX,
    '',
  );
  const displayText = formatNotificationCountForTitle(newSinceLastSeen, capped);
  document.title =
    newSinceLastSeen > 0 ? `(${displayText}) ${baseTitle}` : baseTitle;

  if (
    'setAppBadge' in navigator &&
    typeof navigator.setAppBadge === 'function'
  ) {
    if (newSinceLastSeen > 0) {
      navigator.setAppBadge(newSinceLastSeen).catch(() => undefined);
    } else {
      navigator.clearAppBadge?.().catch(() => undefined);
    }
  }
};
