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

import uniqBy from 'lodash-es/uniqBy';
import Stack from '@material-hu/mui/Stack';

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 { useRequiredAuth } from 'src/contexts/JWTContext';
import useRules from 'src/hooks/useRules';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { getQueryParamsAutocompleteUser } from 'src/utils/userUtils';

import {
  type BaseUser,
  FormUserAutocomplete,
} from 'src/components/dashboard/filterDrawer/FormUserAutocomplete';

import {
  type ProfileInputField,
  ProfileInputType,
  type WithOptions,
  type WithSuggestions,
} from '../types';

const MAX_STRING_LENGTH = 300;

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

const StringField = ({
  field,
}: {
  field: {
    name: string;
    label: string;
    suggestions: string[];
    required?: boolean;
  };
}) => {
  const { t } = useLokaliseTranslation('profile');
  const { setValue } = useFormContext();
  const options = field.suggestions.map(completeOption);

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

const ListAutocomplete = ({
  field,
  type = 'string',
}: {
  field: {
    name: string;
    label: string;
    suggestions: string[];
  };
  type?: 'string' | 'number';
}) => {
  const { setValue, watch } = useFormContext();
  const { t } = useLokaliseTranslation('profile');

  const watchedValues: { label: string; value: string }[] =
    watch(field.name) ?? [];
  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.name}
      options={options}
      autocompleteProps={{
        type,
        label: field.label,
        multiple: true,
        isServerFiltered: false,
        placeholder: hasValues ? undefined : t('general:write_here'),
        onCreate: (inputValue: string) => {
          const newOption = completeOption(inputValue.trim());
          setExtraOptions(prev => [...prev, newOption]);
          setValue(field.name, [...watchedValues, newOption], {
            shouldDirty: true,
          });
        },
        maxLength: MAX_STRING_LENGTH,
      }}
    />
  );
};

type UserAutocompleteField = { name: string; id: string; label: string };

const useProfileUsersQuery = (search: string) => {
  const { queryKey, queryFn } = getQueryParamsAutocompleteUser(
    search,
    false,
    false,
  );
  return useQuery({
    queryKey,
    queryFn,
    staleTime: 60000,
    select: (res: { data?: { items?: BaseUser[] } }) => res?.data?.items ?? [],
  });
};

const ProfileUserAutocomplete = ({
  field,
  multiple = false,
}: {
  field: UserAutocompleteField;
  multiple?: boolean;
}) => {
  const { t } = useLokaliseTranslation('profile');
  const hasValues = !!useWatch({ name: field.name })?.length;
  return (
    <FormUserAutocomplete
      name={field.name}
      label={field.label}
      multiple={multiple}
      usersQuery={useProfileUsersQuery}
      placeholder={hasValues ? undefined : t('general:write_here')}
    />
  );
};

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

const PhoneNumberField = ({
  field,
}: {
  field: { name: string; label: string };
}) => {
  const { t } = useLokaliseTranslation('profile');
  const { instance } = useRequiredAuth();
  const allowWhatsApp = instance.allowSocialNetworks.allowWhatsApp;

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

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

  const emailRules = useRules({
    email: true,
  });

  const urlRules = useRules({
    url: true,
  });

  const requiredRules = useRules({
    requiredWithMessage: true,
  });

  const dateRules = useRules({
    checkValidFormatDate: t('validations:invalid_date'),
  });

  const profileInputMap = {
    [ProfileInputType.STRING]: (field: WithSuggestions) =>
      field.suggestions?.length ? (
        <StringField field={field} />
      ) : (
        <FormInputClassic
          name={field.name}
          inputProps={{
            label: field.label,
            hasCounter: !!field.metadata?.showCounter,
            maxLength: MAX_STRING_LENGTH,
            placeholder: t('general:write_here'),
          }}
          rules={field.required ? requiredRules : undefined}
        />
      ),
    [ProfileInputType.STRING_LIST]: (field: WithSuggestions) => (
      <ListAutocomplete field={field} />
    ),
    [ProfileInputType.NUMBER_LIST]: (field: WithSuggestions) => (
      <ListAutocomplete
        field={field}
        type="number"
      />
    ),
    [ProfileInputType.URL]: (field: ProfileInputField) => (
      <FormInputClassic
        name={field.name}
        inputProps={{
          sx: {
            '.MuiFormHelperText-root': {
              whiteSpace: 'pre-line',
            },
          },
          label: field.label,
          hasCounter: false,
          placeholder: t('general:write_here'),
          helperText: field.helperText,
          errorText: `${t('url_error_message')}\n${field.helperText}`,
        }}
        rules={urlRules}
      />
    ),
    [ProfileInputType.NUMBER]: (field: ProfileInputField) => (
      <FormInputClassic
        name={field.name}
        inputProps={{
          label: field.label,
          hasCounter: false,
          type: 'number',
          placeholder: t('general:write_here'),
        }}
      />
    ),
    [ProfileInputType.EMAIL]: (field: ProfileInputField) => (
      <FormInputClassic
        name={field.name}
        inputProps={{
          label: field.label,
          hasCounter: false,
          placeholder: t('general:write_here'),
        }}
        rules={emailRules}
      />
    ),
    [ProfileInputType.DATE]: (field: ProfileInputField) => (
      <FormInputDate
        name={field.name}
        inputProps={{
          label: field.label,
          enableClear: true,
          views: ['year', 'month', 'day'],
          disableFuture: field.disableFuture,
        }}
        rules={dateRules}
      />
    ),
    [ProfileInputType.USER]: (field: ProfileInputField) => (
      <ProfileUserAutocomplete
        field={field}
        multiple={false}
      />
    ),
    [ProfileInputType.USER_LIST]: (field: ProfileInputField) => (
      <ProfileUserAutocomplete
        field={field}
        multiple
      />
    ),
    [ProfileInputType.OPTION]: (field: WithOptions) => (
      <OptionsAutocomplete field={field} />
    ),
    [ProfileInputType.MULTIPLE_OPTION]: (field: WithOptions) => (
      <OptionsAutocomplete
        field={field}
        multiple
      />
    ),
    [ProfileInputType.PHONE_NUMBER]: (field: ProfileInputField) => (
      <PhoneNumberField field={field} />
    ),
  };

  const renderField = (field: {
    name: string;
    label: string;
    type: ProfileInputType;
  }) => {
    // check just in case some users have adhoc fields
    if (field.type in profileInputMap) {
      return (profileInputMap[field.type] as (f: typeof field) => ReactNode)(
        field,
      );
    }
    return null;
  };

  return {
    renderField,
  };
};

export default useFieldComponents;
