import { useCallback, useMemo } from 'react';
import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk';
import { getPowerLevelTag, PowerLevelTags, usePowerLevelTags } from './usePowerLevelTags';
import { IPowerLevels, readPowerLevel } from './usePowerLevels';
import { MemberPowerTag, MemberPowerTagIcon } from '../../types/matrix/room';
import { useRoomCreatorsTag } from './useRoomCreatorsTag';
import { ThemeKind } from './useTheme';
import { accessibleColor } from '../plugins/color';

export type GetMemberPowerTag = (userId: string) => MemberPowerTag;

export const useGetMemberPowerTag = (
  room: Room,
  creators: Set<string>,
  powerLevels: IPowerLevels
) => {
  const creatorsTag = useRoomCreatorsTag();
  const powerLevelTags = usePowerLevelTags(room, powerLevels);

  // Cache the resolved tag BY POWER LEVEL so identical inputs return the
  // identical object reference across renders. This matters for two reasons:
  //   1. Perf — `getPowerLevelTag` falls back to `generateFallbackTagSimple`
  //      (a freshly-allocated `{ name }`) for any power with no defined tag
  //      (the common default-power case). Without this cache, each call
  //      returns a new object, so `memberPowerTag` is a new reference every
  //      render and defeats the `Message` React.memo — every visible timeline
  //      row then re-renders on every scroll/receipt tick (visible jank).
  //   2. Grouping — `useFlattenPowerTagMembers` groups members by `tag !==
  //      prevTag` reference equality; keying the cache on power (not userId)
  //      keeps same-power members sharing one tag object.
  // Reset whenever the tag table changes; `powerLevels` only shifts which
  // power a user resolves to, which simply selects a different cache entry.
  // `powerLevelTags` is the cache's invalidation key (its identity), not read
  // inside the factory — hence the exhaustive-deps disable.
  const tagByPower = useMemo<Map<number, MemberPowerTag>>(
    () => new Map(),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [powerLevelTags]
  );

  const getMemberPowerTag: GetMemberPowerTag = useCallback(
    (userId) => {
      if (creators.has(userId)) {
        return creatorsTag;
      }

      const power = readPowerLevel.user(powerLevels, userId);
      let tag = tagByPower.get(power);
      if (!tag) {
        tag = getPowerLevelTag(powerLevelTags, power);
        tagByPower.set(power, tag);
      }
      return tag;
    },
    [creators, creatorsTag, powerLevels, powerLevelTags, tagByPower]
  );

  return getMemberPowerTag;
};

export const getPowerTagIconSrc = (
  mx: MatrixClient,
  useAuthentication: boolean,
  icon: MemberPowerTagIcon
): string | undefined =>
  icon?.key?.startsWith('mxc://')
    ? mx.mxcUrlToHttp(icon.key, 96, 96, 'scale', undefined, undefined, useAuthentication) ?? '🌻'
    : icon?.key;

export const useAccessiblePowerTagColors = (
  themeKind: ThemeKind,
  creatorsTag: MemberPowerTag,
  powerLevelTags: PowerLevelTags
): Map<string, string> => {
  const accessibleColors: Map<string, string> = useMemo(() => {
    const colors: Map<string, string> = new Map();
    if (creatorsTag.color) {
      colors.set(creatorsTag.color, accessibleColor(themeKind, creatorsTag.color));
    }

    Object.values(powerLevelTags).forEach((tag) => {
      const { color } = tag;
      if (!color) return;

      colors.set(color, accessibleColor(themeKind, color));
    });

    return colors;
  }, [powerLevelTags, creatorsTag, themeKind]);

  return accessibleColors;
};

export const useFlattenPowerTagMembers = (
  members: RoomMember[],
  getTag: GetMemberPowerTag
): Array<MemberPowerTag | RoomMember> => {
  const PLTagOrRoomMember = useMemo(() => {
    let prevTag: MemberPowerTag | undefined;
    const tagOrMember: Array<MemberPowerTag | RoomMember> = [];
    members.forEach((member) => {
      const tag = getTag(member.userId);
      if (tag !== prevTag) {
        prevTag = tag;
        tagOrMember.push(tag);
      }
      tagOrMember.push(member);
    });
    return tagOrMember;
  }, [members, getTag]);

  return PLTagOrRoomMember;
};
