import { useEffect, useState } from 'react';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { Room, RoomEvent, RoomStateEvent } from 'matrix-js-sdk';
import { StateEvent } from '../../types/matrix/room';
import { useStateEvent } from './useStateEvent';

// matrix-js-sdk's `Room.name` for a DM with no explicit `m.room.name`
// event falls back to a disambiguated peer label and, in many SDK
// versions, suffixes the peer's MXID as `Display Name (@id:server)` —
// either always or whenever a display-name collision is suspected. That
// produces the «Alex (@test6:vojo.chat)» pattern users see in the DM
// list, chat header and room intro even though no two members share
// the same display name. The handle is already shown as a separate
// subline in the chat header, so the parenthetical is pure noise.
//
// Workaround: when the room has no explicit `m.room.name` and is a 1:1
// with exactly one other joined/invited member, take the peer's raw
// display name directly (`RoomMember.rawDisplayName` is the value from
// `m.room.member.content.displayname` without SDK disambiguation).
// Fall back to the local-part of the MXID, then the bare MXID. Group
// rooms and rooms with an explicit name keep the SDK output.
const resolveRoomName = (room: Room): string => {
  const explicit = room.currentState.getStateEvents(StateEvent.RoomName, '');
  if (explicit) return room.name;

  const myUserId = room.client.getUserId();
  if (myUserId) {
    const peers = room
      .getMembersWithMembership('join')
      .concat(room.getMembersWithMembership('invite'))
      .filter((m) => m.userId !== myUserId);
    if (peers.length === 1) {
      const peer = peers[0];
      if (peer.rawDisplayName && peer.rawDisplayName.trim()) {
        return peer.rawDisplayName.trim();
      }
      const local = peer.userId.startsWith('@') ? peer.userId.slice(1).split(':')[0] : peer.userId;
      return local || peer.userId;
    }
  }

  return room.name;
};

export const useRoomAvatar = (room: Room, dm?: boolean): string | undefined => {
  const avatarEvent = useStateEvent(room, StateEvent.RoomAvatar);

  if (dm) {
    return room.getAvatarFallbackMember()?.getMxcAvatarUrl();
  }
  const content = avatarEvent?.getContent();
  const avatarMxc = content && typeof content.url === 'string' ? content.url : undefined;

  return avatarMxc;
};

export const useRoomName = (room: Room): string => {
  const [name, setName] = useState(() => resolveRoomName(room));

  useEffect(() => {
    setName(resolveRoomName(room));

    // No-arg form keeps the same callback assignable to both RoomEvent.Name
    // (event handler is `(room: Room) => void`) and RoomStateEvent.Members
    // (`(event, state, member) => void`); we ignore the args either way.
    const recompute = () => {
      setName(resolveRoomName(room));
    };
    // RoomEvent.Name fires when m.room.name changes;
    // RoomStateEvent.Members fires on every m.room.member event,
    // covering both display-name updates of the peer and membership
    // flips that could change which member counts as the DM peer.
    room.on(RoomEvent.Name, recompute);
    room.currentState.on(RoomStateEvent.Members, recompute);
    return () => {
      room.removeListener(RoomEvent.Name, recompute);
      room.currentState.removeListener(RoomStateEvent.Members, recompute);
    };
  }, [room]);

  return name;
};

export const useRoomTopic = (room: Room): string | undefined => {
  const topicEvent = useStateEvent(room, StateEvent.RoomTopic);

  const content = topicEvent?.getContent();
  const topic = content && typeof content.topic === 'string' ? content.topic : undefined;

  return topic;
};

export const useRoomJoinRule = (room: Room): RoomJoinRulesEventContent | undefined => {
  const mEvent = useStateEvent(room, StateEvent.RoomJoinRules);
  const joinRuleContent = mEvent?.getContent<RoomJoinRulesEventContent>();
  return joinRuleContent;
};
