import { useCallback, useEffect, useState } from 'react';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { ClientEvent, MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk';
import { StateEvent } from '../../types/matrix/room';
import { useStateEvent } from './useStateEvent';
import { useMatrixClient } from './useMatrixClient';
import { getAccountData } from '../utils/accountData';

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(room.name);

  useEffect(() => {
    setName(room.name);

    const handleRoomNameChange: RoomEventHandlerMap[RoomEvent.Name] = () => {
      setName(room.name);
    };
    room.on(RoomEvent.Name, handleRoomNameChange);
    return () => {
      room.removeListener(RoomEvent.Name, handleRoomNameChange);
    };
  }, [room]);

  return name;
};

export const LOCAL_ROOM_NAMES_KEY = 'io.lotus.room_names';

export type LocalRoomNamesContent = { rooms: Record<string, string> };

export function getLocalRoomNamesContent(
  mx: ReturnType<typeof useMatrixClient>,
): LocalRoomNamesContent {
  const raw: unknown = getAccountData<unknown>(mx, LOCAL_ROOM_NAMES_KEY);
  if (
    raw &&
    typeof raw === 'object' &&
    'rooms' in raw &&
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    typeof (raw as any).rooms === 'object'
  ) {
    return raw as LocalRoomNamesContent;
  }
  return { rooms: {} };
}

export const useLocalRoomName = (room: Room): string => {
  const mx = useMatrixClient();

  const getLocalName = useCallback((): string => {
    const content = getLocalRoomNamesContent(mx);
    return content.rooms[room.roomId] ?? room.name;
  }, [mx, room]);

  const [name, setName] = useState(getLocalName);

  useEffect(() => {
    setName(getLocalName());

    const handleAccountData = (event: MatrixEvent) => {
      if (event.getType() !== LOCAL_ROOM_NAMES_KEY) return;
      setName(getLocalName());
    };
    mx.on(ClientEvent.AccountData, handleAccountData);

    const handleRoomNameChange: RoomEventHandlerMap[RoomEvent.Name] = () => {
      setName(getLocalName());
    };
    room.on(RoomEvent.Name, handleRoomNameChange);

    return () => {
      mx.removeListener(ClientEvent.AccountData, handleAccountData);
      room.removeListener(RoomEvent.Name, handleRoomNameChange);
    };
  }, [mx, room, getLocalName]);

  return name;
};

export const useHasLocalRoomName = (roomId: string): boolean => {
  const mx = useMatrixClient();

  const check = useCallback((): boolean => {
    const content = getLocalRoomNamesContent(mx);
    return !!content.rooms[roomId];
  }, [mx, roomId]);

  const [hasLocal, setHasLocal] = useState(check);

  useEffect(() => {
    setHasLocal(check());

    const handleAccountData = (event: MatrixEvent) => {
      if (event.getType() !== LOCAL_ROOM_NAMES_KEY) return;
      setHasLocal(check());
    };
    mx.on(ClientEvent.AccountData, handleAccountData);
    return () => {
      mx.removeListener(ClientEvent.AccountData, handleAccountData);
    };
  }, [mx, check]);

  return hasLocal;
};

export type RoomTopicContent = {
  topic: string;
  formatted_body?: string;
  format?: string;
};

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

  const content = topicEvent?.getContent();
  if (!content || typeof content.topic !== 'string') return undefined;

  return {
    topic: content.topic,
    formatted_body: typeof content.formatted_body === 'string' ? content.formatted_body : undefined,
    format: typeof content.format === 'string' ? content.format : undefined,
  };
};

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