import React, { useRef, useEffect, useState } from 'react';
import BScroll, { Options } from '@better-scroll/core';
import Pulldown from '@better-scroll/pull-down';
import Pulldup from '@better-scroll/pull-up';
import ScrollBar from '@better-scroll/scroll-bar';
import NestedScroll from '@better-scroll/nested-scroll';
import ObserveDOM from '@better-scroll/observe-dom';
import Bubble from './Bubble';
// import './index.scss';
import ReactResizeObserver from 'rc-resize-observer';

BScroll.use(NestedScroll)

export interface Position {
  x: number;
  y: number;
}

export interface ScrollProps {
  scrollRef?: (ref: BScroll) => void;
  nestedScroll?: true | undefined; // 是否是嵌套滚动 子Scroll需要设置成 true 父Scroll 不需要传
  scrollConfig: Partial<Options>; // BScroll 配置
  className?: string; // 额外的 class
  children?: React.ReactNode;
  loadingNode?: React.ReactNode; // 松手时候的loading展示组件
  successNode?: React.ReactNode; // 刷新成功的展示组件
  onPullingUp?: () => Promise<any>; // 上拉加载更多执行的方法
  onPullingDown?: () => Promise<any>; // 下拉刷新执行的方法
  onScroll?: (e: Position) => void; // 实时滚动监听
  ref?: any;
}

interface ScrollState {
  beforePullDown: boolean;
  isPullingDown: boolean;
  beforePullUp: boolean;
  isPullingUp: boolean;
  hasPullup: boolean;
  isEnd: boolean; // 数据全部加载完毕
}

const defaultState: ScrollState = {
  beforePullDown: true,
  isPullingDown: false,
  beforePullUp: true,
  isPullingUp: false,
  hasPullup: false,
  isEnd: false,
}
const defaultProps: ScrollProps = {
  scrollConfig: {
    scrollbar: {
      fade: true
    },
    click: true,
    preventDefault: false,
    tap: 'tap',
    bounceTime: 800,
    pullDownRefresh: undefined,
    pullUpLoad: undefined,
    // 实时派发
    // 关闭自动失焦，否则移动端对同一输入框点击会触发失焦再Focus
    // autoBlur: false
  }
}
const ScrollMb = React.forwardRef((props: ScrollProps, ref) => {
  const { scrollRef, nestedScroll, scrollConfig = {}, children = null, className, loadingNode, successNode, onPullingDown, onPullingUp, onScroll } = props;
  const [bScroll, setBScroll] = useState<BScroll>();
  const [bubbleY, setBubbleY] = useState(0);
  const [state, setState] = useState<ScrollState>(defaultState);
  const { beforePullDown, isPullingDown, beforePullUp, isPullingUp, hasPullup, isEnd } = state;
  const config = Object.assign(defaultProps.scrollConfig, scrollConfig);
  nestedScroll && (config.nestedScroll = nestedScroll);
  const wrapperEle = useRef<HTMLDivElement>(null);
  const childrenEle = useRef<HTMLDivElement>(null);
  React.useImperativeHandle(ref, () => {
    bScroll
  });
  useEffect(() => {
    bScroll && bScroll.destroy();
    init();
  }, [config.pullUpLoad]);
  const init = () => {
    // 根据配置选择性加载必要插件
    config.scrollbar && BScroll.use(ScrollBar)
    config.pullDownRefresh && BScroll.use(Pulldown);
    config.pullUpLoad && BScroll.use(Pulldup);
    config.observeDOM && BScroll.use(ObserveDOM);
    const bScroll: BScroll = new BScroll(wrapperEle.current as HTMLElement, config);
    typeof scrollRef === 'function' && scrollRef(bScroll);
    setBScroll(bScroll);
    /**
     * 下拉刷新
     */
    config.pullDownRefresh && bScroll.on('pullingDown', () => {
      if (!beforePullDown) return
      setState(state => {
        return { ...state, beforePullDown: false }
      });
      config.pullDownRefresh && onPullingDown && onPullingDown()
        .then(response => {
          setState(state => {
            return { ...state, isEnd: false }
          });
        })
        .finally(() => {
          setState(state => {
            return { ...state, isPullingDown: true }
          });
          setTimeout(() => {
            bScroll.finishPullDown();
          }, 400);
          setTimeout(() => {
            setState(state => {
              return { ...state, beforePullDown: true, isPullingDown: false }
            });
          }, (config.bounceTime || 800) + 400)
        });
    });
    /**
     * 上拉加载更多
     */
    config.pullUpLoad && bScroll.on('pullingUp', () => {
      if (!beforePullUp) {
        return
      }
      setState(state => {
        return { ...state, beforePullUp: false }
      });
      config.pullUpLoad && onPullingUp && onPullingUp()
        .then(response => {
          const { isEnd = false } = response || {};
          setTimeout(() => {
            bScroll.finishPullUp();
            setState(state => {
              return { ...state, isPullingUp: false, isEnd: isEnd }
            });
          }, 400);
          setState(state => {
            return { ...state, beforePullUp: true, isPullingUp: true }
          });
        });
    });
    bScroll.on('scroll', (pos: Position) => {
      onScroll && onScroll(pos);
      if (pos.y < 0) return
      config.pullDownRefresh && setBubbleY(Math.max(0, !isPullingDown ? pos.y - 40 : 0));
    });
  }

  return <ReactResizeObserver onResize={() => {
    bScroll?.refresh();
    setState(data => {
      return {
        ...data,
        hasPullup: childrenEle?.current?.clientHeight! > wrapperEle?.current?.clientHeight!
      }
    })
  }}>
    <div ref={wrapperEle} className={!className ? 'scroll-wrapper' : `scroll-wrapper ${className}`}>
      <div className="scroll-content">
        {
          config.pullDownRefresh !== false &&
          <div style={typeof config.pullDownRefresh === 'object' ? { height: config.pullDownRefresh.stop } : {}} className="pulldown-wrapper">
            {beforePullDown && !isPullingDown && <Bubble y={bubbleY} />}
            {!beforePullDown && !isPullingDown && (loadingNode || <Loading />)}
            {isPullingDown && (successNode || <Success />)}
          </div>
        }
        <ReactResizeObserver
          onResize={() => {
            bScroll?.refresh();
            setState(data => {
              return {
                ...data,
                hasPullup: childrenEle?.current?.clientHeight! > wrapperEle?.current?.clientHeight!
              }
            })
          }}
        >
          <div ref={childrenEle} className="scroll-content-children">{children}</div>
        </ReactResizeObserver>

        {
          // 当开启上拉加载更多  且当前滚动区域大于父容器时才展示 加载组件
          (scrollConfig as Partial<Options>)?.pullUpLoad === true &&
          <div className="pullup-wrapper">
            {
              hasPullup &&
              (
                !isEnd ?
                  (!isPullingUp ? <>
                    <Loading />
                    <span>加载中...</span>
                  </>
                    :
                    <Success label="加载成功！" />
                  )
                  :
                  <span>已经到底啦！不要再拉了</span>
              )
            }
          </div>
        }
      </div>
    </div>
  </ReactResizeObserver>
})
export default ScrollMb;
interface SuccessProps {
  label?: string;
}

export const Success: React.FC<SuccessProps> = ({ label = '刷新成功' }) => {
  return <div className="pulldown-success">
    <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3538" width="1em" height="1em"><path d="M878.08 731.275a32 32 0 0 1-54.88-32.939A360.79 360.79 0 0 0 874.667 512c0-200.299-162.368-362.667-362.667-362.667S149.333 311.701 149.333 512 311.701 874.667 512 874.667a360.79 360.79 0 0 0 186.315-51.446 32 32 0 0 1 32.928 54.88A424.779 424.779 0 0 1 512 938.667c-235.637 0-426.667-191.03-426.667-426.667S276.363 85.333 512 85.333 938.667 276.363 938.667 512c0 78.293-21.152 153.568-60.587 219.275zM374.581 489.45l84.342 83.989 190.432-190.72a32 32 0 0 1 45.29 45.227L481.632 641.28a32 32 0 0 1-45.227 0.064L329.42 534.794a32 32 0 1 1 45.162-45.343z" p-id="3539" fill="#999999"></path></svg>
    <span>{label}</span>
  </div>
}
export const Loading: React.FC = () => {
  return <svg className="pull-loading" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2742" width="1em" height="1em"><path d="M512 907c-24.852 0-45-20.148-45-45s20.148-45 45-45c168.446 0 305-136.554 305-305S680.446 207 512 207 207 343.554 207 512c0 24.852-20.148 45-45 45S117 536.852 117 512c0-218.152 176.848-395 395-395S907 293.848 907 512 730.152 907 512 907z" p-id="2743" fill="#999999"></path></svg>
}