import { useCallback, useLayoutEffect, useMemo, useState } from 'react';

import { isList } from 'immutable';

import type { ApiCustomEmojiJSON } from '@/flavours/glitch/api_types/custom_emoji';
import { useAppSelector } from '@/flavours/glitch/store';
import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment';

import { toSupportedLocale } from './locale';
import { determineEmojiMode } from './mode';
import { emojifyElement, emojifyText } from './render';
import type {
  CustomEmojiMapArg,
  EmojiAppState,
  ExtraCustomEmojiMap,
} from './types';
import { stringHasAnyEmoji } from './utils';

interface UseEmojifyOptions {
  text: string;
  extraEmojis?: CustomEmojiMapArg;
  deep?: boolean;
}

export function useEmojify({
  text,
  extraEmojis,
  deep = true,
}: UseEmojifyOptions) {
  const [emojifiedText, setEmojifiedText] = useState<string | null>(null);

  const appState = useEmojiAppState();
  const extra: ExtraCustomEmojiMap = useMemo(() => {
    if (!extraEmojis) {
      return {};
    }
    if (isList(extraEmojis)) {
      return (
        extraEmojis.toJS() as ApiCustomEmojiJSON[]
      ).reduce<ExtraCustomEmojiMap>(
        (acc, emoji) => ({ ...acc, [emoji.shortcode]: emoji }),
        {},
      );
    }
    return extraEmojis;
  }, [extraEmojis]);

  const emojify = useCallback(
    async (input: string) => {
      let result: string | null = null;
      if (deep) {
        const wrapper = document.createElement('div');
        wrapper.innerHTML = input;
        if (await emojifyElement(wrapper, appState, extra)) {
          result = wrapper.innerHTML;
        }
      } else {
        result = await emojifyText(text, appState, extra);
      }
      if (result) {
        setEmojifiedText(result);
      } else {
        setEmojifiedText(input);
      }
    },
    [appState, deep, extra, text],
  );
  useLayoutEffect(() => {
    if (isModernEmojiEnabled() && !!text.trim() && stringHasAnyEmoji(text)) {
      void emojify(text);
    } else {
      // If no emoji or we don't want to render, fall back.
      setEmojifiedText(text);
    }
  }, [emojify, text]);

  return emojifiedText;
}

export function useEmojiAppState(): EmojiAppState {
  const locale = useAppSelector((state) =>
    toSupportedLocale(state.meta.get('locale') as string),
  );
  const mode = useAppSelector((state) =>
    determineEmojiMode(state.meta.get('emoji_style') as string),
  );

  return {
    currentLocale: locale,
    locales: [locale],
    mode,
    darkTheme: document.body.classList.contains('theme-default'),
  };
}
