import React, {
  memo,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  // lazy,
} from "react";
import { Controller, TranscriptingState } from "@tingwujs/core";
import { TranscriptionContent } from "../transcriptionContent";
import {
  Wrapper,
  ScrollContainer,
  ScrollContainerInner,
  TranscriptionContentWrapper,
} from "./styled";
import {
  generateTranscriptionController,
  getTranscriptionController,
  TranscriptionHooks,
  removeTranscriptionController,
  TranscriptionEventes,
} from "../../controller";
import { generateExtension, ExtendProps } from "./extend";
import { isSpaceHotKey, useSyncEffect } from "@tingwujs/common";
import { BaseExtension } from "../../extensions";
import { generateTheme } from "../../theme";
import { TingwuContext } from "../../context";
import { useRecoilValue } from "recoil";
import { transcriptingStateState } from "../../model";
import { TranslateMenu } from "../translateMenu";
import { ReportFunction, implementReport } from "../../report";
import { TextBox } from "../textBox";
import { ThemeProvider } from "styled-components";

type UserTranscriptionHooks = Omit<TranscriptionHooks, "getTranscriptionContainer">;

export interface TranscriptionProps extends UserTranscriptionHooks {
  style?: React.CSSProperties;
  className?: string;
  renderBefore?: (controller?: Controller) => React.ReactNode;
  renderAfter?: (controller?: Controller) => React.ReactNode;
  extensions?: BaseExtension[];
  shareDisabled?: boolean;
  onSendEventLog?: ReportFunction;
  showDropMenuMark?: boolean;
}

export const Transcription: React.FC<TranscriptionProps> = memo(props => {
  const {
    className,
    style,
    renderBefore,
    renderAfter,
    onFetchAlibabaEmployees,
    getAvailableSpeakers,
    setAvailableSpeakers,
    renderAvatar,
    enableSelectAlibabaEmployee,
    enableCommonUseSpeaker,
    enableShowSpeaker,
    enableShowFirsttime,
    getDefaultSpeakerName,
    getDefaultSpeakerAvatar,
    enableEditSpeaker,
    enableOfflineEditWordTip,
    onFinishEnableOfflineEditWordTip,
    onEvent,
    extensions = [],
    theme,
    getCommonUseSpeakers,
    setCommonUseSpeakers,
    addCommonUseSpeaker,
    renderCommonUseAvatar,
    qnaController,
    aiAssistantController,
    onDeleteQnas,
    beforeCoverHeight,
    shareDisabled,
    onSendEventLog,
    type,
    showDropMenuMark = true,
  } = props;

  const { getController } = useContext(TingwuContext);

  const controller = useMemo(() => {
    return getController()!;
  }, [getController]);

  const containerRef = useRef<HTMLDivElement>(null);
  const transcriptionRef = useRef<HTMLDivElement>(null);
  const isUnfixFocus = useRef<boolean>(false);
  const tranController = generateTranscriptionController(controller);
  const { playingWordController } = tranController;
  const genedTheme = useMemo(() => {
    return generateTheme(theme);
  }, [theme]);

  const transcriptingState = useRecoilValue(transcriptingStateState);

  useEffect(() => {
    return () => {
      removeTranscriptionController(controller.id);
    };
  }, [controller.id]);

  const getTranscriptionContainer = useCallback(() => {
    return containerRef.current as HTMLElement;
  }, []);

  const getTranscriptionDocument = useCallback(() => {
    return transcriptionRef.current as HTMLElement;
  }, []);

  // onSendLog
  useEffect(() => {
    if (!onSendEventLog) return;
    implementReport(onSendEventLog);
  }, [onSendEventLog]);

  useEffect(() => {
    tranController.setHooks({
      getTranscriptionContainer,
      getTranscriptionDocument,
      onFetchAlibabaEmployees,
      getAvailableSpeakers,
      getCommonUseSpeakers,
      setCommonUseSpeakers,
      setAvailableSpeakers,
      addCommonUseSpeaker,
      renderCommonUseAvatar,
      renderAvatar,
      enableSelectAlibabaEmployee,
      enableCommonUseSpeaker,
      enableShowSpeaker,
      enableShowFirsttime,
      getDefaultSpeakerName,
      getDefaultSpeakerAvatar,
      enableEditSpeaker,
      enableOfflineEditWordTip,
      onFinishEnableOfflineEditWordTip,
      onEvent,
      qnaController,
      aiAssistantController,
      onDeleteQnas,
      theme: genedTheme,
      beforeCoverHeight,
    });
  }, [
    tranController,
    onFetchAlibabaEmployees,
    getAvailableSpeakers,
    getCommonUseSpeakers,
    setCommonUseSpeakers,
    addCommonUseSpeaker,
    renderCommonUseAvatar,
    setAvailableSpeakers,
    renderAvatar,
    enableSelectAlibabaEmployee,
    enableCommonUseSpeaker,
    enableShowSpeaker,
    enableShowFirsttime,
    getDefaultSpeakerName,
    getDefaultSpeakerAvatar,
    enableEditSpeaker,
    getTranscriptionContainer,
    getTranscriptionDocument,
    enableOfflineEditWordTip,
    onFinishEnableOfflineEditWordTip,
    onEvent,
    qnaController,
    aiAssistantController,
    onDeleteQnas,
    genedTheme,
    beforeCoverHeight,
  ]);

  useEffect(() => {
    tranController.setExtensions(extensions);
  }, [tranController, extensions]);

  useEffect(() => {
    if (setAvailableSpeakers) {
      controller.injectSpeakersSetter(setAvailableSpeakers);
    }
  }, [controller, setAvailableSpeakers]);

  useEffect(() => {
    if (getAvailableSpeakers) {
      controller.injectSpeakersGetter(getAvailableSpeakers);
    }
  }, [controller, getAvailableSpeakers]);

  useEffect(() => {
    if (renderAvatar) {
      controller.injectRenderAvatarGetter(renderAvatar);
    }
  }, [controller, renderAvatar]);

  // 设置常用发言人列表
  useEffect(() => {
    if (getCommonUseSpeakers) {
      controller.injectCommonUseSpeakersGetter(getCommonUseSpeakers);
    }
  }, [controller, getCommonUseSpeakers]);

  // 设置常用发言人列表
  useEffect(() => {
    if (setCommonUseSpeakers) {
      controller.injectCommonUseSpeakersSetter(setCommonUseSpeakers);
    }
  }, [controller, setCommonUseSpeakers]);

  useEffect(() => {
    if (getDefaultSpeakerName) {
      controller.injectDefaultSpeakerNameGetter(getDefaultSpeakerName);
    }
  }, [controller, getDefaultSpeakerName]);

  useEffect(() => {
    if (getDefaultSpeakerAvatar) {
      controller.injectDefaultSpeakerAvatarGetter(getDefaultSpeakerAvatar);
    }
  }, [controller, getDefaultSpeakerAvatar]);

  const transController = getTranscriptionController(controller);

  useEffect(() => {
    return () => {
      transController.clean();
    };
  }, [transController]);

  const beforeContent = useMemo(() => {
    return (renderBefore && renderBefore(controller)) || null;
  }, [renderBefore, controller]);

  const afterContent = useMemo(() => {
    return (renderAfter && renderAfter(controller)) || null;
  }, [renderAfter, controller]);

  const hasExtraContent = Boolean(beforeContent || afterContent);

  useEffect(() => {
    return controller.on("searchReplaceChange", event => {
      const { selections, activeIndex, enableScroll } = event;
      if (!enableScroll || selections.length === 0) {
        return;
      }
      let selection = selections[activeIndex];

      if (!selection) {
        selection = selections[activeIndex - 1];
      }

      transController.emit("onDisplayWordShowUp", {
        pid: selection.pid,
      });
    });
  }, [controller, transController]);

  useEffect(() => {
    return controller.on("playingVoiceWordChange", event => {
      const { pid, word, forceDisplay, seekWithExtContent, isPlaying = true } = event;
      if (!word) {
        return;
      }
      transController.emitAsync("onDisplayWordShowUp", {
        pid,
        word,
        forceDisplay,
        seekWithExtContent,
        isPlaying,
      });
    });
  }, [controller, transController]);

  const onDisplayWordShowUpStrategy = useCallback(
    (event: TranscriptionEventes["onDisplayWordShowUp"]) => {
      const transcriptionElem = transcriptionRef.current;
      if (!transcriptionElem) {
        return;
      }

      const { pid, seekWithExtContent } = event;
      playingWordController.emit("paragraphShowUp", { pid, seekWithExtContent });
    },
    [playingWordController]
  );

  useEffect(() => {
    const keydownHandler = (event: KeyboardEvent) => {
      if (isSpaceHotKey(event.keyCode) && event.target === document.body) {
        event.preventDefault();
      }
    };

    window.addEventListener("keydown", keydownHandler);
    return () => {
      window.removeEventListener("keydown", keydownHandler);
    };
  }, []);

  // ******start 跨段划选滚动条自动滚动 start******
  const scrollRequestAnimationTimer = useRef<number | null>(null);
  const startScroll = useCallback((direction: "up" | "down") => {
    const scrollContainer = containerRef.current;
    if (!scrollContainer) {
      return;
    }
    if (scrollContainer.scrollTop < 0 || scrollContainer.scrollTop > scrollContainer.scrollHeight) {
      return;
    }

    const distance = 8;
    scrollRequestAnimationTimer.current = requestAnimationFrame(() => {
      const top =
        direction === "up"
          ? scrollContainer.scrollTop - distance
          : scrollContainer.scrollTop + distance;

      scrollContainer.scrollTo({ top });
      startScroll(direction);
    });
  }, []);

  const onMouseMove = useCallback(
    (event: React.MouseEvent) => {
      if (controller.isSelectionPending(false) && containerRef.current) {
        const scrollContainer = containerRef.current;
        const { top, bottom } = scrollContainer.getBoundingClientRect();
        const clientY = Math.floor(event.clientY);
        const scrollDistance = 30;

        if (scrollRequestAnimationTimer.current === null) {
          if (clientY - top <= scrollDistance) {
            startScroll("up");
          } else if (bottom - clientY <= scrollDistance) {
            startScroll("down");
          }
        } else if (clientY - top > scrollDistance && bottom - clientY > scrollDistance) {
          cancelAnimationFrame(scrollRequestAnimationTimer.current);
          scrollRequestAnimationTimer.current = null;
        }
      } else if (scrollRequestAnimationTimer.current !== null) {
        cancelAnimationFrame(scrollRequestAnimationTimer.current);
        scrollRequestAnimationTimer.current = null;
      }
    },
    [controller, startScroll]
  );

  useEffect(() => {
    document.body.addEventListener("mouseup", () => {
      if (scrollRequestAnimationTimer.current !== null) {
        cancelAnimationFrame(scrollRequestAnimationTimer.current);
        scrollRequestAnimationTimer.current = null;
      }
    });
  }, []);
  // ******end 跨段划选滚动条自动滚动 end******

  const renderContentStrategy: ExtendProps["renderContent"] = useCallback(
    params => {
      const { onWhell, onScroll, isShowingLivingScrollToBottom, onScrollToBottom } = params;
      return (
        <ScrollContainer
          ref={containerRef}
          onWheel={onWhell}
          onScroll={onScroll}
          onMouseMove={onMouseMove}
          id={"tingwu_paragraphs_scrollContainer"}
        >
          <ScrollContainerInner id={"tingwu_paragraphs_scrollContainerInner"}>
            {beforeContent}
            <TranscriptionContentWrapper
              ref={transcriptionRef}
              data-e2e-test-id="trans_container_div"
            >
              <TranscriptionContent
                isShowingLivingScrollToBottom={isShowingLivingScrollToBottom}
                onScrollToBottom={onScrollToBottom}
                controller={controller}
                hasExtraContent={hasExtraContent}
                theme={genedTheme}
                rootClassName={className}
                shareDisabled={shareDisabled}
                className="transcriptionContent"
              />
            </TranscriptionContentWrapper>
            {afterContent}
          </ScrollContainerInner>
          <TranslateMenu controller={controller} />
          <TextBox
            controller={controller}
            transController={tranController}
            scrollViewRef={containerRef}
            theme={genedTheme}
          />
        </ScrollContainer>
      );
    },
    [
      onMouseMove,
      beforeContent,
      controller,
      hasExtraContent,
      genedTheme,
      shareDisabled,
      afterContent,
      tranController,
      className,
    ]
  );

  const onContainerRectChangeStrategy = useCallback(
    (rect: DOMRectReadOnly) => {
      const { width, height } = rect;
      // This method will call in component initialze
      transController.setContainerRect({
        width,
        height,
      });
    },
    [transController]
  );

  const localExtensions = useMemo(() => {
    const getTranscriptionElem = () => transcriptionRef.current;
    const getContainerElem = () => containerRef.current;

    return generateExtension({
      controller,
      tranController,
      getTranscriptionElem,
      getContainerElem,
      type,
      rootClassName: className,
    });
  }, [controller, className, tranController]);

  const {
    onWhell,
    onScroll,
    renderContent,
    onDisplayWordShowUp,
    isShowingLivingScrollToBottom,
    onScrollToBottom,
    onContainerRectChange,
  }: ExtendProps = (() => {
    const { extendByPlayingWord, extendByCheckAgenda, extendByLivingScrollEnd } = localExtensions;
    let params: ExtendProps = {
      renderContent: renderContentStrategy,
      onDisplayWordShowUp: onDisplayWordShowUpStrategy,
      onContainerRectChange: onContainerRectChangeStrategy,
    };
    params = extendByPlayingWord(params);
    params = extendByCheckAgenda(params);
    params = extendByLivingScrollEnd(params);
    return params;
  })();

  controller.on("livingFixWord", e => {
    // 直播， unfix 区域修改（手动修、机器fix变词等）
    e && onScrollToBottom && onScrollToBottom("livingIsUnfix");
  });

  controller.on("editorFocusChange", ({ pid }) => {
    // unfix区域 获得焦点 、 编辑字词时 执行滚动最底部
    if (pid && controller.getRealTimeLivingMode()) {
      const paragraph = controller.getParagraph(pid);
      paragraph.isUnfix && onScrollToBottom && onScrollToBottom("livingIsUnfix");
      isUnfixFocus.current = !!paragraph.isUnfix;
    } else if (!pid) {
      isUnfixFocus.current = false;
    }
  });

  controller.on("paragraphChange", ({ pid, typeChange }) => {
    const { isUnfix } = controller.getParagraph(pid);
    if (
      controller.getRealTimeLivingMode() &&
      isUnfixFocus.current &&
      onScrollToBottom &&
      isUnfix &&
      typeChange === "addWord"
    ) {
      onScrollToBottom("livingIsUnfix");
    }
  });

  useSyncEffect(() => {
    if (!onDisplayWordShowUp) {
      return;
    }
    return transController.on("onDisplayWordShowUp", onDisplayWordShowUp);
  }, [transController, onDisplayWordShowUp]);

  useEffect(() => {
    const container = containerRef.current;
    if (!window.ResizeObserver || !container) {
      return;
    }
    //
    // User will manual import `resize-observer-polyfill` if he need.
    const resizeObserver = new ResizeObserver(entries => {
      if (entries.length === 0) {
        return;
      }
      onContainerRectChange(entries[0].contentRect);
    });
    resizeObserver.observe(container);
    return () => {
      resizeObserver.disconnect();
    };
  }, [onContainerRectChange]);

  const scrollToBottomButton = useMemo(() => {
    if (!isShowingLivingScrollToBottom) {
      return null;
    }

    if (transcriptingState === TranscriptingState.Disabled) {
      return null;
    }

    return tranController.renderScrollToBottomButton({
      isShowingLivingScrollToBottom,
      onScrollToBottom,
    });
  }, [tranController, onScrollToBottom, isShowingLivingScrollToBottom, transcriptingState]);

  return (
    // @ts-ignore
    <ThemeProvider theme={genedTheme}>
      <Wrapper className={className} style={style}>
        {renderContent({
          onWhell,
          onScroll,
          isShowingLivingScrollToBottom,
          onScrollToBottom,
        })}
        {scrollToBottomButton}
      </Wrapper>
    </ThemeProvider>
  );
});
