import PropTypes from 'prop-types';
import { Box } from '@mui/material';
import { LOGIN_PROVIDER, LOGIN_PROVIDER_NAME, LOGIN_PROVIDER_ICON_SIZE } from '@arcblock/ux/lib/Util/constant';
import { checkSameProtocol, getWebWalletUrl } from '@arcblock/ux/lib/Util/wallet';
import noop from 'lodash/noop';
import { useMemoizedFn, useCreation } from 'ahooks';
import Cookie from 'js-cookie';
import { detectWalletExtension, 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 { GA_LAST_LOGIN_METHOD } from '@arcblock/ux/lib/withTracker/constant';
import { translate } from '@arcblock/ux/lib/Locale/util';
import Empty from '@arcblock/ux/lib/Empty';
import { useBrowser } from '@arcblock/react-hooks';

import MobileLoginItem from './mobile-login-item';
import WebLoginItem from './web-login-item';
import LoginMethodItem from './login-method-item';
import WalletLoginOptions from './wallet-login-options';
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';
import translations from '../../assets/locale';

export default function ConnectProviderList({
  onSuccess = noop,
  onError = noop,
  size = 'small',
  tokenState,
  webWalletUrl = getWebWalletUrl(),
  tokenKey,
  passkeyBehavior = 'none',
  onReset = noop,
  providerList = [],
  slotProps = {},
  disableSwitchApp = false,
  forceUpdate = noop,
  magicToken = undefined,
  baseUrl = '/',
  customItems = [],
  qrcode = null,
  hideQRCode = false,
}) {
  const walletLoginRef = useRef(null);
  const webLoginRef = useRef(null);
  const passkeyLoginRef = useRef(null);
  const isSameProtocol = checkSameProtocol(webWalletUrl);
  const extension = detectWalletExtension();

  const browser = useBrowser();

  const { loginOAuth, logoutOAuth, t: oauthT, oauthState } = useOAuth();
  const { passkeyState } = usePasskey();
  const {
    extraParams,
    locale,
    connectState,
    plugins,
    setPlugins,
    setSelectedPlugin,
    getPlugin,
    showWalletOptions,
    setShowWalletOptions,
    lastLoginMethod,
  } = useStateContext();

  const t = useMemoizedFn((key, data = {}) => {
    return translate(translations, key, locale, 'en', data);
  });

  const handleLoginOAuth = useMemoizedFn(async (item) => {
    localStorage.setItem(GA_LAST_LOGIN_METHOD, item.provider);
    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, oauthT('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 onReset();
    tokenState.status = 'created';
    connectState.chooseMethod = 'wallet';
  });

  const handleMobileLoginClick = useMemoizedFn(async () => {
    localStorage.setItem(GA_LAST_LOGIN_METHOD, LOGIN_PROVIDER.WALLET);
    tokenState.reset();
    await onReset();
    tokenState.status = 'created';
    connectState.chooseMethod = 'wallet';
    const connectFn = walletLoginRef.current?.connect;
    connectState.retryConnect = () => {
      connectFn(defaultRetryConnect);
    };
  });

  const handleWebLoginClick = useMemoizedFn(() => {
    localStorage.setItem(GA_LAST_LOGIN_METHOD, LOGIN_PROVIDER.WALLET);
    // HACK: 在点击插件登录时，直接将状态置为扫码中，避免插件登录时的状态切换
    tokenState.status = 'scanned';
    connectState.chooseMethod = 'wallet-web';
    const connectFn = webLoginRef.current.connect;
    connectState.retryConnect = async () => {
      await onReset();
      tokenState.error = '';
      tokenState.status = 'scanned';
      connectFn();
    };
  });

  const handleWalletLoginBack = useMemoizedFn(() => {
    setShowWalletOptions(false);
  });

  const onlyWalletProvider = useCreation(() => {
    return providerList.length === 1 && providerList[0].provider === LOGIN_PROVIDER.WALLET;
  }, [providerList]);

  useEffect(() => {
    if (hideQRCode && onlyWalletProvider && !browser.mobile.any && !showWalletOptions) {
      setShowWalletOptions(true);
    }
  }, [hideQRCode, onlyWalletProvider, browser.mobile.any, setShowWalletOptions, showWalletOptions]);

  const showEmailLogin = providerList.some((p) => p.provider === LOGIN_PROVIDER.EMAIL);

  const emailPlugin = useEmailPlugin({ baseUrl });

  const setupMagicToken = useMemoizedFn(() => {
    if (
      magicToken &&
      providerList.some((provider) => provider.name === LOGIN_PROVIDER.EMAIL) &&
      plugins.some((plugin) => plugin.name === LOGIN_PROVIDER.EMAIL)
    ) {
      const plugin = getPlugin(LOGIN_PROVIDER.EMAIL);
      if (plugin.state.status === 'idle') {
        localStorage.setItem(GA_LAST_LOGIN_METHOD, LOGIN_PROVIDER.EMAIL);
        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]);

  const renderProviderList = providerList
    .map((item) => {
      if (item.provider === LOGIN_PROVIDER.WALLET) {
        if (browser.mobile.any && !showWalletOptions) {
          return (
            <MobileLoginItem
              t={t}
              isLatest={lastLoginMethod && lastLoginMethod === LOGIN_PROVIDER.WALLET}
              key={LOGIN_PROVIDER.WALLET}
              ref={walletLoginRef}
              tokenState={tokenState}
              sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
              locale={locale}
              tokenKey={tokenKey}
              disableSwitchApp={disableSwitchApp}
              onClick={handleMobileLoginClick}
            />
          );
        }
        // 是否显示 web 登录项
        // 1. 如果隐藏了二维码，会在新页面显示 wallet 登录项
        // 2. 如果显示了二维码，不需要在新页面显示 wallet 登录项，根据是否是同一协议、是否安装了钱包扩展、是否是移动端来判断是否显示
        const shouldShowWebLoginItem =
          hideQRCode || (!hideQRCode && (isSameProtocol || extension) && (!browser.mobile.any || extension));
        return shouldShowWebLoginItem ? (
          <WebLoginItem
            t={t}
            isLatest={lastLoginMethod && lastLoginMethod === LOGIN_PROVIDER.WALLET}
            key={LOGIN_PROVIDER.WALLET}
            ref={webLoginRef}
            tokenState={tokenState}
            webWalletUrl={webWalletUrl}
            sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
            disableSwitchApp={disableSwitchApp}
            autoConnect={!hideQRCode}
            onClick={() => {
              if (!hideQRCode) {
                handleWebLoginClick();
              } else {
                setShowWalletOptions(true);
              }
            }}
          />
        ) : null;
      }

      if (item.provider === LOGIN_PROVIDER.PASSKEY) {
        if (passkeyBehavior === 'none') {
          return null;
        }
        return (
          <PasskeyLoginItem
            key={LOGIN_PROVIDER.PASSKEY}
            ref={passkeyLoginRef}
            onSuccess={onSuccess}
            onError={onError}
            tokenState={tokenState}
            behavior={passkeyBehavior}
            sx={[size === 'small' ? { p: 1 } : { p: 2 }]}
            onClick={() => {
              localStorage.setItem(GA_LAST_LOGIN_METHOD, LOGIN_PROVIDER.PASSKEY);
              const connectFn = passkeyLoginRef.current.connect;
              connectState.chooseMethod = 'passkey';
              connectState.retryConnect = () => {
                passkeyState.verifying = true;
                connectState.chooseMethod = 'passkey';
                connectFn();
              };
            }}
            slotProps={{
              icon: {
                sx: {
                  fontSize: LOGIN_PROVIDER_ICON_SIZE,
                  '& svg': {
                    fontSize: LOGIN_PROVIDER_ICON_SIZE,
                    width: '1em',
                    height: '1em',
                  },
                },
              },
            }}
          />
        );
      }

      if (item.provider === LOGIN_PROVIDER.EMAIL) {
        const findEmailPlugin = plugins.find((p) => p.name === LOGIN_PROVIDER.EMAIL);
        if (!findEmailPlugin) {
          return null;
        }
        return findEmailPlugin?.renderListItem({
          key: findEmailPlugin.name,
          sx: [size === 'small' ? { p: 1 } : { p: 2 }],
          // forceUpdate,
        });
      }

      if (item.type === 'oauth') {
        return (
          <LoginMethodItem
            t={t}
            isLatest={lastLoginMethod && lastLoginMethod === item.provider}
            key={item.provider}
            title={LOGIN_PROVIDER_NAME[item.provider]}
            icon={
              <ProviderIcon
                provider={item.provider}
                sx={{
                  transform: 'scale(0.95)',
                  width: LOGIN_PROVIDER_ICON_SIZE,
                  height: LOGIN_PROVIDER_ICON_SIZE,
                  color: 'text.primary',
                }}
              />
            }
            onClick={() => {
              handleLoginOAuth(item);
              connectState.retryConnect = () => {
                handleLoginOAuth(item);
              };
            }}
            sx={[size === 'small' ? { p: 1.2 } : { p: 2 }]}
          />
        );
      }

      return null;
    })
    .filter(Boolean);

  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',
              ...(showWalletOptions && !browser.mobile.any ? { alignItems: 'center', gap: 0 } : { gap: 1.5 }),
            },
          ]}>
          {showWalletOptions && !browser.mobile.any ? (
            <WalletLoginOptions
              qrcode={qrcode}
              tokenState={tokenState}
              webWalletUrl={webWalletUrl}
              size={size}
              disableSwitchApp={disableSwitchApp}
              tokenKey={tokenKey}
              walletLoginRef={walletLoginRef}
              webLoginRef={webLoginRef}
              onMobileLoginClick={handleMobileLoginClick}
              onWebLoginClick={handleWebLoginClick}
              onBack={onlyWalletProvider ? undefined : handleWalletLoginBack}
            />
          ) : (
            <Box
              sx={{
                display: 'grid',
                gridTemplateColumns: 'repeat(12, 1fr)' /* 灵活的12列基础网格 */,
                gap: 1.5,
                '.arc-login-item:nth-child(-n+3)': {
                  gridColumn: 'span 12',
                },
                // 当登录项大于等于 5 时，第三个登录项增加一个 marginBottom，用于分割独占一行的和一行多个的登录项
                '.arc-login-item:nth-child(3):nth-last-child(n+3)': {
                  mb: 1,
                },
                '.arc-login-item:nth-child(4):last-child': {
                  gridColumn: 'span 12',
                },
                '.arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2), .arc-login-item:nth-child(4):nth-last-child(2) ~ .arc-login-item':
                  {
                    gridColumn: 'span 6',
                    justifyContent: 'center',
                    '.arc-login-item__body, .other-item-icon': {
                      display: 'none',
                    },
                  },
                '.arc-login-item:nth-child(4):nth-last-child(3), .arc-login-item:nth-child(4):nth-last-child(3) ~ .arc-login-item':
                  {
                    justifyContent: 'center',
                    gridColumn: 'span 4',
                    '.arc-login-item__body, .other-item-icon': {
                      display: 'none',
                    },
                  },
                '.arc-login-item:nth-child(4):nth-last-child(4), .arc-login-item:nth-child(4):nth-last-child(4) ~ .arc-login-item':
                  {
                    gridColumn: 'span 3',
                    justifyContent: 'center',
                    '.arc-login-item__body, .other-item-icon': {
                      display: 'none',
                    },
                  },
                '&:has(.arc-login-item:nth-child(8)) .arc-login-item:nth-child(n+4)': {
                  gridColumn: 'span 3',
                  justifyContent: 'center',
                  '.arc-login-item__body, .other-item-icon': {
                    display: 'none',
                  },
                },
              }}>
              {renderProviderList.length > 0 ? (
                renderProviderList
              ) : (
                <Empty
                  className="arc-login-item"
                  sx={{
                    '.empty-content': {
                      textAlign: 'center',
                    },
                  }}>
                  {t('noAuthenticationProvider')}
                </Empty>
              )}
            </Box>
          )}

          {customItems.map((item) => (!item ? null : item))}
        </Box>
      </Box>
    </Box>
  );
}

ConnectProviderList.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']),
  onReset: PropTypes.func,
  providerList: PropTypes.array,
  slotProps: PropTypes.object,
  disableSwitchApp: PropTypes.bool,
  forceUpdate: PropTypes.func,
  magicToken: PropTypes.string,
  baseUrl: PropTypes.string,
  customItems: PropTypes.arrayOf(PropTypes.node),
  qrcode: PropTypes.node,
  hideQRCode: PropTypes.bool,
};
