import { useDebounceFn, useLatest } from 'ahooks';
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Box } from '@mui/material';
import { styled } from '../Theme';
import DidAddress, { HTMLDidAddressElement, IDidAddressProps } from './did-address';
import { getFontSize } from '../Util';

/**
 * 根据父容器宽度自动切换 compact 模式
 *
 * 实现逻辑:
 * - DidAddress 外层包裹一个容器, 其宽度自动撑满父容器宽度 (即这个容器需要是块级元素或 100% 宽的 inline-block)
 * - DidAddress 本身以 inline 形式渲染 (方便探测 did-address 的 full-width)
 * - 组件 mounted 时记录 did address 的 full-width (非 compact 模式的宽度)
 * - 监听容器宽度变化, 当容器宽度变化时, 对比容器宽度和 did address full-width, => 切换 compact 模式
 */
export default function ResponsiveDidAddress({
  ref = undefined,
  style = {},
  className = undefined,
  component = 'span',
  size = 0,
  ...rest
}: IResponsiveDidAddressProps & {
  ref?: React.Ref<unknown>;
}) {
  const [compact, setCompact] = useState(false);
  const isCompact = useLatest(compact);
  // did address 完整显示时的宽度
  const [addressFullWidth, setAddressFullWidth] = useState<number | null | undefined>(null);
  const [containerWidth, setContainerWidth] = useState<number | null | undefined>(null);
  const [loading, setLoading] = useState(true);
  const { run: delaySetLoadingFalse } = useDebounceFn(() => setLoading(false), {
    wait: 50,
  });
  const containerRef = useRef<HTMLDivElement>(null);
  const innerRef = useRef<HTMLDidAddressElement>(null);

  useImperativeHandle(
    ref,
    () =>
      new Proxy(
        {},
        {
          get(target: any, key: keyof HTMLDidAddressElement) {
            return innerRef.current?.[key];
          },
        }
      )
  );

  // 存储完整显示时 address 组件的宽度
  useEffect(() => {
    const innerWidth = innerRef.current?.offsetWidth || 0;
    let innerObserver: ResizeObserver | null = null;
    let containerObserver: ResizeObserver | null = null;

    // 初始化宽度
    setAddressFullWidth(innerWidth);
    setContainerWidth(containerRef.current?.offsetWidth);

    // 由于自定义字体的缘故，innerRef 的初始宽度可能发生二次改变，使用 observer 监听捕获
    innerObserver = new ResizeObserver(() => {
      if (!isCompact.current && innerRef.current?.offsetWidth !== innerWidth) {
        setAddressFullWidth(innerRef.current?.offsetWidth);
        delaySetLoadingFalse();
      }
    });
    containerObserver = new ResizeObserver(() => {
      setContainerWidth(containerRef.current?.offsetWidth);
    });

    innerObserver.observe(innerRef.current!.getEl());
    containerObserver.observe(containerRef.current!);
    delaySetLoadingFalse();

    return () => {
      innerObserver?.disconnect();
      containerObserver?.disconnect();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (containerWidth && addressFullWidth) {
      setCompact(containerWidth < addressFullWidth);
    }
  }, [containerWidth, addressFullWidth]);

  return (
    <Root
      as={component}
      ref={containerRef}
      style={style}
      className={className}
      size={size}
      {...(component === 'span'
        ? {}
        : {
            inline: rest.inline,
          })}>
      <StyledDidAddress
        style={{
          position: loading ? 'absolute' : 'static',
          left: loading ? '-99999px' : 'auto',
        }}
        {...rest}
        size={size}
        component={component}
        inline
        compact={compact}
        ref={innerRef}
      />
      {/* placeholder */}
      {loading && <div style={{ width: '100%', height: `${(size || 16) * 1.5}px` }} />}
    </Root>
  );
}

export interface IResponsiveDidAddressProps extends IDidAddressProps {
  style?: React.CSSProperties;
  className?: string;
  component?: React.ElementType;
}

type RootProps = {
  size: number;
  inline?: boolean;
};

const Root = styled(Box, { shouldForwardProp: (prop) => prop !== 'size' })<RootProps>`
  display: block;
  width: 100%; /* flex & column & align-items: flex-start 时, 块级元素不会自动铺满容器 */
  font-size: ${({ size }) => getFontSize(size)};
  overflow: hidden;
  ${({ inline }) =>
    inline &&
    `
    display: inline-block;
    width: 100%;
  `}
`;

const StyledDidAddress = styled(DidAddress)`
  && {
    max-width: none;
  }
  .did-address-text {
    /* 禁止文本 Ellipsis/截断, 以便测量真实的宽度 */
    white-space: nowrap;
    overflow: visible;
    text-overflow: unset;
  }
`;
