import React, { memo, useState, useRef, useMemo, useEffect } from "react";
import { Controller, Selection, PID } from "@tingwujs/core";
import { useSyncEffect } from "@tingwujs/common";
import { useRenderingParagraph } from "../../../hooks";
import { useRecoilValue } from "recoil";
import { caretStyleState, composingDataState } from "../../../model";
import { PureCaret } from "./pureCaret";
import { isEqual } from "lodash-es";

export interface CaretProps {
  controller: Controller;
  pid: PID;
  containerRef: ReturnType<typeof useRef<HTMLDivElement | null>>;
  isHighlight: boolean;
}

export const Caret: React.FC<CaretProps> = memo(props => {
  const { controller, pid, containerRef, isHighlight } = props;

  const [localSelection, setLocalSelectionState] = useState<Selection | undefined>(undefined);
  const localSelectionRef = useRef(localSelection);
  const caretStyleValue = useRecoilValue(caretStyleState);
  const [caretStyleValueState, setCaretStyleValueState] = useState(caretStyleValue?.style);
  const caretStyleValueStateRef = useRef(caretStyleValueState);

  const { isFinishRender } = useRenderingParagraph(controller, pid);
  const composingData = useRecoilValue(composingDataState);
  const isCalcRef = useRef(false);

  const setLocalSelection = (v: Selection | undefined) => {
    localSelectionRef.current = v;
    setLocalSelectionState(v);
  };

  const setCaretStyleValue = (v: React.CSSProperties | undefined) => {
    if (!isEqual(caretStyleValueStateRef.current, v)) {
      caretStyleValueStateRef.current = v;
      setCaretStyleValueState(caretStyleValueStateRef.current);
    }
  };

  if (localSelection && localSelection.range.start.pid === pid) {
    setCaretStyleValue(caretStyleValue?.style);
  } else if (!localSelection) {
    setCaretStyleValue(undefined);
  }

  useSyncEffect(() => {
    const currentSelection = controller.getCurrentSelection();
    setLocalSelection(currentSelection);
    return controller.on("selectionChange", ({ selection }) => {
      if (!isEqual(localSelectionRef.current, selection)) {
        setLocalSelection(selection);
        isCalcRef.current = true;
      }
    });
  }, [controller]);

  useEffect(() => {
    if (
      containerRef.current &&
      isFinishRender &&
      localSelection &&
      localSelection.range.start.pid === pid
    ) {
      controller.calcCaretStyles(isCalcRef.current);
      isCalcRef.current = false;
    }
  }, [isFinishRender, localSelection, pid, containerRef, controller]);

  const caretStyle = useMemo(() => {
    let style: React.CSSProperties | undefined;
    if (localSelection && localSelection.range.start.pid === pid && caretStyleValueState) {
      const { left, top } = caretStyleValueState;
      style = {
        ...caretStyleValueState,
        transform: `translate(${left}px, ${top}px)`,
        top: 0,
        left: 0,
      };
    }
    return style;
  }, [caretStyleValueState, localSelection, pid]);

  const isDisplayingCaret = useMemo(() => {
    return localSelection && localSelection.isCaret() && !composingData;
  }, [localSelection, composingData]);

  useEffect(() => {
    isCalcRef.current = false;
  }, [props]);

  if (isDisplayingCaret && caretStyle && !isCalcRef.current) {
    return <PureCaret controller={controller} style={caretStyle} isHighlight={isHighlight} />;
  }

  return null;
});
