import { type ReactNode, useState } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { useMutation } from 'react-query';

import uniqBy from 'lodash/uniqBy';
import { IconCheck, IconRefresh, IconX } from '@material-hu/icons/tabler';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import Avatar from '@material-hu/components/design-system/Avatar';
import Button from '@material-hu/components/design-system/Buttons/Button';
import CardContainer from '@material-hu/components/design-system/CardContainer';
import FormCheckbox from '@material-hu/components/design-system/Checkbox/Checkbox/form';
import FormAutocomplete from '@material-hu/components/design-system/Inputs/Autocomplete/form';
import FormInputClassic from '@material-hu/components/design-system/Inputs/Classic/form';
import FormInputDate from '@material-hu/components/design-system/Inputs/DatePicker/form';
import FormInputPhone from '@material-hu/components/design-system/Inputs/Phone/form';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';
import { useDialogLayerItem } from '@material-hu/components/layers/Dialogs';

import { useRequiredAuth } from 'src/contexts/JWTContext';
import useGeneralError from 'src/hooks/useGeneralError';
import { restoreRecognitionPhoto } from 'src/pages/dashboard/Users/queries';
import { useDeactivationReasons } from 'src/pages/dashboard/Users/UserEdit/components/GeneralTab/hooks/useDeactivationReasons';
import * as usersService from 'src/services/usersService';
import { type User, UserStatus } from 'src/types/user';
import { useLokaliseTranslation } from 'src/utils/i18n';
import {
  validateDateRule,
  validateEmailRule,
  validatePhoneNumberRule,
  validateRequiredStringRule,
  validateUrlRule,
} from 'src/utils/validation';

import HugoDepartmentsAutocomplete from 'src/components/UsersAutocomplete/HugoDepartmentsAutocomplete';
import HugoPositionsAutocomplete from 'src/components/UsersAutocomplete/HugoPositionsAutocomplete';
import HugoPotentialBossAutocomplete from 'src/components/UsersAutocomplete/HugoPotentialBossAutocomplete';
import PublicSearchHugoUsersAutocomplete from 'src/components/UsersAutocomplete/PublicSearchHugoUsersAutocomplete';

import {
  ProfileFieldTypes,
  type ProfileSectionField,
} from '../User/components/InformationTab/types';

const MAX_STRING_LENGTH = 300;

const completeOption = (option: string) => ({
  label: option,
  value: option,
});

const StringField = ({ field }: { field: ProfileSectionField }) => {
  const { t } = useLokaliseTranslation('profile');
  const { setValue } = useFormContext();
  const options = (field.suggestions ?? []).map(completeOption);

  return (
    <FormAutocomplete
      name={field.id}
      options={options}
      rules={
        field.required
          ? { required: { value: true, message: t('general:required_field') } }
          : undefined
      }
      autocompleteProps={{
        label: field.name,
        isServerFiltered: false,
        placeholder: t('general:write_here'),
        onCreate: (inputValue: string) => {
          setValue(field.id, completeOption(inputValue), { shouldDirty: true });
        },
        maxLength: MAX_STRING_LENGTH,
      }}
    />
  );
};

const ListAutocomplete = ({
  field,
  type = 'string',
}: {
  field: ProfileSectionField;
  type?: 'string' | 'number';
}) => {
  const { setValue, getValues, watch } = useFormContext();
  const { t } = useLokaliseTranslation('profile');

  const watchedValues: { label: string; value: string }[] =
    watch(field.id) ?? [];
  const hasValues = !!watchedValues.length;

  const [extraOptions, setExtraOptions] = useState<
    { label: string; value: string }[]
  >([]);

  const options = uniqBy(
    [
      ...(field.suggestions || []).map(completeOption),
      ...extraOptions,
      ...watchedValues,
    ],
    'value',
  );

  return (
    <FormAutocomplete
      name={field.id}
      options={options}
      autocompleteProps={{
        type,
        label: field.name,
        multiple: true,
        isServerFiltered: false,
        placeholder: hasValues ? undefined : t('general:write_here'),
        onCreate: (inputValue: string) => {
          const newOption = completeOption(
            inputValue.trim().slice(0, MAX_STRING_LENGTH),
          );
          setExtraOptions(prev => [...prev, newOption]);
          setValue(field.id, [...getValues(field.id), newOption], {
            shouldDirty: true,
          });
        },
        maxLength: MAX_STRING_LENGTH,
      }}
    />
  );
};

const ProfileUserHugoAutocomplete = ({
  field,
  multiple = true,
}: {
  field: ProfileSectionField;
  multiple?: boolean;
}) => {
  const { t } = useLokaliseTranslation('profile');
  const status = field.metadata?.activeOnly ? UserStatus.ACTIVE : undefined;

  return (
    <PublicSearchHugoUsersAutocomplete
      name={field.id}
      autocompleteProps={{
        label: field.name,
        multiple,
        placeholder: t('general:write_here'),
      }}
      extraSearchParams={{
        status,
      }}
    />
  );
};

const OptionsAutocomplete = ({
  field,
  multiple = false,
}: {
  field: ProfileSectionField;
  multiple?: boolean;
}) => {
  const { t } = useLokaliseTranslation('profile');
  const value = useWatch({ name: field.id });
  const hasValues = multiple ? !!value?.length : !!value;
  return (
    <FormAutocomplete
      name={field.id}
      options={field.options?.map(completeOption) ?? []}
      autocompleteProps={{
        label: field.name,
        multiple,
        placeholder: hasValues ? undefined : t('general:write_here'),
      }}
    />
  );
};

const PhoneNumberField = ({ field }: { field: ProfileSectionField }) => {
  const { t } = useLokaliseTranslation('profile');
  const { instance } = useRequiredAuth();
  const allowWhatsApp = instance.allowSocialNetworks.allowWhatsapp;

  return (
    <Stack sx={{ gap: 2 }}>
      <FormInputPhone
        name={field.id}
        inputProps={{
          label: t(field.name),
          showErrors: true,
          success: false,
        }}
        rules={{ validate: validatePhoneNumberRule }}
      />
      {allowWhatsApp && (
        <FormCheckbox
          name="allowWhatsApp"
          checkBoxProps={{ label: t('allow_whatsapp') }}
        />
      )}
    </Stack>
  );
};

const FacialRecognitionField = ({
  field,
  user,
}: {
  field: ProfileSectionField;
  user: User;
}) => {
  const { t } = useLokaliseTranslation('users');
  const { enqueueSnackbar } = useHuSnackbar();
  const showGeneralError = useGeneralError();
  const [hasPhoto, setHasPhoto] = useState(Boolean(field.value));

  const { mutate, isLoading } = useMutation(
    () => usersService.deleteUserFacialRecognitionConfig(user.id),
    {
      onSuccess: () => {
        setHasPhoto(false);
        enqueueSnackbar({ title: t('RESTORE_SUCCESS'), variant: 'success' });
        restoreRecognitionPhoto(user.id);
        closeDialog();
      },
      onError: (err: unknown) => {
        showGeneralError(err);
        closeDialog();
      },
    },
  );

  const { openDialog, closeDialog } = useDialogLayerItem(
    'restore-facial-recognition',
    {
      title: t('CONFIRM_RESTORE'),
      textBody: t('RESTORE_WARNING'),
      primaryButtonProps: {
        children: t('RESTORE'),
        disabled: isLoading,
        loading: isLoading,
        onClick: () => mutate(),
      },
      secondaryButtonProps: {
        children: t('CANCEL'),
        onClick: () => closeDialog(),
      },
      dialogProps: { maxWidth: 'sm' },
    },
  );

  const statusText = hasPhoto
    ? t('facial_recognition_photo_set')
    : t('facial_recognition_photo_not_set');

  return (
    <Stack sx={{ gap: 1 }}>
      <Typography
        variant="globalS"
        fontWeight="fontWeightSemiBold"
      >
        {t(field.name)}
      </Typography>
      <CardContainer
        fullWidth
        sx={{
          '.MuiCardContent-root': {
            display: 'flex',
            flexDirection: 'column',
            gap: 1,
          },
        }}
      >
        <Stack
          sx={{
            flexDirection: 'row',
            gap: 1,
            alignItems: 'center',
            justifyContent: 'space-between',
          }}
        >
          <Stack sx={{ flexDirection: 'row', gap: 1, alignItems: 'center' }}>
            <Avatar
              Icon={hasPhoto ? IconCheck : IconX}
              color={hasPhoto ? 'success' : 'default'}
            />
            <Typography
              variant="globalS"
              fontWeight="fontWeightSemiBold"
            >
              {statusText}
            </Typography>
          </Stack>
          {hasPhoto && (
            <Button
              variant="outlined"
              size="small"
              startIcon={<IconRefresh size={16} />}
              onClick={() => openDialog()}
            >
              {t('RESTORE')}
            </Button>
          )}
        </Stack>
        <Typography
          variant="globalXS"
          sx={{ color: ({ palette }) => palette.new.text.neutral.lighter }}
        >
          {field.description}
        </Typography>
      </CardContainer>
    </Stack>
  );
};

const DeactivationReasonField = ({ field }: { field: ProfileSectionField }) => {
  const { t } = useLokaliseTranslation('users');
  const { data: options = [] } = useDeactivationReasons(response =>
    response.data.map(r => ({
      value: r,
      label: t(`deactivation_reasons.${r}`),
    })),
  );

  return (
    <FormAutocomplete
      name={field.id}
      options={options}
      autocompleteProps={{
        label: field.name,
        placeholder: t('general:select'),
        isServerFiltered: false,
      }}
    />
  );
};

const validateOptionalDateRule = (value: unknown) => {
  if (value == null) return true;
  return validateDateRule(value);
};

const DateField = ({ field }: { field: ProfileSectionField }) => {
  const { t } = useLokaliseTranslation('users');
  return (
    <FormInputDate
      name={field.id}
      inputProps={{
        label: t(field.name),
        enableClear: true,
        views: ['year', 'month', 'day'],
        disableFuture: !!field.metadata?.disableFuture,
      }}
      rules={{
        validate: field.required ? validateDateRule : validateOptionalDateRule,
      }}
    />
  );
};

const useFieldComponents = () => {
  const { t } = useLokaliseTranslation(['profile', 'validations']);

  const profileInputMap: Partial<
    Record<
      ProfileFieldTypes,
      (field: ProfileSectionField, user: User) => ReactNode
    >
  > = {
    [ProfileFieldTypes.DEPARTMENT]: (field: ProfileSectionField) => (
      <HugoDepartmentsAutocomplete
        name={field.id}
        autocompleteProps={{
          label: t(field.name),
        }}
      />
    ),
    [ProfileFieldTypes.JOB_POSITION]: (field: ProfileSectionField) => (
      <HugoPositionsAutocomplete
        name={field.id}
        autocompleteProps={{
          label: t(field.name),
        }}
      />
    ),
    [ProfileFieldTypes.DEACTIVATION_REASON]: (field: ProfileSectionField) => (
      <DeactivationReasonField field={field} />
    ),
    [ProfileFieldTypes.STRING]: (field: ProfileSectionField) =>
      field.suggestions?.length ? (
        <StringField field={field} />
      ) : (
        <FormInputClassic
          name={field.id}
          inputProps={{
            label: t(field.name),
            hasCounter: !!field.metadata?.showCounter,
            maxLength: MAX_STRING_LENGTH,
            placeholder: t('general:write_here'),
            multiline: !!field.metadata?.multiline,
          }}
          rules={
            field.required
              ? { validate: validateRequiredStringRule }
              : undefined
          }
        />
      ),
    [ProfileFieldTypes.EMAIL]: (field: ProfileSectionField) => (
      <FormInputClassic
        name={field.id}
        inputProps={{
          label: t(field.name),
          hasCounter: false,
          placeholder: t('general:write_here'),
        }}
        rules={{ validate: validateEmailRule }}
      />
    ),
    [ProfileFieldTypes.BOSS]: (field: ProfileSectionField, user: User) => (
      <HugoPotentialBossAutocomplete
        name={field.id}
        potentialBossesId={user?.id}
        autocompleteProps={{
          placeholder: t('general:select'),
          label: t(field.name),
        }}
      />
    ),
    [ProfileFieldTypes.USER]: (field: ProfileSectionField) => (
      <ProfileUserHugoAutocomplete
        field={field}
        multiple={false}
      />
    ),
    [ProfileFieldTypes.USER_LIST]: (field: ProfileSectionField) => (
      <ProfileUserHugoAutocomplete
        field={field}
        multiple
      />
    ),
    [ProfileFieldTypes.FULL_DATE]: (field: ProfileSectionField) => (
      <DateField field={field} />
    ),
    [ProfileFieldTypes.DATE]: (field: ProfileSectionField) => (
      <DateField field={field} />
    ),
    [ProfileFieldTypes.PHONE_NUMBER]: (field: ProfileSectionField) => (
      <PhoneNumberField field={field} />
    ),
    [ProfileFieldTypes.URL]: (field: ProfileSectionField) => (
      <FormInputClassic
        name={field.id}
        inputProps={{
          sx: {
            '.MuiFormHelperText-root': {
              whiteSpace: 'pre-line',
            },
          },
          label: t(field.name),
          hasCounter: false,
          placeholder: t('general:write_here'),
          helperText: field.helperText,
          errorText: `${t('url_error_message')}\n${field.helperText}`,
        }}
        rules={{ validate: validateUrlRule }}
      />
    ),
    [ProfileFieldTypes.STRING_LIST]: (field: ProfileSectionField) => (
      <ListAutocomplete
        field={field}
        type="string"
      />
    ),
    [ProfileFieldTypes.NUMBER_LIST]: (field: ProfileSectionField) => (
      <ListAutocomplete
        field={field}
        type="number"
      />
    ),
    [ProfileFieldTypes.NUMBER]: (field: ProfileSectionField) => (
      <FormInputClassic
        name={field.id}
        inputProps={{
          label: t(field.name),
          hasCounter: false,
          type: 'number',
          placeholder: t('general:write_here'),
        }}
      />
    ),
    [ProfileFieldTypes.OPTION]: (field: ProfileSectionField) => (
      <OptionsAutocomplete field={field} />
    ),
    [ProfileFieldTypes.MULTIPLE_OPTION]: (field: ProfileSectionField) => (
      <OptionsAutocomplete
        field={field}
        multiple
      />
    ),
    [ProfileFieldTypes.FACIAL_RECOGNITION]: (
      field: ProfileSectionField,
      user: User,
    ) => (
      <FacialRecognitionField
        field={field}
        user={user}
      />
    ),
  };

  const renderField = (field: ProfileSectionField, user: User) => {
    const render = profileInputMap[field.type];
    return render ? render(field, user) : null;
  };

  return {
    renderField,
  };
};

export default useFieldComponents;
