import { useEffect, useRef } from 'react';
import useIdle from 'react-use/lib/useIdle';
import { useLocalStorageState } from 'ahooks';
import useBrowser from '@arcblock/react-hooks/lib/useBrowser';

import { getWebWalletUrl, checkSameProtocol } from '../Util/wallet';
import { detectWalletExtension } from '../Util';

// 默认最大空闲时间: 30min
const DEFAULT_MAX_IDLE_TIME = 1000 * 60 * 30;
// 可使用 localStorage.setItem('wallet_sw_keeper_disabled', 1) 来禁用嵌入 wallet iframe
const STORAGE_KEY_DISABLED = 'wallet_sw_keeper_disabled';
// iframe id, 如果存在多个 WebWalletSWKeeper 组件实例, 共享此 id, 保证只有一个 iframe
let id: string;

const injectIframe = (webWalletUrl: string) => {
  const iframe = document.createElement('iframe');
  iframe.title = 'abt wallet';
  iframe.id = id;
  iframe.style.width = '0';
  iframe.style.height = '0';
  iframe.style.border = '0';
  // https://stackoverflow.com/questions/27858989/iframe-with-0-height-creates-a-gap
  iframe.style.display = 'block';
  // fix: 页面自动滚动到底部问题 (https://github.com/blocklet/abt-wallet/issues/1160)
  //      top: 0 可能不是必须的, 但测试中发现, 如果不设置, 在某些特殊情况下似乎也会导致页面自动滚动到底部
  iframe.style.position = 'absolute';
  iframe.style.top = '0';
  iframe.src = `${webWalletUrl}?action=iframe`;
  document.body.appendChild(iframe);
};

const removeIframe = () => {
  const iframe = document.getElementById(id);
  if (iframe) {
    document.body.removeChild(iframe);
  }
};

const cleanup = () => {
  removeIframe();
  id = '';
};

const enable = (webWalletUrl: string) => {
  if (!id) {
    id = `web_wallet_sw_keeper_${Date.now()}`;
    injectIframe(webWalletUrl);
  }
};

interface WebWalletSWKeeperProps {
  webWalletUrl: string;
  maxIdleTime: number;
}

// 该组件通过嵌入一个 web wallet iframe 帮助 web wallet service worker 延最大空闲时间
function WebWalletSWKeeper({
  webWalletUrl,
  maxIdleTime = DEFAULT_MAX_IDLE_TIME,
}: WebWalletSWKeeperProps & {
  maxIdleTime?: number;
}) {
  // 渲染前先记录是否已经存在一个 WebWalletSWKeeper 实例
  const instanceExists = useRef(!!id);
  const isIdle = useIdle(maxIdleTime);
  // 如果已经存在一个 WebWalletSWKeeper 实例, cleanup 的时机应该由该实例控制
  const _cleanup = () => {
    if (!instanceExists.current) {
      cleanup();
    }
  };
  // 用户操作空闲时间超过 maxIdleTime 时禁用, 活跃时启用
  useEffect(() => {
    if (isIdle) {
      _cleanup();
    } else {
      enable(webWalletUrl);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isIdle]);
  // 组件销毁时自动清理
  useEffect(() => () => _cleanup(), []);
  return null;
}

export const withWebWalletSWKeeper = <P extends { webWalletUrl: string }>(Component: React.ComponentType<P>) => {
  // eslint-disable-next-line react/prop-types
  return function WithWebWalletSWKeeperComponent({ webWalletUrl, maxIdleTime, ...rest }: WebWalletSWKeeperProps & P) {
    const browser = useBrowser();
    // eslint-disable-next-line no-param-reassign
    webWalletUrl = webWalletUrl || getWebWalletUrl();
    const [disabled] = useLocalStorageState(STORAGE_KEY_DISABLED, {
      defaultValue: 1,
    });
    const extension = detectWalletExtension();
    const isSameProtocol = checkSameProtocol(webWalletUrl);
    const isWalletWebview = browser.wallet || browser.arcSphere;
    // 以下几种情况不会嵌入 wallet iframe :
    // - 通过设置 localStorage#wallet_sw_keeper_disabled = 1 明确禁止 (开发调试过程中可以使用, 避免控制台打印一堆日志影响调试)
    // - 检查到 wallet 浏览器插件
    // - webWalletUrl 与当前页面 url 的 protocol 不同
    // - wallet webview

    const props = { webWalletUrl, ...rest } as unknown as P;
    return (
      <>
        {!disabled && !extension && isSameProtocol && !isWalletWebview && (
          <WebWalletSWKeeper webWalletUrl={webWalletUrl} maxIdleTime={maxIdleTime} />
        )}
        <Component {...props} />
      </>
    );
  };
};

export default WebWalletSWKeeper;
