import PropTypes from 'prop-types';
import { useImperativeHandle, useState } from 'react';
import styled from '@emotion/styled';
import {
  Alert,
  Box,
  Button,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  TextField,
} from '@mui/material';
import isEmail from 'validator/lib/isEmail';
import { Icon } from '@iconify/react';
import passKeyRoundedIcon from '@iconify-icons/material-symbols/passkey-rounded';
import { useMemoizedFn } from 'ahooks';
import Toast from '@arcblock/ux/lib/Toast';
import Typography from '@arcblock/ux/lib/Typography';
import { getCookieOptions } from '@arcblock/ux/lib/Util';
import trim from 'lodash/trim';
import base64 from 'base64-url';
import Cookie from 'js-cookie';
import noop from 'lodash/noop';

import { usePasskey } from './context';
import { getWebAuthnErrorMessage } from '../utils';
import { VERIFY_CODE_LENGTH } from './constants';

function PasskeyDialog({ ref, extraParams = {}, createMode = 'connect', action, onSuccess = noop, onError = noop }) {
  const { api, locale, t, loginPasskey, logoutPasskey, passkeyState, connectPasskey } = usePasskey();
  const isEmailRequired = createMode === 'register';
  const [isEmailVerified, setIsEmailVerified] = useState(false);
  const handleOpenDialog = useMemoizedFn(() => {
    passkeyState.openDialog = true;
    passkeyState.error = '';
    passkeyState.creatingStatus = '';
    passkeyState.verifyingStatus = '';
    const initialEmail = passkeyState.email;
    setIsEmailVerified(initialEmail && isEmailRequired ? !isEmail(initialEmail) : false);
  });
  const handleCloseDialog = useMemoizedFn(() => {
    passkeyState.openDialog = false;
    passkeyState.error = '';
    passkeyState.creatingStatus = '';
    passkeyState.verifyingStatus = '';
    const initialEmail = passkeyState.email;
    setIsEmailVerified(initialEmail && isEmailRequired ? !isEmail(initialEmail) : false);
  });

  const handleSendCode = useMemoizedFn(async (e) => {
    e.preventDefault();
    if (isEmailRequired && !isEmail(passkeyState.email)) {
      setIsEmailVerified(true);
      return;
    }
    setIsEmailVerified(false);
    passkeyState.loading = true;
    try {
      await api.post(`../kyc/email/send?locale=${locale}`, { email: passkeyState.email });
      passkeyState.sent = true;
      Toast.success(t('codeSentSuccess'));
    } catch (error) {
      Toast.error(error.message);
      document.getElementById('email-input')?.focus();
    } finally {
      passkeyState.loading = false;
    }
  });

  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) {
        passkeyState.verifyingStatus = 'succeed';
        onSuccess({ ...result, encrypted: false }, (val) => val);
      }
    } catch (err) {
      console.error('Failed to verify passkey', err);
      const errorMessage = getWebAuthnErrorMessage(err, t('verifyPasskeyFailed'));
      passkeyState.verifying = false;
      passkeyState.error = errorMessage;
      passkeyState.verifyingStatus = 'error';
      await logoutPasskey();
      onError(new Error(errorMessage));
    }
  });

  const handleCreatePasskey = useMemoizedFn(async () => {
    if (isEmailRequired && !isEmail(passkeyState.email)) {
      setIsEmailVerified(true);
      return;
    }
    setIsEmailVerified(false);
    passkeyState.creating = true;
    passkeyState.error = '';
    passkeyState.creatingStatus = '';

    try {
      const result = await connectPasskey({
        ...extraParams,
        action,
        purpose: isEmailRequired ? 'register' : 'connect',
        email: isEmailRequired ? base64.encode(passkeyState.email) : '',
      });
      if (!result) {
        // 用户取消的操作
        passkeyState.error = t('cancelAuth');
        passkeyState.creatingStatus = 'error';
        passkeyState.creating = false;
        passkeyState.verifying = false;
        passkeyState.verifyingStatus = '';
        return;
      }
      passkeyState.creating = false;
      passkeyState.openDialog = false;
      passkeyState.creatingStatus = 'succeed';

      if (createMode === 'connect') {
        onSuccess({ ...result, encrypted: false }, (val) => val);
      } else if (result?.credentialId) {
        handleVerifyPasskey(result.credentialId);
      }
    } catch (err) {
      console.error('Failed to create passkey', err);
      passkeyState.creating = false;
      passkeyState.error = getWebAuthnErrorMessage(err, t('createPasskeyFailed'));
      passkeyState.creatingStatus = 'error';
    }
  });

  const handleVerifyCode = useMemoizedFn(async (e) => {
    if (e) e.preventDefault();
    passkeyState.loading = true;
    try {
      await api.post(`../kyc/email/verify?locale=${locale}`, { code: passkeyState.code });
      passkeyState.verified = true;
      handleCreatePasskey();
    } catch (error) {
      Toast.error(error.message);
      passkeyState.verified = false;
      document.getElementById(`code-input-${trim(passkeyState.code).length - 1}`)?.focus();
    } finally {
      passkeyState.loading = false;
    }
  });

  useImperativeHandle(ref, () => {
    return {
      open: handleOpenDialog,
      close: handleCloseDialog,
      handleVerifyCode,
    };
  }, [handleOpenDialog, handleCloseDialog, handleVerifyCode]);

  return (
    <StyledDialog
      open={passkeyState.openDialog}
      onClose={handleCloseDialog}
      fullWidth
      maxWidth="sm"
      fullScreen={window.innerWidth < 600}>
      <StyledDialogTitle>
        <KeyIcon>
          <Icon icon={passKeyRoundedIcon} fontSize={24} color="initial" />
        </KeyIcon>
        {t('createPasskey')}
      </StyledDialogTitle>
      <StyledDialogContent>
        <Typography variant="body1">{t('createPasskeyDesc1')}</Typography>
        <Typography variant="body1">{t('createPasskeyDesc2')}</Typography>
        {passkeyState.error && <Alert severity="error">{passkeyState.error}</Alert>}
        {isEmailRequired && (
          <Form>
            {!passkeyState.sent && (
              <TextField
                type="email"
                id="email-input"
                size="medium"
                placeholder={t('emailPlaceholder')}
                sx={{ width: '100%', '.MuiFormHelperText-root': { ml: 0 } }}
                value={passkeyState.email}
                onChange={(e) => {
                  passkeyState.email = e.target.value;
                  setIsEmailVerified(!isEmail(passkeyState.email));
                }}
                disabled={passkeyState.loading || passkeyState.creating}
                required
                error={isEmailVerified}
                helperText={isEmailVerified ? t('emailInvalid') : ''}
              />
            )}
            {passkeyState.sent && !passkeyState.verified && (
              <Stack
                direction="column"
                sx={{
                  justifyContent: 'center',
                  alignItems: 'center',
                  mb: 2,
                }}>
                <Typography variant="body2">{t('codeSentMessage', { email: passkeyState.email })}</Typography>
                <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between', gap: 2, mt: 0 }}>
                  {Array(VERIFY_CODE_LENGTH)
                    .fill('')
                    .map((_, index) => (
                      <TextField
                        // eslint-disable-next-line react/no-array-index-key
                        key={`code-input-${index}`}
                        value={trim(passkeyState.code[index]) || ''}
                        type="number"
                        margin="none"
                        onChange={(e) => {
                          const newCode = passkeyState.code.split('');
                          newCode[index] = e.target.value;
                          passkeyState.code = newCode.join('');
                          if (e.target.value && index < 5) {
                            document.getElementById(`code-input-${index + 1}`)?.focus();
                          }
                        }}
                        onKeyDown={(e) => {
                          if (e.key === 'Backspace' && !trim(passkeyState.code[index]) && index > 0) {
                            document.getElementById(`code-input-${index - 1}`)?.focus();
                          }
                        }}
                        disabled={passkeyState.loading}
                        required
                        id={`code-input-${index}`}
                        autoComplete="off"
                        slotProps={{
                          htmlInput: {
                            maxLength: 1,
                            style: { textAlign: 'center', fontSize: '1.5rem' },
                            autoComplete: 'off',
                          },
                        }}
                      />
                    ))}
                </Box>
              </Stack>
            )}
          </Form>
        )}
      </StyledDialogContent>
      <StyledDialogActions>
        <Button onClick={handleCloseDialog} sx={{ color: 'text.secondary' }}>
          {t('cancel')}
        </Button>
        {isEmailRequired && (
          <Button
            variant="contained"
            onClick={
              // eslint-disable-next-line no-nested-ternary
              passkeyState.verified ? handleCreatePasskey : passkeyState.sent ? handleVerifyCode : handleSendCode
            }
            disabled={
              passkeyState.verifying ||
              passkeyState.creating ||
              !passkeyState.email ||
              (passkeyState.sent && trim(passkeyState.code).length !== VERIFY_CODE_LENGTH)
            }>
            {(passkeyState.loading || passkeyState.creating) && <CircularProgress size={16} sx={{ mr: 1 }} />}
            {/* eslint-disable-next-line no-nested-ternary */}
            {passkeyState.verified ? t('createPasskey') : passkeyState.sent ? t('verifyButton') : t('sendCodeButton')}
          </Button>
        )}
        {!isEmailRequired && (
          <Button
            variant="contained"
            color="primary"
            onClick={handleCreatePasskey}
            disabled={
              passkeyState.loading ||
              passkeyState.creating ||
              (isEmailRequired && (!passkeyState.email || !passkeyState.verified))
            }
            startIcon={
              (passkeyState.loading || passkeyState.creating) && (
                <CircularProgress size={16} sx={{ mr: 1 }} color="inherit" />
              )
            }>
            {passkeyState.loading || passkeyState.creating ? t('creatingPasskey') : t('createPasskey')}
          </Button>
        )}
      </StyledDialogActions>
    </StyledDialog>
  );
}
PasskeyDialog.propTypes = {
  extraParams: PropTypes.object,
  createMode: PropTypes.string,
  action: PropTypes.string.isRequired,
  onSuccess: PropTypes.func,
  onError: PropTypes.func,
  ref: PropTypes.any.isRequired,
};

export default PasskeyDialog;

const StyledDialog = styled(Dialog)`
  .MuiDialog-paper {
    border-radius: 12px;
    max-width: 440px;
  }
`;

const StyledDialogTitle = styled(DialogTitle)`
  text-align: center;
  padding: 24px 24px 16px;
  font-size: 20px;
  font-weight: 600;
`;

const StyledDialogContent = styled(DialogContent)`
  padding: 0 24px;

  .MuiTypography-body1 {
    font-size: 14px;
    line-height: 1.5;
    margin-bottom: 16px;
  }

  .MuiTextField-root {
    margin-top: 16px;
    width: 100%;
  }
`;

const StyledDialogActions = styled(DialogActions)`
  text-align: center;
  padding: 16px 24px 24px;

  .MuiButton-root {
    text-transform: none;
    font-size: 14px;
    border-radius: 6px;
    padding: 6px 16px;
  }
`;

const KeyIcon = styled('div')`
  width: 48px;
  height: 48px;
  margin: 0 auto 16px;
  background-color: #f6f8fa;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;

  svg {
    width: 32px;
    height: 32px;
  }
`;

const Form = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  gap: 24px;

  input[type='number']::-webkit-inner-spin-button,
  input[type='number']::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
  div.Mui-focused input {
    caret-color: ${(props) => props.theme.palette.primary.main};
  }
`;
