import { useEffect, useRef, useImperativeHandle } from 'react';
import trim from 'lodash/trim';
import PropTypes from 'prop-types';
import Cookie from 'js-cookie';
import { CircularProgress } from '@mui/material';
import { Icon } from '@iconify/react';
import { getCookieOptions } from '@arcblock/ux/lib/Util';
import { useMemoizedFn } from 'ahooks';
import passKeyRoundedIcon from '@iconify-icons/material-symbols/passkey-rounded';
import personAddRoundedIcon from '@iconify-icons/material-symbols/person-add-rounded';
import noop from 'lodash/noop';
import { mergeSx } from '@arcblock/ux/lib/Util/style';

import { usePasskey } from './context';
import { getWebAuthnErrorMessage, logger } from '../utils';
import LoginMethodItem from '../Connect/components/login-item/login-method-item';
import PasskeyDialog from './dialog';
import { VERIFY_CODE_LENGTH } from './constants';

function PasskeyAction({ action, onClick, title, state, dense = false, icon, ...rest }) {
  return (
    <LoginMethodItem
      {...rest}
      icon={
        state[action] ? (
          <CircularProgress
            size="1em"
            sx={{
              color: 'inherit',
              '& svg': {
                transform: 'scale(0.75)',
              },
            }}
          />
        ) : (
          icon
        )
      }
      iconScale={1}
      title={title}
      onClick={onClick}
      sx={mergeSx(dense ? { p: 1, backgroundColor: 'transparent' } : {}, rest?.sx)}
    />
  );
}

PasskeyAction.propTypes = {
  action: PropTypes.string.isRequired,
  onClick: PropTypes.func.isRequired,
  title: PropTypes.string.isRequired,
  state: PropTypes.object.isRequired,
  dense: PropTypes.bool,
  icon: PropTypes.any.isRequired,
};

export default function PasskeyActions({
  ref = null,
  action,
  behavior = 'none',
  onSuccess,
  onError,
  extraParams = {},
  createButtonText = '',
  createMode = 'register',
  dense = false,
  slotProps = {},
  sx = {},
  mode = 'normal',
  onClick = noop,
  isLatest = false,
}) {
  const currentAction = useRef('');
  const passkeyDialogRef = useRef(null);
  const { t, loginPasskey, logoutPasskey, passkeyState } = usePasskey();

  const handleVerifyPasskey = useMemoizedFn(async (credentialId = '') => {
    try {
      passkeyState.verifying = true;
      passkeyState.error = '';
      if (!credentialId) {
        passkeyState.verifyingStatus = '';
      }

      const result = await loginPasskey({
        ...extraParams,
        action,
        credentialId,
      });

      const cookieOptions = getCookieOptions({ expireInDays: 7 });
      Cookie.remove('connected_did', cookieOptions);
      Cookie.remove('connected_pk', cookieOptions);
      Cookie.remove('connected_wallet_os', cookieOptions);

      passkeyState.verifying = false;
      if (result?.sessionToken) {
        await onSuccess({ ...result, encrypted: false }, (val) => val);
        passkeyState.verifyingStatus = 'succeed';
      }
    } catch (err) {
      logger.error('Failed to verify passkey', err);
      const errorMessage = getWebAuthnErrorMessage(err, t('verifyPasskeyFailed'), t);
      passkeyState.verifying = false;
      passkeyState.error = errorMessage;
      passkeyState.verifyingStatus = 'error';
      await logoutPasskey();
      onError(new Error(errorMessage));
    }
  });

  const handlePaste = useMemoizedFn(
    (e) => {
      if (!passkeyState.sent) return;
      e.preventDefault();
      const pastedData = trim(e.clipboardData.getData('text/plain').slice(0, VERIFY_CODE_LENGTH));
      if (!/^\d+$/.test(pastedData)) return;
      passkeyState.code = pastedData.padEnd(VERIFY_CODE_LENGTH, ' ');
      const nextInput = document.getElementById(`code-input-${pastedData.length}`);
      if (nextInput) {
        nextInput.focus();
      }
    },
    [passkeyState]
  );

  useEffect(() => {
    document.addEventListener('paste', handlePaste);
    return () => {
      document.removeEventListener('paste', handlePaste);
    };
  }, [handlePaste]);

  const handleConnect = useMemoizedFn(() => {
    if (currentAction.current === 'verifying') {
      handleVerifyPasskey();
    } else if (currentAction.current === 'creating') {
      passkeyDialogRef.current.open();
    }
  });

  useImperativeHandle(ref, () => ({
    click: handleConnect,
  }));

  if (behavior === 'none') {
    return null;
  }

  return (
    <>
      {['both', 'only-existing'].includes(behavior) ? (
        <PasskeyAction
          isLatest={isLatest}
          t={t}
          action="verifying"
          state={passkeyState}
          title={t('usePasskey')}
          onClick={() => {
            currentAction.current = 'verifying';
            handleConnect();
            onClick();
          }}
          dense={dense}
          slotProps={slotProps}
          sx={sx}
          mode={mode}
          icon={<Icon icon={passKeyRoundedIcon} fontSize="1em" />}
        />
      ) : null}
      {['both', 'only-new'].includes(behavior) ? (
        <>
          <PasskeyAction
            t={t}
            action="creating"
            state={passkeyState}
            title={createButtonText || t('createPasskey')}
            onClick={() => {
              currentAction.current = 'creating';
              handleConnect();
              onClick();
            }}
            dense={dense}
            slotProps={slotProps}
            sx={sx}
            mode={mode}
            icon={<Icon icon={personAddRoundedIcon} fontSize="1em" />}
          />
          <PasskeyDialog
            ref={passkeyDialogRef}
            action={action}
            createMode={createMode}
            extraParams={extraParams}
            onSuccess={onSuccess}
            onError={onError}
          />
        </>
      ) : null}
    </>
  );
}

PasskeyActions.propTypes = {
  action: PropTypes.string.isRequired,
  onSuccess: PropTypes.func.isRequired,
  onError: PropTypes.func.isRequired,
  behavior: PropTypes.oneOf(['none', 'both', 'only-existing', 'only-new']),
  createButtonText: PropTypes.string,
  createMode: PropTypes.string,
  extraParams: PropTypes.object,
  dense: PropTypes.bool,
  slotProps: PropTypes.object,
  sx: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  mode: PropTypes.oneOf(['simple', 'normal']),
  onClick: PropTypes.func,
  ref: PropTypes.any,
  isLatest: PropTypes.bool,
};
