import { addCallback } from '../../lib/teact/teactn';
import { getGlobal } from '../../global';

import type { ApiSticker } from '../../api/types';
import type { GlobalState } from '../../global/types';
import { ApiMediaFormat } from '../../api/types';

import { requestMutation } from '../../lib/fasterdom/fasterdom';
import { getStickerHashById } from '../../global/helpers';
import { selectCanPlayAnimatedEmojis, selectCustomEmoji } from '../../global/selectors';
import { IS_WEBM_SUPPORTED } from '../browser/windowEnvironment';
import { createCallbackManager } from '../callbacks';
import generateUniqueId from '../generateUniqueId';
import * as mediaLoader from '../mediaLoader';
import { throttle } from '../schedulers';

import blankSrc from '../../assets/blank.png';
import placeholderSrc from '../../assets/square.svg';

type CustomEmojiLoadCallback = (customEmojis: GlobalState['customEmojis']) => void;
type CustomEmojiInputRenderCallback = (emojiId: string) => void;

const DOM_PROCESS_THROTTLE = 500;

const INPUT_WAITING_CUSTOM_EMOJI_IDS = new Set<string>();

const handlers = new Map<CustomEmojiLoadCallback, string>();
const renderCallbacks = createCallbackManager<CustomEmojiInputRenderCallback>();

let prevGlobal: GlobalState | undefined;

addCallback((global: GlobalState) => {
  if (
    global.customEmojis.byId !== prevGlobal?.customEmojis.byId
    || selectCanPlayAnimatedEmojis(global) !== selectCanPlayAnimatedEmojis(prevGlobal)
  ) {
    for (const entry of handlers) {
      const [handler, id] = entry;
      if (selectCustomEmoji(global, id)) {
        handler(global.customEmojis);
      }
    }

    checkInputCustomEmojiLoad(global.customEmojis);
  }

  prevGlobal = global;
});

export function addCustomEmojiCallback(handler: CustomEmojiLoadCallback, emojiId: string) {
  handlers.set(handler, emojiId);
}

export function removeCustomEmojiCallback(handler: CustomEmojiLoadCallback) {
  handlers.delete(handler);
}

export const addCustomEmojiInputRenderCallback = renderCallbacks.addCallback;
const callInputRenderHandlers = throttle(renderCallbacks.runCallbacks, DOM_PROCESS_THROTTLE);

function processDomForCustomEmoji() {
  const emojis = document.querySelectorAll<HTMLImageElement>('.custom-emoji.placeholder');
  emojis.forEach((emoji) => {
    const customEmoji = selectCustomEmoji(getGlobal(), emoji.dataset.documentId!);
    if (!customEmoji) {
      INPUT_WAITING_CUSTOM_EMOJI_IDS.add(emoji.dataset.documentId!);
      return;
    }
    const [isPlaceholder, src, uniqueId] = getInputCustomEmojiParams(customEmoji);

    if (customEmoji.shouldUseTextColor && !emoji.classList.contains('colorable')) {
      requestMutation(() => {
        emoji.classList.add('colorable');
      });
    }

    if (!isPlaceholder) {
      requestMutation(() => {
        emoji.src = src;
        emoji.classList.remove('placeholder');
        if (uniqueId) emoji.dataset.uniqueId = uniqueId;

        callInputRenderHandlers(customEmoji.id);
      });
    }
  });
}

export const processMessageInputForCustomEmoji = throttle(processDomForCustomEmoji, DOM_PROCESS_THROTTLE);

function checkInputCustomEmojiLoad(customEmojis: GlobalState['customEmojis']) {
  const loaded = Array.from(INPUT_WAITING_CUSTOM_EMOJI_IDS).filter((id) => Boolean(customEmojis.byId[id]));
  if (loaded.length) {
    loaded.forEach((id) => INPUT_WAITING_CUSTOM_EMOJI_IDS.delete(id));
    processMessageInputForCustomEmoji();
  }
}

export function getCustomEmojiMediaDataForInput(emojiId: string, isPreview?: boolean) {
  const mediaHash = getStickerHashById(emojiId, isPreview);
  const data = mediaLoader.getFromMemory(mediaHash);
  if (data) {
    return data;
  }

  void fetchAndProcess(mediaHash);
  return undefined;
}

function fetchAndProcess(mediaHash: string) {
  return mediaLoader.fetch(mediaHash, ApiMediaFormat.BlobUrl).then(() => {
    processMessageInputForCustomEmoji();
  });
}

export function getInputCustomEmojiParams(customEmoji?: ApiSticker) {
  if (!customEmoji) return [true, placeholderSrc, undefined] as const;
  const shouldUseStaticFallback = !IS_WEBM_SUPPORTED && customEmoji.isVideo;
  const isUsingSharedCanvas = customEmoji.isLottie || (customEmoji.isVideo && !shouldUseStaticFallback);
  if (isUsingSharedCanvas) {
    void fetchAndProcess(`sticker${customEmoji.id}`);
    return [false, blankSrc, generateUniqueId()] as const;
  }

  const mediaData = getCustomEmojiMediaDataForInput(customEmoji.id, shouldUseStaticFallback);

  return [!mediaData, mediaData || placeholderSrc, undefined] as const;
}
