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

// The four global underride rules that govern "all messages" notifications.
// They are written in lockstep behind one consolidated control so a single
// "Default for new chats" choice fans out to DM / encrypted DM / rooms /
// encrypted rooms. NEVER drop a ruleId here — a missing one silently stops
// notifying (e.g. encrypted rooms) for that surface.
const ALL_MESSAGE_RULES: {
  ruleId: RuleId.DM | RuleId.EncryptedDM | RuleId.Message | RuleId.EncryptedMessage;
  encrypted: boolean;
  oneToOne: boolean;
}[] = [
  { ruleId: RuleId.DM, encrypted: false, oneToOne: true },
  { ruleId: RuleId.EncryptedDM, encrypted: true, oneToOne: true },
  { ruleId: RuleId.Message, encrypted: false, oneToOne: false },
  { ruleId: RuleId.EncryptedMessage, encrypted: true, oneToOne: false },
];

const getAllMessageDefaultRule = (
  ruleId: RuleId,
  encrypted: boolean,
  oneToOne: boolean
): PushRuleData => {
  const conditions: PushRuleCondition[] = [];
  if (oneToOne)
    conditions.push({
      kind: ConditionKind.RoomMemberCount,
      is: '2',
    });
  conditions.push({
    kind: ConditionKind.EventMatch,
    key: 'type',
    pattern: encrypted ? 'm.room.encrypted' : 'm.room.message',
  });

  return {
    kind: PushRuleKind.Underride,
    pushRule: {
      rule_id: ruleId,
      default: true,
      enabled: true,
      conditions,
      actions: getNotificationModeActions(NotificationMode.NotifyLoud),
    },
  };
};

function AllMessagesModeSwitcher({ pushRules }: { pushRules: IPushRules }) {
  const mx = useMatrixClient();
  const getModeActions = useNotificationModeActions();

  // The displayed mode is the loudest across all four rules, with each rule's
  // mode factoring in whether the server has it disabled (so a disabled rule
  // reads OFF rather than echoing stale loud actions).
  const selectedMode = useMemo(
    () =>
      loudestNotificationMode(
        ALL_MESSAGE_RULES.map(({ ruleId, encrypted, oneToOne }) => {
          const { pushRule } =
            getPushRule(pushRules, ruleId) ?? getAllMessageDefaultRule(ruleId, encrypted, oneToOne);
          return getNotificationModeFromRule(pushRule);
        })
      ),
    [pushRules]
  );

  const handleChange = useCallback(
    async (mode: NotificationMode) => {
      const actions = getModeActions(mode);
      // Fan the choice across ALL four underride rules so encrypted rooms never
      // silently fall out of sync. Also (re-)enable each rule: a server-disabled
      // rule otherwise ignores the new actions and stays silent.
      await Promise.all(
        ALL_MESSAGE_RULES.flatMap(({ ruleId, encrypted, oneToOne }) => {
          const { kind } =
            getPushRule(pushRules, ruleId) ?? getAllMessageDefaultRule(ruleId, encrypted, oneToOne);
          return [
            mx.setPushRuleEnabled('global', kind, ruleId, true),
            mx.setPushRuleActions('global', kind, ruleId, actions),
          ];
        })
      );
    },
    [mx, getModeActions, pushRules]
  );

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

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

  return (
    <SettingsSection label={t('Settings.all_messages')}>
      <SettingTile
        title={t('Settings.default_for_new_chats')}
        description={t('Settings.default_for_new_chats_desc')}
        after={<AllMessagesModeSwitcher pushRules={pushRules} />}
      />
    </SettingsSection>
  );
}
