import React, { useEffect, useState, useMemo, useCallback, useRef, memo } from "react";
import {
  Controller,
  Paragraph,
  Word,
  PID,
  EmptyUnfixPID,
  TranslateMode,
  TextPolishMode,
} from "@tingwu/core";
import { getFlexibleTime, myLocalStorage, useSyncEffect } from "@tingwu/common";
import { SpeakerInput, SpeakerInputHandler } from "../speakerInput";
import { NoRecordTip } from "../noRecordTip";
import {
  ParagraphHeadingStyled,
  ParagraphHeadingSpeakerWrapper,
  ParagraphSpeakerWrapper,
  ParagraphAvatar,
  ParagraphSpeaker,
  ParagraphSpeakerText,
  ParagraphHeadingTime,
  ParagraphSpeaking,
  ParagraphHeadingExtra,
  DefaultAvatar,
} from "./styled";
import { TranslateStatus } from "../translateStatus";
import { LiveSpeakerTip } from "../liveSpeakerTip";
import {
  generateTranscriptionController,
  getTranscriptionController,
  useTranscriptionHooks,
} from "../../controller";
import useForceUpdate from "use-force-update";
import { TYIcon } from "@tingwu/design";

export interface ParagraphHeadingProps {
  controller: Controller;
  pid?: PID;
  paragraph?: Paragraph;
  words: Word[];
  isUnfix?: boolean;
  emptyUnfix?: boolean;
  readonly?: boolean;
  defaultSpeakerReadonly?: boolean;
  showBackground?: boolean;
  isParagraphMouseEnter?: boolean;
  isEditorFocus: boolean;
  translateMode: TranslateMode;
  textPolishMode: TextPolishMode;
  getParagraphContainer?: () => HTMLDivElement;
  shareDisabled?: boolean;
}

export const ParagraphHeading: React.FC<ParagraphHeadingProps> = memo(props => {
  const {
    controller,
    pid,
    paragraph,
    words,
    isUnfix,
    emptyUnfix,
    readonly = false,
    defaultSpeakerReadonly = true,
    showBackground = false,
    isParagraphMouseEnter = false,
    isEditorFocus,
    translateMode,
    textPolishMode,
    getParagraphContainer,
    shareDisabled,
  } = props;
  const {
    getAvailableSpeakers,
    renderAvatar,
    enableShowSpeaker,
    enableShowFirsttime,
    getDefaultSpeakerName,
    getDefaultSpeakerAvatar,
    enableEditSpeaker,
    onEvent,
  } = useTranscriptionHooks(controller, [
    "getAvailableSpeakers",
    "renderAvatar",
    "enableShowSpeaker",
    "enableShowFirsttime",
    "getDefaultSpeakerName",
    "getDefaultSpeakerAvatar",
    "enableEditSpeaker",
    "onEvent",
  ]);

  const transController = getTranscriptionController(controller);

  const forceUpdate = useForceUpdate();

  const [editingSpeaker, setEditingSpeaker] = useState(false);
  const [showSpeakerTip, setShowSpeakerTip] = useState<boolean>(false);
  const [noRecordNodeVisible, setNoRecordNodeVisible] = useState<boolean>(false);
  const [showHeadingExtra, setShowHeadingExtra] = useState(true);
  const [isDropdownShowUp, setIsDropdownShowUp] = useState(false);

  useEffect(() => {
    if (!isUnfix) {
      setShowSpeakerTip(false);
      return;
    }
    const isShow =
      controller.isFirstUnfixPid(pid) &&
      myLocalStorage.getItem("showedLiveSpeakerTip") !== true &&
      !transController.getIsDidShowUnfixSpeakerTip();
    setShowSpeakerTip(isShow);
  }, [isUnfix, pid, controller, transController]);

  const uid = (() => {
    if (paragraph) {
      return paragraph.uid;
    } else if (emptyUnfix) {
      return controller.getEmptyUnfixTempUid();
    }
  })();

  const getName = useCallback(() => {
    return controller.renderSpeakerName(uid);
  }, [uid, getAvailableSpeakers, getDefaultSpeakerName]);

  const getHighlight = useCallback(() => {
    return controller.renderSpeakerHighlight(uid);
  }, [uid, controller]);

  const [name, setName] = useState(() => getName());
  const [isHighlight, setIsHighlight] = useState(getHighlight());

  useSyncEffect(() => {
    if (!emptyUnfix) {
      return;
    }
    controller.on("emptyUnfixSpeakerChange", forceUpdate);
  }, [controller, emptyUnfix, forceUpdate]);

  const initName = useCallback(() => {
    setName(getName());
  }, [getName]);

  const initIsHighlight = useCallback(() => {
    setIsHighlight(getHighlight());
  }, [getHighlight, getName]);

  useSyncEffect(() => {
    if (!uid) {
      return;
    }
    return controller.on("speakerNameChange", ({ uid: _uid }) => {
      if (uid === _uid) {
        initName();
      }
    });
  }, [controller, uid, initName]);

  useEffect(() => {
    initName();
  }, [initName]);

  useEffect(() => {
    initIsHighlight();
  }, [initIsHighlight]);

  const beginTimeFormated = useMemo(() => {
    if (words && words.length > 0) {
      return getFlexibleTime(words[0].beginTime);
    } else {
      const paras = controller.getParagraphs();
      const lastParagraph = paras.length > 0 ? paras[paras.length - 1] : undefined;
      if (lastParagraph && lastParagraph.wids.length > 0) {
        const word = controller.getWord(lastParagraph.wids[lastParagraph.wids.length - 1]);
        return getFlexibleTime(word.beginTime);
      } else {
        return getFlexibleTime(0);
      }
    }
  }, [words, controller]);

  const inputRef = useRef<SpeakerInputHandler>(null);

  const handleInputFocus = useCallback(() => {
    setShowHeadingExtra(false);
  }, []);

  const isCurrentPid = useCallback(
    (_pid: PID | EmptyUnfixPID | undefined) => {
      return (emptyUnfix && _pid === "emptyUnfixPid") || (_pid && _pid === pid);
    },
    [emptyUnfix, pid]
  );

  const handleEditing = useCallback(() => {
    if (enableEditSpeaker) {
      onEvent &&
        onEvent("willEditSpeaker", {
          pid,
          emptyUnfix: Boolean(emptyUnfix),
          oldSpeaker: name,
        });
      transController.setCurrentEditingSpeakerPid(emptyUnfix ? "emptyUnfixPid" : pid!);
      setEditingSpeaker(true);
      setTimeout(() => {
        inputRef.current?.focus();
      });
    }
  }, [enableEditSpeaker, transController, emptyUnfix, pid, onEvent, name]);

  const handleFinishEditing = useCallback(() => {
    transController.setCurrentEditingSpeakerPid(undefined);
    setEditingSpeaker(false);
    setShowHeadingExtra(true);
  }, [transController]);

  useSyncEffect(() => {
    if (!editingSpeaker) {
      return;
    }
    return transController.on("currentEditingSpeakerPidChange", ({ pid: _pid }) => {
      if (!isCurrentPid(_pid)) {
        setEditingSpeaker(false);
      }
    });
  }, [transController, pid, emptyUnfix, isCurrentPid, editingSpeaker]);

  const invisibleSpeakerNode = useMemo(() => {
    const isUnfixRender = transController.getIsRenderUnfixSpeakerPlaceholder();

    if (!enableShowFirsttime && isUnfix && isUnfixRender) {
      return <ParagraphSpeaking>发言人正在讲话…</ParagraphSpeaking>;
    } else {
      return beginTimeFormated ? (
        <ParagraphHeadingTime className="time">{beginTimeFormated}</ParagraphHeadingTime>
      ) : null;
    }
  }, [isUnfix, beginTimeFormated, transController, enableShowFirsttime]);

  const liveSpeakerTipNode = useMemo(() => {
    return (
      <LiveSpeakerTip
        onClose={isUserAction => {
          if (isUserAction) {
            myLocalStorage.setItem("showedLiveSpeakerTip", true);
          }
          transController.setIsDidShowUnfixSpeakerTip(true);
          setShowSpeakerTip(false);
        }}
      />
    );
  }, [transController]);

  useSyncEffect(() => {
    if (!isUnfix) {
      // 只需要在是unfix的时候监听，非unfix不用监听
      return;
    }
    return controller.on("detectEmptyContent", () => {
      setNoRecordNodeVisible(true);
      onEvent && onEvent("detectEmptyContentOpen", {});
    });
  }, [isUnfix]);

  useSyncEffect(() => {
    if (!isUnfix) {
      // 只需要在是unfix的时候监听，非unfix不用监听
      return;
    }
    return controller.on("closeNoRecordTip", () => {
      setNoRecordNodeVisible(false);
    });
  }, [isUnfix]);

  const noRecordNode = useMemo(() => {
    return (
      !editingSpeaker && (
        <NoRecordTip
          controller={controller}
          visible={noRecordNodeVisible}
          onVisibleChange={() => {
            setNoRecordNodeVisible(false);
          }}
        />
      )
    );
  }, [noRecordNodeVisible, editingSpeaker, controller]);

  const defaultSpeakerAvatar = useMemo(() => {
    return getDefaultSpeakerAvatar() || "";
  }, [getDefaultSpeakerAvatar]);

  const speakerNode = useMemo(() => {
    const avatarIconForUid = uid && renderAvatar && renderAvatar(uid);
    const avatarIcon = avatarIconForUid || (
      <DefaultAvatar defaultAvatarImg={defaultSpeakerAvatar} />
    );

    const speakerReadonly: boolean =
      !!readonly || (!avatarIconForUid ? defaultSpeakerReadonly : false);
    const isShowTime = controller.getShowHeaderTime();

    return (
      <>
        <ParagraphSpeakerWrapper
          visible={enableShowSpeaker}
          disableHoverEffect={speakerReadonly}
          data-e2e-test-id="trans_paragraphHead_speaker_div"
        >
          <ParagraphAvatar>{avatarIcon}</ParagraphAvatar>
          {editingSpeaker ? (
            <SpeakerInput
              controller={controller}
              pid={pid}
              ref={inputRef}
              onFocus={handleInputFocus}
              defaultName={name}
              onFinishEditing={handleFinishEditing}
              emptyUnfix={emptyUnfix}
              isUnfix={isUnfix}
              getParagraphContainer={getParagraphContainer}
            />
          ) : (
            <>
              <ParagraphSpeaker
                className="speaker"
                readonly={speakerReadonly}
                onClick={!speakerReadonly ? handleEditing : undefined}
              >
                <ParagraphSpeakerText isHighlight={isHighlight}>{name}</ParagraphSpeakerText>
                <TYIcon
                  type={"tongyi-edit-line"}
                  style={{ color: "var(--Text_Caption)" }}
                  size={12}
                />
              </ParagraphSpeaker>
              {beginTimeFormated && isShowTime ? (
                <ParagraphHeadingTime
                  className="time"
                  readonly={speakerReadonly}
                  isHighlight={isHighlight}
                >
                  {beginTimeFormated}
                </ParagraphHeadingTime>
              ) : null}
              {showSpeakerTip ? liveSpeakerTipNode : null}
            </>
          )}
          {noRecordNode}
        </ParagraphSpeakerWrapper>
        {!enableShowSpeaker && invisibleSpeakerNode}
      </>
    );
  }, [
    enableShowSpeaker,
    beginTimeFormated,
    controller,
    editingSpeaker,
    handleInputFocus,
    name,
    handleFinishEditing,
    pid,
    handleEditing,
    uid,
    renderAvatar,
    emptyUnfix,
    invisibleSpeakerNode,
    showSpeakerTip,
    readonly,
    defaultSpeakerReadonly,
    liveSpeakerTipNode,
    noRecordNode,
    isUnfix,
    getParagraphContainer,
    defaultSpeakerAvatar,
    isHighlight,
  ]);

  const tranController = generateTranscriptionController(controller);
  useSyncEffect(() => {
    return tranController.on("onDropdownShowUp", ({ pids: _pids = [] }) => {
      const selection = controller.getCurrentSelection();

      if (_pids.includes(pid || "") && selection && !selection.isCaret() && !readonly) {
        setIsDropdownShowUp(true);
      }
    });
  }, [tranController, readonly]);

  useSyncEffect(() => {
    return tranController.on("onDropdownHideOff", () => {
      setIsDropdownShowUp(false);
    });
  }, [tranController]);

  const extra = transController.renderParagraphHeadingExtra({
    isMouseEnter: isParagraphMouseEnter,
    pid,
    emptyUnfix,
    isUnfix,
    isDropdownShowUp,
    isEditorFocus,
    readonly,
    translateMode,
    textPolishMode,
    shareDisabled,
  });
  const headingExtra =
    (showHeadingExtra && extra) || (pid && <TranslateStatus controller={controller} pid={pid} />);

  return (
    <ParagraphHeadingStyled showBackground={showBackground} enableShowSpeaker={enableShowSpeaker}>
      <ParagraphHeadingSpeakerWrapper>{speakerNode}</ParagraphHeadingSpeakerWrapper>
      <ParagraphHeadingExtra data-e2e-test-id="trans_paragraphHead_tag_div">
        {headingExtra}
      </ParagraphHeadingExtra>
    </ParagraphHeadingStyled>
  );
});
