import { Box, Typography as MuiTypography, Skeleton, type TypographyProps as MuiTypographyProps } from '@mui/material';
import { useCreation, useReactive, useSize } from 'ahooks';
import noop from 'lodash/noop';
import { useEffect, useRef } from 'react';

export interface TypographyProps extends MuiTypographyProps {
  fontSize?: string | number;
  minFontSize?: number;
  onShrink?: ({ fontSize, initialSize }: { fontSize: number; initialSize: number }) => void;
}

let minBrowserFontSize = -1;

function detectMinimumFontSize() {
  const testDiv = document.createElement('div');

  Object.assign(testDiv.style, {
    fontSize: '1px',
    visibility: 'hidden',
    position: 'absolute',
  });
  testDiv.textContent = 'Test';
  document.body.appendChild(testDiv);

  const size = parseFloat(getComputedStyle(testDiv).fontSize);
  document.body.removeChild(testDiv);
  return size;
}

export default function Typography({
  children = null,
  minFontSize = 12,
  /* 设置为 auto 时字体会自动收缩，直到达到浏览器最小字体限制 */
  fontSize = undefined,
  sx,
  onShrink = noop,
  ...rest
}: TypographyProps) {
  const refMock = useRef<HTMLSpanElement>(null);
  const refContainer = useRef<HTMLDivElement>(null);
  const state = useReactive<{
    fontSize?: number;
    loading: boolean;
    initialSize?: number;
  }>({
    // @ts-expect-error
    fontSize,
    loading: true,
  });
  const mockSize = useSize(refMock.current);
  const containerSize = useSize(refContainer.current);

  useEffect(() => {
    if (state.loading) {
      if (fontSize === 'auto') {
        if (mockSize && !state.initialSize) {
          const styleSize = getComputedStyle(refMock.current!).fontSize;
          state.initialSize = Number(styleSize.replace('px', ''));
          state.fontSize = state.initialSize;
        }
        if (minBrowserFontSize < 0) {
          minBrowserFontSize = detectMinimumFontSize();
        }
        // 达到浏览器最小字体限制，停止缩放
        if (state.fontSize! <= minBrowserFontSize) {
          state.loading = false;
          onShrink?.({ fontSize: state.fontSize!, initialSize: state.initialSize! });
        } else if (containerSize && mockSize) {
          // 超过容器宽度，尝试收缩
          if (containerSize.width < mockSize.width && state.fontSize! > minFontSize) {
            state.fontSize!--;
          } else {
            state.loading = false;
            onShrink?.({ fontSize: state.fontSize!, initialSize: state.initialSize! });
          }
        }
      } else {
        state.loading = false;
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fontSize, mockSize?.width, containerSize?.width]);

  const loading = useCreation(() => {
    if (fontSize === 'auto') {
      return state.loading;
    }
    return false;
  }, [fontSize, state.loading]);

  return loading ? (
    <Box
      ref={refContainer}
      sx={{
        flex: 1,
      }}>
      <MuiTypography {...rest} sx={sx} noWrap>
        <Skeleton variant="text" sx={{ fontSize: '1rem' }} />
      </MuiTypography>
      <MuiTypography
        ref={refMock}
        {...rest}
        sx={{
          ...sx,
          ...(state.fontSize ? { fontSize: `${state.fontSize}px !important` } : {}),
          position: 'fixed',
          top: -1000,
          left: -1000,
        }}
        noWrap>
        {children}
      </MuiTypography>
    </Box>
  ) : (
    <MuiTypography
      {...rest}
      sx={{
        ...sx,
        ...(state.fontSize ? { fontSize: `${state.fontSize}px !important` } : {}),
      }}>
      {children}
    </MuiTypography>
  );
}
