import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { Room } from 'matrix-js-sdk';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { allInvitesAtom } from '../../../state/room-list/inviteList';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { bannedInRooms, getMemberDisplayName, getStateEvent } from '../../../utils/room';
import { testBadWords } from '../../../plugins/bad-words';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
import { getMxIdLocalPart } from '../../../utils/matrix';
import { StateEvent } from '../../../../types/matrix/room';
import { useBotPresets } from '../../../features/bots/catalog';
import { isFreshBridgeGhostInvite } from '../../../features/bots/room';

export type DirectInviteEntry = {
  room: Room;
  roomId: string;
  ts: number;
  isSpam: boolean;
};

const getInviteTs = (room: Room, myUserId: string): number => {
  const me = room.getMember(myUserId);
  return me?.events.member?.getTs() ?? 0;
};

const inviteHasBadWords = (room: Room, myUserId: string): boolean => {
  const roomName = room.name || '';
  const topic =
    getStateEvent(room, StateEvent.RoomTopic)?.getContent<{ topic?: string }>()?.topic ?? '';
  const me = room.getMember(myUserId);
  const memberEvent = me?.events.member;
  const senderId = memberEvent?.getSender() ?? '';
  const senderName = senderId
    ? getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId
    : '';
  const reasonContent = memberEvent?.getContent();
  const reason =
    reasonContent && 'reason' in reasonContent && typeof reasonContent.reason === 'string'
      ? reasonContent.reason
      : '';

  return (
    testBadWords(roomName) ||
    testBadWords(topic) ||
    testBadWords(senderName) ||
    testBadWords(senderId) ||
    testBadWords(reason)
  );
};

export const useDirectInvites = (): DirectInviteEntry[] => {
  const mx = useMatrixClient();
  const inviteIds = useAtomValue(allInvitesAtom);
  const allRooms = useAtomValue(allRoomsAtom);
  const [spamFilterEnabled] = useSetting(settingsAtom, 'inviteSpamFilter');
  const bots = useBotPresets();
  const myUserId = mx.getSafeUserId();

  return useMemo(() => {
    // Cache `bannedInRooms` per senderId — a single inviter MXID can be the
    // sender of multiple invite rooms, and bannedInRooms iterates every
    // joined room. Without the cache we'd be O(invites × joinedRooms) on
    // every recompute.
    const bannedCache = new Map<string, boolean>();
    const isSenderBanned = (senderId: string): boolean => {
      const cached = bannedCache.get(senderId);
      if (cached !== undefined) return cached;
      const banned = bannedInRooms(mx, allRooms, senderId);
      bannedCache.set(senderId, banned);
      return banned;
    };

    const out: DirectInviteEntry[] = [];
    inviteIds.forEach((roomId) => {
      const room = mx.getRoom(roomId);
      if (!room) return;
      // Bridge portal invites get auto-accepted by the bridge's double
      // puppet within seconds of arriving — rendering Accept/Decline rows
      // that vanish on their own reads as flicker, especially during the
      // post-login burst when the bridge syncs dozens of chats. Fresh ghost
      // invites are suppressed; stale ones (auto-accept broken) fall
      // through and stay actionable.
      if (isFreshBridgeGhostInvite(mx, room, bots)) return;
      const me = room.getMember(myUserId);
      const senderId = me?.events.member?.getSender() ?? '';
      // Moderation signal — sender is banned in a room we share. This is a
      // server-side moderator's verdict, not a personal preference, so it's
      // ALWAYS classified as spam regardless of the user-facing toggle.
      const moderationSpam = !!senderId && isSenderBanned(senderId);
      // Lexical signal — bad-words match in the room name / topic / sender /
      // reason. This is heuristic and noisy, so it's gated on the user-facing
      // `inviteSpamFilter` toggle for users who'd rather see everything raw.
      const lexicalSpam = spamFilterEnabled && inviteHasBadWords(room, myUserId);
      const isSpam = moderationSpam || lexicalSpam;
      out.push({
        room,
        roomId,
        ts: getInviteTs(room, myUserId),
        isSpam,
      });
    });
    out.sort((a, b) => b.ts - a.ts);
    return out;
  }, [mx, inviteIds, allRooms, myUserId, spamFilterEnabled, bots]);
};
