import noop from 'lodash/noop';
import { use, useState } from 'react';
import { Box } from '@mui/material';
import { useAsyncEffect, useCreation } from 'ahooks';
import { getConnectedAccounts, getSourceProvider } from '@arcblock/ux/lib/SessionUser/libs/utils';
import { LOGIN_PROVIDER } from '@arcblock/ux/lib/Util/constant';
import { SessionContext } from '@arcblock/did-connect-react/lib/Session';
import { PasskeyActions } from '@arcblock/did-connect-react/lib/Passkey';
import { useBrowser } from '@arcblock/react-hooks';

import type { ConnectedAccount, OAuthAccount, User, SessionContext as TSessionContext } from '../../../@types';
import ThirdPartyItem from './third-party-item';

type ConnectedAccountProps = OAuthAccount & {
  enabled: boolean;
};

export default function ThirdPartyLogin({ user }: { user: User }) {
  const { session } = use<TSessionContext>(SessionContext);
  const [oauthConfigs, setOauthConfigs] = useState<Record<string, ConnectedAccountProps>>({});
  const { getOAuthConfigs } = session.useOAuth();
  const browser = useBrowser();

  useAsyncEffect(async () => {
    const data = await getOAuthConfigs({
      sourceAppPid: user?.sourceAppPid,
    });
    setOauthConfigs(data);
  }, [user?.sourceAppPid]);

  const availableProviders = useCreation(() => {
    const oauthList = Object.keys(oauthConfigs)
      .map((x) => {
        return {
          ...oauthConfigs[x],
          provider: x,
        };
      })
      .filter((x) => x.enabled);
    return oauthList;
  }, [oauthConfigs]);

  /**
   * 1. 如果 connectedAccount 中存在 auth0，则应该将 auth0 转换为 google, apple, github 中的一种。如果可以转换，则标记需要移除 auth0；如果无法转换，则保留 auth0
   * 2. 如果标记移除了 auth0，则还需要对 sourceProvider 进行转换，转换的值为 google, apple, github 中的一种
   * 3. 如果标记移除了 auth0，还需要将 auth0 的 userInfo 数据添加到转换后的 provider 上
   */
  const oauthAccounts = useCreation(() => {
    const connectedAccounts: ConnectedAccount[] = getConnectedAccounts(user);
    let removeAuth0 = false;
    let patchProvider = '';
    let sourceProvider = getSourceProvider(user);
    const auth0ConnectedAccount = connectedAccounts.find((x) => x.provider === LOGIN_PROVIDER.AUTH0) as OAuthAccount;
    if (auth0ConnectedAccount) {
      if (auth0ConnectedAccount.id.startsWith('google-oauth2|')) {
        if (!connectedAccounts.some((x) => x.provider === 'google')) {
          removeAuth0 = true;
          patchProvider = LOGIN_PROVIDER.GOOGLE;
          if (sourceProvider === LOGIN_PROVIDER.AUTH0) {
            sourceProvider = LOGIN_PROVIDER.GOOGLE;
          }
        }
      }
      if (auth0ConnectedAccount.id.startsWith('appleid|')) {
        if (!connectedAccounts.some((x) => x.provider === LOGIN_PROVIDER.APPLE)) {
          removeAuth0 = true;
          patchProvider = LOGIN_PROVIDER.APPLE;
          if (sourceProvider === LOGIN_PROVIDER.AUTH0) {
            sourceProvider = LOGIN_PROVIDER.APPLE;
          }
        }
      }
      if (auth0ConnectedAccount.id.startsWith('github|')) {
        if (!connectedAccounts.some((x) => x.provider === LOGIN_PROVIDER.GITHUB)) {
          removeAuth0 = true;
          patchProvider = LOGIN_PROVIDER.GITHUB;
          if (sourceProvider === LOGIN_PROVIDER.AUTH0) {
            sourceProvider = LOGIN_PROVIDER.GITHUB;
          }
        }
      }
    }

    const transformedProviders = availableProviders
      .map((x) => {
        if (x.provider === LOGIN_PROVIDER.AUTH0 && removeAuth0) {
          return null;
        }
        const findConnectedAccount =
          removeAuth0 && x.provider === patchProvider
            ? (connectedAccounts.find((i) => i.provider === LOGIN_PROVIDER.AUTH0) as OAuthAccount)
            : (connectedAccounts.find((i) => i.provider === x.provider) as OAuthAccount);
        if (findConnectedAccount) {
          return {
            ...x,
            provider: x.provider,
            did: findConnectedAccount.did,
            pk: findConnectedAccount.pk,
            userInfo: findConnectedAccount.userInfo,
            _bind: true,
            _rawProvider: findConnectedAccount.provider,
            _mainProvider: x.provider === sourceProvider,
          };
        }
        return {
          ...x,
          provider: x.provider,
          _rawProvider: x.provider,
          _mainProvider: x.provider === sourceProvider,
        };
      })
      .filter(Boolean)
      .sort((a, b) => {
        if (a?.order !== undefined && b?.order !== undefined) {
          return a.order - b.order;
        }
        if (a?.order !== undefined) {
          return -1;
        }
        return 1;
      });
    return transformedProviders;
  }, [user?.connectedAccounts, availableProviders]);

  const passkeyAccounts = useCreation(() => {
    const connectedAccounts: ConnectedAccount[] = getConnectedAccounts(user);
    const passkeyConnectedAccount = connectedAccounts.filter(
      (x) => x.provider === LOGIN_PROVIDER.PASSKEY && x.did !== user?.did
    ) as OAuthAccount[];
    return passkeyConnectedAccount.map((x) => {
      return {
        ...x,
        _bind: true,
      };
    });
  }, [user]);

  const walletAccounts = useCreation(() => {
    const connectedAccounts: ConnectedAccount[] = getConnectedAccounts(user);
    const walletConnectedAccount = connectedAccounts.filter(
      (x) => x.provider === LOGIN_PROVIDER.WALLET && x.did !== user?.did
    ) as OAuthAccount[];
    return walletConnectedAccount.map((x) => {
      return {
        ...x,
        _mainProvider: true,
      };
    });
  }, [user]);

  return (
    <Box
      sx={{
        gap: 1,
        display: 'grid',
        gridTemplateColumns: '1fr min-content',
      }}>
      {!oauthAccounts.length && !passkeyAccounts.length && !walletAccounts.length ? (
        <PasskeyActions
          behavior="only-new"
          action="connect"
          createMode="connect"
          createButtonText="Add Passkey"
          onSuccess={noop}
          onError={noop}
          dense
        />
      ) : (
        <>
          {oauthAccounts.map((account) => {
            return <ThirdPartyItem key={account?.provider} item={account as OAuthAccount} />;
          })}
          {passkeyAccounts.map((account) => {
            return <ThirdPartyItem key={account.id} item={account as OAuthAccount} />;
          })}
          {walletAccounts.map((account) => {
            return <ThirdPartyItem key={account.id} item={account as OAuthAccount} />;
          })}
          {browser.wallet || browser.arcSphere ? null : (
            <PasskeyActions
              behavior="only-new"
              action="connect"
              createMode="connect"
              createButtonText="Add New Passkey"
              onSuccess={noop}
              onError={noop}
              dense
              sx={{
                px: 1.5,
                py: 1,
                gap: 0.75,
              }}
            />
          )}
        </>
      )}
    </Box>
  );
}
