import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { ConditionKind, IPushRules, PushRuleKind, RuleId } from 'matrix-js-sdk';
import { useAccountData } from '../../../hooks/useAccountData';
import { AccountDataEvent } from '../../../../types/matrix/accountData';
import { SettingTile } from '../../../components/setting-tile';
import { SettingsSection } from '../SettingsSection';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useAuthedUserId } from '../../../hooks/useAuthedUserId';
import { getMxIdLocalPart } from '../../../utils/matrix';
import { getPushRule, makePushRuleData, PushRuleData } from '../../../hooks/usePushRule';
import {
  getNotificationModeActions,
  getNotificationModeFromRule,
  loudestNotificationMode,
  NotificationMode,
  NotificationModeOptions,
  useNotificationModeActions,
} from '../../../hooks/useNotificationMode';
import { NotificationModeSwitcher } from './NotificationModeSwitcher';

const NOTIFY_MODE_OPS: NotificationModeOptions = {
  highlight: true,
};
const getDefaultIsUserMention = (userId: string): PushRuleData =>
  makePushRuleData(
    PushRuleKind.Override,
    RuleId.IsUserMention,
    getNotificationModeActions(NotificationMode.NotifyLoud, { highlight: true }),
    [
      {
        kind: ConditionKind.EventPropertyContains,
        key: 'content.m\\.mentions.user_ids',
        value: userId,
      },
    ]
  );

const DefaultContainsDisplayName = makePushRuleData(
  PushRuleKind.Override,
  RuleId.ContainsDisplayName,
  getNotificationModeActions(NotificationMode.NotifyLoud, { highlight: true }),
  [
    {
      kind: ConditionKind.ContainsDisplayName,
    },
  ]
);

const getDefaultContainsUsername = (username: string) =>
  makePushRuleData(
    PushRuleKind.ContentSpecific,
    RuleId.ContainsUserName,
    getNotificationModeActions(NotificationMode.NotifyLoud, { highlight: true }),
    undefined,
    username
  );

const DefaultIsRoomMention = makePushRuleData(
  PushRuleKind.Override,
  RuleId.IsRoomMention,
  getNotificationModeActions(NotificationMode.Notify, { highlight: true }),
  [
    {
      kind: ConditionKind.EventPropertyIs,
      key: 'content.m\\.mentions.room',
      value: true,
    },
    {
      kind: ConditionKind.SenderNotificationPermission,
      key: 'room',
    },
  ]
);

const DefaultAtRoomNotification = makePushRuleData(
  PushRuleKind.Override,
  RuleId.AtRoomNotification,
  getNotificationModeActions(NotificationMode.Notify, { highlight: true }),
  [
    {
      kind: ConditionKind.EventMatch,
      key: 'content.body',
      pattern: '@room',
    },
    {
      kind: ConditionKind.SenderNotificationPermission,
      key: 'room',
    },
  ]
);

type RuleGroupEntry = {
  ruleId: RuleId;
  getDefault: () => PushRuleData;
};

// One consolidated "Mentions" control fans across the three personal-mention
// rules; one "@room" control fans across the two room-wide announcement rules.
// Every ruleId is still written behind the grouped control — a dropped ruleId
// silently breaks mention notifications.
type MentionGroupSwitcherProps = {
  pushRules: IPushRules;
  group: RuleGroupEntry[];
};
function MentionGroupSwitcher({ pushRules, group }: MentionGroupSwitcherProps) {
  const mx = useMatrixClient();
  const getModeActions = useNotificationModeActions(NOTIFY_MODE_OPS);

  // Displayed mode is the loudest across the group, each rule factoring in
  // whether the server has it disabled.
  const selectedMode = useMemo(
    () =>
      loudestNotificationMode(
        group.map(({ ruleId, getDefault }) => {
          const { pushRule } = getPushRule(pushRules, ruleId) ?? getDefault();
          return getNotificationModeFromRule(pushRule);
        })
      ),
    [pushRules, group]
  );

  const handleChange = useCallback(
    async (mode: NotificationMode) => {
      const actions = getModeActions(mode);
      // Write actions AND (re-)enable each rule in lockstep — a server-disabled
      // mention rule otherwise ignores the new actions and stays silent.
      await Promise.all(
        group.flatMap(({ ruleId, getDefault }) => {
          const { kind } = getPushRule(pushRules, ruleId) ?? getDefault();
          return [
            mx.setPushRuleEnabled('global', kind, ruleId, true),
            mx.setPushRuleActions('global', kind, ruleId, actions),
          ];
        })
      );
    },
    [mx, getModeActions, pushRules, group]
  );

  return <NotificationModeSwitcher selectedMode={selectedMode} onChange={handleChange} />;
}

export function SpecialMessagesNotifications() {
  const { t } = useTranslation();
  const userId = useAuthedUserId();
  const pushRulesEvt = useAccountData(AccountDataEvent.PushRules);
  const pushRules = useMemo(
    () => pushRulesEvt?.getContent<IPushRules>() ?? { global: {} },
    [pushRulesEvt]
  );

  const username = getMxIdLocalPart(userId) ?? userId;

  const mentionGroup = useMemo<RuleGroupEntry[]>(
    () => [
      { ruleId: RuleId.IsUserMention, getDefault: () => getDefaultIsUserMention(userId) },
      { ruleId: RuleId.ContainsDisplayName, getDefault: () => DefaultContainsDisplayName },
      { ruleId: RuleId.ContainsUserName, getDefault: () => getDefaultContainsUsername(username) },
    ],
    [userId, username]
  );

  const roomGroup = useMemo<RuleGroupEntry[]>(
    () => [
      { ruleId: RuleId.IsRoomMention, getDefault: () => DefaultIsRoomMention },
      { ruleId: RuleId.AtRoomNotification, getDefault: () => DefaultAtRoomNotification },
    ],
    []
  );

  return (
    <SettingsSection label={t('Settings.special_messages')}>
      <SettingTile
        title={t('Settings.notify_on_mention')}
        description={t('Settings.notify_on_mention_desc')}
        after={<MentionGroupSwitcher pushRules={pushRules} group={mentionGroup} />}
      />
      <SettingTile
        title={t('Settings.room_announcements')}
        description={t('Settings.room_announcements_desc')}
        after={<MentionGroupSwitcher pushRules={pushRules} group={roomGroup} />}
      />
    </SettingsSection>
  );
}
