import styled from '@emotion/styled';
import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { gray150, gray900 } from '../styles/colors';

export const MicroModal = styled.div`
  background-color: ${gray150};
  box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.6);
`;

const TooltipChildrenWrapper = styled.div`
  display: contents;
`;

export const TooltipMessage = styled(MicroModal)`
  pointer-events: none;
  display: none;
  position: absolute;
  padding: 5px;
  z-index: 20;
  font-size: 14px;
  border-radius: 4px;
  color: ${gray150};
  background-color: ${gray900};
  white-space: pre-wrap;
`;

export const AutoTooltip = ({
  message,
  children,
  enabled,
}: {
  message: React.ReactNode[] | React.ReactNode;
  children: React.ReactNode;
  enabled: boolean;
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const messageRef = useRef<HTMLDivElement>(null);

  const handleDismiss = useCallback(() => {
    const messageElement = messageRef.current;
    if (!messageElement) return;
    messageElement.style.display = 'none';
    window.removeEventListener('click', handleDismiss);
  }, []);

  const handleActivate = useCallback(() => {
    const container = containerRef.current;
    const content = container?.children[0];
    if (!content) return;

    const messageElement = messageRef.current;
    if (!messageElement) return;

    const contentBox = content.getBoundingClientRect();
    const targetX = contentBox.left + contentBox.width / 2;
    const targetY = contentBox.top + contentBox.height / 2;

    if (targetY + 200 > window.innerHeight) {
      messageElement.style.bottom = `${window.innerHeight - targetY + 10}px`;
      messageElement.style.top = '';
    } else {
      messageElement.style.top = `${targetY + 20}px`;
      messageElement.style.bottom = '';
    }

    if (targetX + 100 > window.innerWidth) {
      messageElement.style.right = `${window.innerWidth - targetX}px`;
      messageElement.style.left = '';
      messageElement.style.transform = ``;
    } else if (targetX - 100 < 0) {
      messageElement.style.left = `${targetX}px`;
      messageElement.style.right = '';
      messageElement.style.transform = ``;
    } else {
      messageElement.style.left = `${targetX}px`;
      messageElement.style.right = '';
      messageElement.style.transform = `translateX(-50%)`;
    }

    messageElement.style.display = 'block';

    setTimeout(handleDismiss, 3000);
    setTimeout(() => window.addEventListener('click', handleDismiss), 100);
  }, [handleDismiss]);

  return (
    <Fragment>
      {createPortal(<TooltipMessage ref={messageRef}>{message}</TooltipMessage>, document.body)}
      <TooltipChildrenWrapper ref={containerRef} onClick={enabled ? handleActivate : undefined}>
        {children}
      </TooltipChildrenWrapper>
    </Fragment>
  );
};

let tooltipRapidfireTimeout: ReturnType<typeof setTimeout> | null = null;
let tooltipRapidfire = false;

const Tooltip = ({
  message,
  children,
  enabled = true,
  force = false,
  position,
  immediate = false,
  allowRapidfire = false,
}: {
  message?: React.ReactNode[] | React.ReactNode;
  children: React.ReactNode;
  enabled?: boolean;
  force?: boolean;
  position?: 'bottom' | 'right';
  immediate?: boolean;
  allowRapidfire?: boolean;
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const messageRef = useRef<HTMLDivElement>(null);
  const waitingRef = useRef(false);
  const interactingRef = useRef(false);
  const timeout = useRef<number | null>(null);

  const handleDismiss = useCallback(() => {
    waitingRef.current = false;
    const messageElement = messageRef.current;
    tooltipRapidfireTimeout = setTimeout(() => {
      tooltipRapidfire = false;
    }, 300);
    if (!messageElement) return;
    if (!force) messageElement.style.display = 'none';
  }, [force]);

  const mouseDownRef = useRef<null | (() => void)>(null);

  const handleMouseEnter = useCallback(
    (e: any) => {
      waitingRef.current = true;
      if (tooltipRapidfireTimeout) {
        clearTimeout(tooltipRapidfireTimeout);
      }
      if (timeout.current) {
        clearTimeout(timeout.current);
      }

      timeout.current = setTimeout(
        () => {
          if (interactingRef.current) return;

          if (!waitingRef.current) return;
          const messageElement = messageRef.current;

          if (!messageElement) return;
          const target = containerRef.current?.children[0];

          if (!target) return;

          if (allowRapidfire) tooltipRapidfire = true;

          const targetClientRect = target.getBoundingClientRect();
          const targetBoundingBox = {
            top: targetClientRect.top + window.scrollY,
            left: targetClientRect.left + window.scrollX,
            width: targetClientRect.width,
            height: targetClientRect.height,
            bottom: targetClientRect.bottom + window.scrollY,
            right: targetClientRect.right + window.scrollX,
          };

          if (position === 'bottom') {
            const targetHorizontalCenter = targetBoundingBox.left + targetBoundingBox.width / 2;
            messageElement.style.top = `${targetBoundingBox.bottom + 5}px`;
            messageElement.style.bottom = '';

            messageElement.style.left = `${targetHorizontalCenter}px`;
            messageElement.style.right = '';
            messageElement.style.transform = `translateX(-50%)`;
          } else if (position === 'right') {
            const targetVerticalCenter = targetBoundingBox.top + targetBoundingBox.height / 2;
            messageElement.style.top = `${targetVerticalCenter}px`;
            messageElement.style.bottom = '';

            messageElement.style.left = `${targetBoundingBox.right + 5}px`;
            messageElement.style.right = '';
            messageElement.style.transform = `translateY(-50%)`;
          } else {
            if (e.clientY + 200 > window.innerHeight) {
              messageElement.style.bottom = `${window.innerHeight - e.clientY + 20}px`;
              messageElement.style.top = '';
            } else {
              messageElement.style.top = `${e.clientY + 30}px`;
              messageElement.style.bottom = '';
            }

            if (e.clientX + 100 > window.innerWidth) {
              messageElement.style.right = `${window.innerWidth - e.clientX}px`;
              messageElement.style.left = '';
              messageElement.style.transform = ``;
            } else if (e.clientX - 100 < 0) {
              messageElement.style.left = `${e.clientX}px`;
              messageElement.style.right = '';
              messageElement.style.transform = ``;
            } else {
              messageElement.style.left = `${e.clientX}px`;
              messageElement.style.right = '';
              messageElement.style.transform = `translateX(-50%)`;
            }
          }

          if (!force) messageElement.style.display = 'block';
        },
        tooltipRapidfire || immediate ? 0 : 300
      ) as any as number;
    },
    [allowRapidfire, force, handleDismiss, immediate, position]
  );

  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) {
    return null;
  }

  return (
    <Fragment>
      {message &&
        enabled &&
        !force &&
        createPortal(<TooltipMessage ref={messageRef}>{message}</TooltipMessage>, document.body)}
      {message &&
        enabled &&
        force &&
        createPortal(
          <TooltipMessage ref={messageRef} style={{ display: 'block' }}>
            {message}
          </TooltipMessage>,
          document.body
        )}
      <TooltipChildrenWrapper
        ref={containerRef}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleDismiss}
        onMouseMove={handleMouseEnter}
      >
        {children}
      </TooltipChildrenWrapper>
    </Fragment>
  );
};

export default Tooltip;
