import PropTypes from 'prop-types';
import { Box } from '@mui/material';
import { LOGIN_PROVIDER, LOGIN_PROVIDER_NAME } from '@arcblock/ux/lib/Util/constant';
import { getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
import noop from 'lodash/noop';
import { useMemoizedFn } from 'ahooks';
import Cookie from 'js-cookie';
import { getCookieOptions } from '@arcblock/ux/lib/Util';
import { useEffect, useRef } from 'react';
import { mergeSx } from '@arcblock/ux/lib/Util/style';
import ProviderIcon from '@arcblock/ux/lib/DIDConnect/provider-icon';

import MobileLoginItem from './mobile-login-item';
import WebLoginItem from './web-login-item';
import LoginMethodItem from './login-method-item';
import { useOAuth } from '../../../OAuth';
import { useStateContext } from '../../contexts/state';
import { getApiErrorMessage, getAppId, logger } from '../../../utils';
import PasskeyLoginItem from './passkey-login-item';
import { usePasskey } from '../../../Passkey';
import { useEmailPlugin } from '../../plugins';

export default function ConnectChooseList({
  onSuccess = noop,
  onError = noop,
  size = 'small',
  tokenState,
  webWalletUrl = getWebWalletUrl(),
  tokenKey,
  passkeyBehavior = 'none',
  onRest = noop,
  showMobileLogin = true,
  showOAuthLogin = true,
  showPasskeyLogin = true,
  showWebLogin = true,
  showEmailLogin = true,
  oauthProviderList = [],
  slotProps = {},
  disableSwitchApp = false,
  forceUpdate = noop,
  magicToken = undefined,
  baseUrl = '/',
  customItems = [],
}) {
  const walletLoginRef = useRef(null);
  const webLoginRef = useRef(null);
  const passkeyLoginRef = useRef(null);
  const { loginOAuth, logoutOAuth, t, oauthState } = useOAuth();
  const { passkeyState } = usePasskey();
  const { extraParams, locale, connectState, plugins, setPlugins, setSelectedPlugin, getPlugin } = useStateContext();

  const handleLoginOAuth = useMemoizedFn(async (item) => {
    tokenState.reset();
    oauthState.reset({
      status: 'scanned',
    });
    passkeyState.reset();
    connectState.chooseMethod = item.provider;

    const sourceAppPid = extraParams?.sourceAppPid;
    try {
      oauthState.loading = true;
      oauthState.status = 'scanned';
      const loginResult = await loginOAuth(item, {
        action: tokenState.action,
        ...extraParams,
      });
      const cookieOptions = getCookieOptions({ returnDomain: false });
      Cookie.remove('connected_did', cookieOptions);
      Cookie.remove('connected_pk', cookieOptions);
      Cookie.remove('connected_wallet_os', cookieOptions);

      if (loginResult?.sessionToken) {
        await onSuccess(
          {
            ...loginResult,
            encrypted: false,
          },
          (val) => val,
          {
            sourceAppPid,
            connected_app: getAppId(tokenState.appInfo, tokenState.memberAppInfo),
          }
        );
        oauthState.loading = false;
        oauthState.status = 'succeed';
      }
    } catch (e) {
      logger.error(`Failed login OAuth: ${item.provider}`, e);
      const errorMessage = getApiErrorMessage(e, t('loginOAuthFailed'));
      oauthState.loading = false;
      oauthState.error = errorMessage;
      oauthState.status = 'error';
      await logoutOAuth({ provider: item.provider });
      onError(new Error(errorMessage));
    }
  });

  const defaultRetryConnect = useMemoizedFn(async () => {
    tokenState.reset();
    await onRest();
    tokenState.status = 'created';
    connectState.chooseMethod = 'wallet';
  });

  const emailPlugin = useEmailPlugin({ baseUrl });

  const setupMagicToken = useMemoizedFn(() => {
    if (magicToken && showEmailLogin && plugins.some((plugin) => plugin.name === LOGIN_PROVIDER.EMAIL)) {
      const plugin = getPlugin(LOGIN_PROVIDER.EMAIL);
      if (plugin.state.status === 'idle') {
        plugin.state.reset();
        plugin.state.magicToken = magicToken;
        connectState.chooseMethod = LOGIN_PROVIDER.EMAIL;
        setSelectedPlugin(plugin);
        forceUpdate();
      }
    }
  });

  // 考虑到切换账号的情况，此时 showEmailLogin 会根据当前上下文的 blocklet 发生变化，所以需要监听 showEmailLogin 的变化
  useEffect(() => {
    const finalList = [];
    if (showEmailLogin) {
      // 尽可能确保在 baseUrl 不变的情况下，不更改 email 插件的数据，以确保页面的展示不会出现闪烁
      const prevEmailPlugin = getPlugin(LOGIN_PROVIDER.EMAIL);
      if (prevEmailPlugin && prevEmailPlugin.baseUrl === emailPlugin.baseUrl) {
        finalList.push(prevEmailPlugin);
      } else {
        finalList.push(emailPlugin);
      }
    }
    setPlugins(finalList);
    connectState.retryConnect = defaultRetryConnect;
    // HACK: 必须要设置延迟，不然拿不到最新的 plugins 的值
    setTimeout(() => {
      setupMagicToken();
    }, 100);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [showEmailLogin]);

  return (
    <Box className="did-connect__choose" sx={mergeSx({ flex: 1 }, slotProps?.root?.sx)}>
      <Box
        sx={{
          display: 'flex',
          flexDirection: 'column',
          gap: 2,
        }}>
        <Box
          sx={[
            {
              display: 'flex',
              flexDirection: 'column',
              gap: 1.5,
            },
          ]}>
          {showMobileLogin && size !== 'small' && (
            <MobileLoginItem
              ref={walletLoginRef}
              tokenState={tokenState}
              sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
              locale={locale}
              tokenKey={tokenKey}
              disableSwitchApp={disableSwitchApp}
              onClick={async () => {
                tokenState.reset();
                await onRest();
                tokenState.status = 'created';
                connectState.chooseMethod = 'wallet';
                const connectFn = walletLoginRef.current?.connect;
                connectState.retryConnect = () => {
                  connectFn(defaultRetryConnect);
                };
              }}
            />
          )}
          {showWebLogin && (
            <WebLoginItem
              ref={webLoginRef}
              tokenState={tokenState}
              webWalletUrl={webWalletUrl}
              sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
              disableSwitchApp={disableSwitchApp}
              onClick={() => {
                // HACK: 在点击插件登录时，直接将状态置为扫码中，避免插件登录时的状态切换
                tokenState.status = 'scanned';
                connectState.chooseMethod = 'wallet-web';
                const connectFn = webLoginRef.current.connect;
                connectState.retryConnect = async () => {
                  await onRest();
                  tokenState.error = '';
                  tokenState.status = 'scanned';
                  connectFn();
                };
              }}
            />
          )}

          {showOAuthLogin || showPasskeyLogin || showEmailLogin ? (
            <Box
              sx={[
                {
                  display: 'flex',
                  flexDirection: 'column',
                  gap: 1.5,
                },
              ]}>
              {/* TODO: @zhanghan 需要将所有 method 转换为 plugin 的形式注入 */}
              {plugins.map((plugin) =>
                plugin.renderListItem({
                  key: plugin.name,
                  sx: [size === 'small' ? { p: 1 } : { p: 2 }],
                  // forceUpdate,
                })
              )}
              {showOAuthLogin
                ? oauthProviderList.map((item) => (
                    <LoginMethodItem
                      key={item.provider}
                      title={LOGIN_PROVIDER_NAME[item.provider]}
                      icon={
                        <ProviderIcon
                          provider={item.provider}
                          sx={{
                            transform: 'scale(0.95)',
                            width: 24,
                            height: 24,
                            color: 'text.primary',
                          }}
                        />
                      }
                      onClick={() => {
                        handleLoginOAuth(item);
                        connectState.retryConnect = () => {
                          handleLoginOAuth(item);
                        };
                      }}
                      sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
                    />
                  ))
                : null}
              {showPasskeyLogin ? (
                <PasskeyLoginItem
                  ref={passkeyLoginRef}
                  onSuccess={onSuccess}
                  onError={onError}
                  tokenState={tokenState}
                  behavior={passkeyBehavior}
                  sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
                  onClick={() => {
                    const connectFn = passkeyLoginRef.current.connect;
                    connectState.chooseMethod = 'passkey';
                    connectState.retryConnect = () => {
                      passkeyState.verifying = true;
                      connectState.chooseMethod = 'passkey';
                      connectFn();
                    };
                  }}
                  slotProps={{
                    icon: {
                      sx: {
                        fontSize: 24,
                        '& svg': {
                          fontSize: 24,
                          width: '1em',
                          height: '1em',
                        },
                      },
                    },
                  }}
                />
              ) : null}
            </Box>
          ) : null}
          {customItems.map((item) => (!item ? null : item))}
        </Box>
      </Box>
    </Box>
  );
}

ConnectChooseList.propTypes = {
  onSuccess: PropTypes.func,
  onError: PropTypes.func,
  size: PropTypes.oneOf(['small', 'normal', 'large']),
  tokenState: PropTypes.object.isRequired,
  webWalletUrl: PropTypes.string,
  tokenKey: PropTypes.string.isRequired,
  passkeyBehavior: PropTypes.oneOf(['none', 'both', 'only-existing', 'only-new']),
  onRest: PropTypes.func,
  showMobileLogin: PropTypes.bool,
  showOAuthLogin: PropTypes.bool,
  showPasskeyLogin: PropTypes.bool,
  showWebLogin: PropTypes.bool,
  showEmailLogin: PropTypes.bool,
  oauthProviderList: PropTypes.array,
  slotProps: PropTypes.object,
  disableSwitchApp: PropTypes.bool,
  forceUpdate: PropTypes.func,
  magicToken: PropTypes.string,
  baseUrl: PropTypes.string,
  customItems: PropTypes.arrayOf(PropTypes.node),
};
