import { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { FormProvider, useForm } from 'react-hook-form';
import { useQuery, useQueryClient } from 'react-query';
import { useNavigate, useSearchParams } from 'react-router-dom';

import { useDebounce } from '@material-hu/hooks/useDebounce';
import { useModal } from '@material-hu/hooks/useModal';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import * as HuAuth from '@material-hu/components/composed-components/auth';
import HuDialog from '@material-hu/components/design-system/Dialog';
import HuCircularProgress from '@material-hu/components/design-system/ProgressIndicators/Spinner';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import webBanner from 'src/assets/images/login-banner-web.webp';
import { ErrorCode } from 'src/constants/exceptions';
import { useAuth } from 'src/contexts/JWTContext';
import { useSettings } from 'src/contexts/SettingsContext';
import useGeneralError from 'src/hooks/useGeneralError';
import useHuGoTheme from 'src/hooks/useHuGoTheme';
import { getInstances, getOTP, resetPassword } from 'src/services/authService';
import { LoginErrors, type LoginInstance } from 'src/types/login';
import { type RequestError } from 'src/types/services';
import { formatTitle } from 'src/utils/helmetUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { handleUserLanguage } from 'src/utils/languages';
import {
  getMessageToRecoverPassword,
  getResetPasswordErrorMessage,
} from 'src/utils/login';
import { MIN_PASSWORD_LENGTH, validateRequired } from 'src/utils/validation';

import AzureLogin from './components/AzureLogin';
import GoogleLogin from './components/GoogleLogin';
import OktaLogin from './components/OktaLogin';
import TermsOfUseAndPrivacy from './components/TermsOfUseAndPrivacy';
import { loginKeys } from './queries';
import { loginRoutes } from './routes';

const Login = () => {
  const [email, setEmail] = useState(''); // needed because react-hook-form doesnt submit disabled input's values
  const [instances, setInstances] = useState<LoginInstance[]>([]);
  const [selectedInstance, setSelectedInstance] =
    useState<LoginInstance | null>(null);
  const [loading, setLoading] = useState(false);
  const [loadInstances, setLoadInstances] = useState(false);
  const [query, setQuery] = useState('');
  const [ssoLoading, setSsoLoading] = useState(false);
  const debouncedQuery = useDebounce(query);
  const HugoThemeProvider = useHuGoTheme();
  const { enqueueSnackbar } = useHuSnackbar();

  const { t } = useLokaliseTranslation('authentication');
  const { login, loginViaJanus, loginWithMFA, loginSaml } = useAuth();
  const { setPrimary, resetPrimary } = useSettings();
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const showGeneralError = useGeneralError();
  const [searchParams] = useSearchParams();
  const token = searchParams.get('bearerToken');

  useEffect(() => {
    (async () => {
      if (token) {
        const response = await loginSaml?.(token);
        const { instance } = response!;
        setPrimary(instance?.color);
      }
    })();
  }, [token]);

  useEffect(() => {
    if (!selectedInstance) {
      resetPrimary();
    } else {
      setPrimary(selectedInstance?.color);
    }
  }, [selectedInstance, setPrimary, resetPrimary]);

  useEffect(() => {
    if (window.location.hash === '#instances') {
      if (!hasInstances || selectedInstance) {
        navigate('');
      }
    }
  }, [window.location.hash]);

  const hasExistantInstances =
    instances.length === 0 && !!debouncedQuery.length;

  const hasInstances = instances.length > 0 || hasExistantInstances;

  const form = useForm({
    defaultValues: {
      email: '',
      password: '',
    },
  });

  const {
    handleSubmit,
    formState: { isSubmitting },
    setError,
    watch,
  } = form;

  const currentEmail = watch('email');
  const currentPassword = watch('password');

  const requestParams = {
    username: currentEmail,
    limit: 50,
    search: debouncedQuery || undefined,
  };

  const { isFetching: loadingCommunities } = useQuery(
    loginKeys.instances(requestParams),
    () => getInstances(requestParams),
    {
      enabled: loadInstances,
      select: response => response.data,
      onSuccess: response => {
        if (response) {
          setInstances(response);
          showSelectInstanceDrawer(true);
          if (response?.length) {
            setEmail(currentEmail);

            if (response.length === 1 && !debouncedQuery) {
              handleSelectInstance(response[0], currentEmail);
            } else {
              navigate('#instances');
            }
          }
        } else {
          setWrongCredentials('email');
          setLoadInstances(false);
        }
      },
      onError: (err: any) => {
        const code = err?.response?.data?.code;
        if (code === LoginErrors.USER_NOT_FOUND) {
          setWrongCredentials('email');
        } else {
          setError('email', {
            type: 'manual',
            message: t('ERROR_UNEXPECTED'),
          });
        }
        setLoadInstances(false);
      },
    },
  );

  const setWrongCredentials = (field: 'email' | 'password') => {
    setError(field, {
      type: 'manual',
      message: t(field === 'email' ? 'WRONG_USER' : 'WRONG_PASSWORD'),
    });
  };

  const clearInstances = () => {
    navigate('');
    setInstances([]);
    setQuery('');
    setSelectedInstance(null);
    setLoadInstances(false);
  };

  const startOtp = async (
    instance: LoginInstance,
    username: string,
    init2faToken?: string,
  ) => {
    const instanceId = instance.id;

    queryClient.prefetchQuery(loginKeys.otp.detail(instanceId, username), () =>
      getOTP(instanceId, username),
    );

    const state = init2faToken
      ? { instance, employeeInternalId: username, init2faToken }
      : { instance, employeeInternalId: username };

    navigate(loginRoutes.otp(), {
      state,
    });
  };

  const handleSelectInstance = (instance: LoginInstance, username = email) => {
    const { samlURI, forceOTP } = instance;

    if (samlURI) {
      window.location.replace(samlURI);
    }
    if (forceOTP) {
      setPrimary(instance?.color);

      return startOtp(instance, username);
    }

    navigate('');
    closeSelectInstanceDrawer();
    setSelectedInstance(instance);
  };

  const handleLogin = async (values: { email: string; password: string }) => {
    const employeeInternalId = values.email || email;

    const body = {
      employeeInternalId,
      instanceId: selectedInstance?.id!,
      password: values.password,
    };

    if (selectedInstance?.otpAfterRegularLogin) {
      const { init2faToken } = await loginWithMFA?.(body)!;
      return startOtp(selectedInstance, employeeInternalId, init2faToken);
    }

    const loginFn = selectedInstance?.useJanusForClassicLogin
      ? loginViaJanus
      : login;

    const { user, instance, accessToken, refreshToken, featureFlags } =
      await loginFn?.(body)!;

    const language = await handleUserLanguage(user, accessToken);

    if (!user?.hasPasswordChanged) {
      navigate(loginRoutes.updatePassword(), {
        state: {
          accessToken,
          refreshToken,
          user: {
            ...user,
            language,
          },
          instance,
          featureFlags,
        },
      });
    }
  };

  const submit = handleSubmit(async values => {
    setLoading(true);
    try {
      !selectedInstance ? setLoadInstances(true) : await handleLogin(values);
    } catch (err: unknown) {
      const error = err as RequestError;
      const code = error?.response?.data?.code;
      if (error.message === ErrorCode.NOT_ENOUGH_PERMISSIONS) {
        showGeneralError(err, t('NOT_ENOUGH_PERMISSIONS'));
      } else if (code === LoginErrors.INVALID_CREDENTIALS) {
        setWrongCredentials(hasInstances ? 'password' : 'email');
      } else {
        setError(hasInstances ? 'password' : 'email', {
          type: 'manual',
          message: t('ERROR_UNEXPECTED'),
        });
      }
    } finally {
      setLoading(false);
    }
  });

  const handleRecoverConfirmation = async () => {
    closeConfirmRecoverModal();
    const { id } = selectedInstance!;
    setLoading(true);
    try {
      await resetPassword(email, id);
      enqueueSnackbar({
        ...getMessageToRecoverPassword(t, selectedInstance?.userHasEmail!),
        variant: 'success',
      });
    } catch (err) {
      const error = err as RequestError;
      showGeneralError(
        err,
        getResetPasswordErrorMessage(t, error.response?.data?.code || ''),
      );
    } finally {
      setLoading(false);
    }
  };

  const handleRecoverPasswordOnClick = () => {
    openConfirmRecoverModal();
  };

  const handleSelectAnotherInstance = () => {
    showSelectInstanceDrawer(true);
  };

  if (token) {
    return (
      <Stack
        sx={{
          justifyContent: 'center',
          alignItems: 'center',
          height: '100%',
          width: '100%',
        }}
      >
        <HuCircularProgress />
      </Stack>
    );
  }

  const {
    drawer: selectInstanceDrawer,
    showDrawer: showSelectInstanceDrawer,
    closeDrawer: closeSelectInstanceDrawer,
  } = HuAuth.useSelectIntanceDrawer({
    onClose: clearInstances,
    loading: loadingCommunities,
    instances,
    onSelectInstance: (instance: { name: string; logo: string }) => {
      handleSelectInstance(instance as LoginInstance, email);
    },
    searchProps: {
      query,
      setQuery,
    },
  });

  const {
    modal: confirmRecoverModal,
    showModal: openConfirmRecoverModal,
    closeModal: closeConfirmRecoverModal,
  } = useModal(
    () => (
      <HuDialog
        title={t('FORGOT_PASSWORD_QUESTION')}
        textBody={t(
          selectedInstance?.userHasEmail
            ? 'CONFIRMATION_EMAIL_TO_YOU'
            : 'CONFIRMATION_EMAIL_TO_ADMIN',
        )}
        primaryButtonProps={{
          children: t(selectedInstance?.userHasEmail ? 'SEND_EMAIL' : 'ACCEPT'),
          onClick: () => {
            handleRecoverConfirmation();
          },
          loading,
        }}
        secondaryButtonProps={{
          children: t('CANCEL'),
          onClick: closeConfirmRecoverModal,
          disabled: loading,
        }}
        onClose={closeConfirmRecoverModal}
      />
    ),
    { fullWidth: true },
  );

  return (
    <Stack>
      <Helmet>
        <title>{formatTitle(t('LOGIN'))}</title>
      </Helmet>
      <HugoThemeProvider>
        <FormProvider {...form}>
          <HuAuth.LoginLayout
            showBackdrop={ssoLoading}
            banner={{
              src: webBanner,
              styles: {
                maxWidth: '45%',
                minWidth: 300,
                alignContent: 'center',
              },
            }}
          >
            <HuAuth.LoginForm
              title={
                selectedInstance ? (
                  <HuAuth.InstanceCard
                    name={selectedInstance?.name ?? ''}
                    logo={selectedInstance?.logo ?? ''}
                  />
                ) : (
                  <Typography
                    variant="globalXL"
                    fontWeight="fontWeightSemiBold"
                  >
                    {t('WELCOME')}
                  </Typography>
                )
              }
              hasInstanceSelected={!!selectedInstance}
              showAnotherInstanceButton={
                !!selectedInstance && (instances.length > 1 || !!query.length)
              }
              isSubmitting={isSubmitting || loading || loadingCommunities}
              submitDisabled={
                !!selectedInstance &&
                currentPassword.length < MIN_PASSWORD_LENGTH
              }
              formConfig={{
                email: {
                  rules: validateRequired(),
                },
                password: {
                  rules: validateRequired(),
                },
              }}
              callbacks={{
                onSubmit: submit,
                onRecoverPassword: handleRecoverPasswordOnClick,
                onSelectAnother: handleSelectAnotherInstance,
              }}
              sso={{
                azureButton: (
                  <AzureLogin
                    onLogin={() => {
                      setLoading(true);
                      setSsoLoading(true);
                    }}
                    onLoginFinally={() => {
                      setLoading(false);
                      setSsoLoading(false);
                    }}
                  />
                ),
                googleButton: (
                  <GoogleLogin
                    onLogin={() => {
                      setLoading(true);
                      setSsoLoading(true);
                    }}
                    onLoginFinally={() => {
                      setLoading(false);
                      setSsoLoading(false);
                    }}
                  />
                ),
                oktaButton: (
                  <OktaLogin
                    onLogin={() => {
                      setLoading(true);
                      setSsoLoading(true);
                    }}
                    onLoginFinally={() => {
                      setLoading(false);
                      setSsoLoading(false);
                    }}
                  />
                ),
              }}
              termsOfUseAndPrivacy={
                selectedInstance ? null : <TermsOfUseAndPrivacy />
              }
            />
          </HuAuth.LoginLayout>
        </FormProvider>
        {selectInstanceDrawer}
        {confirmRecoverModal}
      </HugoThemeProvider>
    </Stack>
  );
};

export default Login;
