import { Room } from 'matrix-js-sdk';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { AccountDataEvent } from '../../types/matrix/accountData';
import { StateEvent } from '../../types/matrix/room';
import {
  getGlobalSoundboardPacks,
  getRoomSoundboardPack,
  getRoomSoundboardPacks,
  getUserSoundboardPack,
  SoundboardPack,
} from '../plugins/soundboard';
import { useMatrixClient } from './useMatrixClient';
import { useAccountDataCallback } from './useAccountDataCallback';
import { useStateEventCallback } from './useStateEventCallback';

// Parallels hooks/useImagePacks.ts (custom emoji). Same aggregation shape.

export const useUserSoundboardPack = (): SoundboardPack | undefined => {
  const mx = useMatrixClient();
  const [userPack, setUserPack] = useState(() => getUserSoundboardPack(mx));

  useAccountDataCallback(
    mx,
    useCallback(
      (mEvent) => {
        if (mEvent.getType() === AccountDataEvent.LotusSoundboard) {
          setUserPack(getUserSoundboardPack(mx));
        }
      },
      [mx],
    ),
  );

  return userPack;
};

export const useGlobalSoundboardPacks = (): SoundboardPack[] => {
  const mx = useMatrixClient();
  const [globalPacks, setGlobalPacks] = useState(() => getGlobalSoundboardPacks(mx));

  useAccountDataCallback(
    mx,
    useCallback(
      (mEvent) => {
        if (mEvent.getType() === AccountDataEvent.LotusSoundboardRooms) {
          setGlobalPacks(getGlobalSoundboardPacks(mx));
        }
      },
      [mx],
    ),
  );

  useStateEventCallback(
    mx,
    useCallback(
      (mEvent) => {
        const roomId = mEvent.getRoomId();
        const stateKey = mEvent.getStateKey();
        if (
          mEvent.getType() === StateEvent.LotusSoundboardRoom &&
          roomId &&
          typeof stateKey === 'string'
        ) {
          const isGlobal = !!globalPacks.find(
            (pack) =>
              pack.address && pack.address.roomId === roomId && pack.address.stateKey === stateKey,
          );
          if (isGlobal) setGlobalPacks(getGlobalSoundboardPacks(mx));
        }
      },
      [mx, globalPacks],
    ),
  );

  return globalPacks;
};

export const useRoomSoundboardPack = (room: Room, stateKey: string): SoundboardPack | undefined => {
  const mx = useMatrixClient();
  const [roomPack, setRoomPack] = useState(() => getRoomSoundboardPack(room, stateKey));

  // Resync when the room/stateKey arg changes — the useState initializer only
  // runs on mount, so without this a re-target (e.g. a different room) kept
  // showing the first room's pack.
  useEffect(() => {
    setRoomPack(getRoomSoundboardPack(room, stateKey));
  }, [room, stateKey]);

  useStateEventCallback(
    mx,
    useCallback(
      (mEvent) => {
        if (
          mEvent.getRoomId() === room.roomId &&
          mEvent.getType() === StateEvent.LotusSoundboardRoom &&
          mEvent.getStateKey() === stateKey
        ) {
          setRoomPack(getRoomSoundboardPack(room, stateKey));
        }
      },
      [room, stateKey],
    ),
  );

  return roomPack;
};

export const useRoomSoundboardPacks = (room: Room): SoundboardPack[] => {
  const mx = useMatrixClient();
  const [roomPacks, setRoomPacks] = useState(() => getRoomSoundboardPacks(room));

  // Resync on room change (see useRoomSoundboardPack).
  useEffect(() => {
    setRoomPacks(getRoomSoundboardPacks(room));
  }, [room]);

  useStateEventCallback(
    mx,
    useCallback(
      (mEvent) => {
        if (
          mEvent.getRoomId() === room.roomId &&
          mEvent.getType() === StateEvent.LotusSoundboardRoom
        ) {
          setRoomPacks(getRoomSoundboardPacks(room));
        }
      },
      [room],
    ),
  );

  return roomPacks;
};

export const useRoomsSoundboardPacks = (rooms: Room[]): SoundboardPack[] => {
  const mx = useMatrixClient();
  const [roomPacks, setRoomPacks] = useState(() => rooms.flatMap(getRoomSoundboardPacks));

  // Resync when the room list changes (callers pass a memoized array, so this
  // fires only on an actual change — see useRoomSoundboardPack).
  useEffect(() => {
    setRoomPacks(rooms.flatMap(getRoomSoundboardPacks));
  }, [rooms]);

  useStateEventCallback(
    mx,
    useCallback(
      (mEvent) => {
        if (
          rooms.find((room) => room.roomId === mEvent.getRoomId()) &&
          mEvent.getType() === StateEvent.LotusSoundboardRoom
        ) {
          setRoomPacks(rooms.flatMap(getRoomSoundboardPacks));
        }
      },
      [rooms],
    ),
  );

  return roomPacks;
};

/** User ∪ global ∪ room packs, deduped by id, keeping only packs with clips. */
export const useRelevantSoundboardPacks = (rooms: Room[]): SoundboardPack[] => {
  const userPack = useUserSoundboardPack();
  const globalPacks = useGlobalSoundboardPacks();
  const roomsPacks = useRoomsSoundboardPacks(rooms);

  return useMemo(() => {
    const packs = userPack ? [userPack] : [];
    const globalPackIds = new Set(globalPacks.map((pack) => pack.id));
    const relPacks = packs.concat(
      globalPacks,
      roomsPacks.filter((pack) => !globalPackIds.has(pack.id)),
    );
    return relPacks.filter((pack) => pack.getClips().length > 0);
  }, [userPack, globalPacks, roomsPacks]);
};
