import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { stringAvatar } from "@/components/Event/utils/eventUtils";
import { useUserSearch } from "./useUserSearch";
import { SnackbarAlert } from "@/components/Loading/SnackBarAlert";
import CloseIcon from "@mui/icons-material/Close";
import {
  Autocomplete,
  Avatar,
  Chip,
  CircularProgress,
  ListItem,
  ListItemAvatar,
  ListItemText,
  PaperProps,
  PopperProps,
  TextField,
  useTheme,
  type AutocompleteRenderInputParams,
} from "@linagora/twake-mui";
import PeopleOutlineOutlinedIcon from "@mui/icons-material/PeopleOutlineOutlined";
import {
  HTMLAttributes,
  useCallback,
  type ReactNode,
  type SyntheticEvent,
} from "react";
import { useI18n } from "twake-i18n";
import { ResourceIcon } from "./ResourceIcon";

export interface User {
  email: string;
  displayName: string;
  avatarUrl?: string;
  openpaasId?: string;
  color?: Record<string, string>;
  objectType?: string;
}

export interface ExtendedAutocompleteRenderInputParams extends AutocompleteRenderInputParams {
  error?: boolean;
  helperText?: string | null;
  placeholder?: string;
  label?: string;
  onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
}

export function PeopleSearch({
  selectedUsers,
  onChange,
  objectTypes,
  disabled,
  freeSolo,
  onToggleEventPreview,
  placeholder,
  inputSlot,
  customRenderInput,
  customSlotProps,
  getChipIcon,
}: {
  selectedUsers: User[];
  onChange: (event: SyntheticEvent, users: User[]) => void;
  objectTypes: string[];
  disabled?: boolean;
  freeSolo?: boolean;
  onToggleEventPreview?: () => void;
  placeholder?: string;
  inputSlot?: (
    params: ExtendedAutocompleteRenderInputParams
  ) => React.ReactNode;
  customRenderInput?: (
    params: AutocompleteRenderInputParams,
    query: string,
    setQuery: (value: string) => void
  ) => ReactNode;
  customSlotProps?: {
    popper?: Partial<PopperProps>;
    paper?: Partial<PaperProps>;
    listbox?: Partial<HTMLAttributes<HTMLUListElement>>;
  };
  getChipIcon?: (user: User) => ReactNode;
}) {
  const { t } = useI18n();
  const searchPlaceholder = placeholder ?? t("peopleSearch.placeholder");
  const errorMessage = t("peopleSearch.searchError");

  const {
    query,
    setQuery,
    loading,
    options,
    hasSearched,
    isOpen,
    setIsOpen,
    inputError,
    setInputError,
    snackbarOpen,
    setSnackbarOpen,
    snackbarMessage,
    setSnackbarMessage,
  } = useUserSearch<User>({ objectTypes, errorMessage });

  const isValidEmail = (email: string) =>
    /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  const theme = useTheme();

  const handleBlurCommit = useCallback(
    (event: React.SyntheticEvent) => {
      const trimmed = query.trim();
      if (!trimmed) return;
      if (!isValidEmail(trimmed)) {
        setInputError(
          t("peopleSearch.invalidEmail").replace("%{email}", trimmed)
        );
        return;
      }
      if (selectedUsers.find((u) => u.email === trimmed)) {
        setQuery("");
        return;
      }
      setInputError(null);
      const newUser: User = { email: trimmed, displayName: trimmed };
      onChange(event, [...selectedUsers, newUser]);
      setQuery("");
    },
    [query, selectedUsers, onChange, t, setInputError, setQuery]
  );

  const defaultRenderInput = useCallback(
    (params: AutocompleteRenderInputParams) => {
      const inputProps = {
        ...params.InputProps,
        startAdornment: params.InputProps.startAdornment ? (
          params.InputProps.startAdornment
        ) : (
          <PeopleOutlineOutlinedIcon
            fontSize="small"
            sx={{ mr: 1, color: "action.active" }}
          />
        ),
        endAdornment: (
          <>
            {loading ? <CircularProgress color="inherit" size={20} /> : null}
            {params.InputProps.endAdornment}
          </>
        ),
      };

      const enhancedParams = {
        ...params,
        InputProps: inputProps,
        inputProps: {
          ...params.inputProps,
          autoComplete: "off",
        },
      };

      const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key === "Enter" && onToggleEventPreview) {
          e.preventDefault();
          onToggleEventPreview();
        }
      };

      const defaultTextFieldProps = {
        error: !!inputError,
        helperText: inputError,
        placeholder: searchPlaceholder,
        label: "",
        onKeyDown: handleEnterKey,
        slotProps: {
          input: {
            ...inputProps,
          },
        },
      };

      if (inputSlot) {
        return (
          <>
            <label htmlFor={params.id} className="visually-hidden">
              {t("peopleSearch.label")}
            </label>
            {inputSlot({
              ...enhancedParams,
              error: !!inputError,
              helperText: inputError,
              placeholder: searchPlaceholder,
              label: "",
              onKeyDown: handleEnterKey,
            })}
          </>
        );
      }

      return (
        <>
          <label htmlFor={params.id} className="visually-hidden">
            {t("peopleSearch.label")}
          </label>
          <TextField
            {...enhancedParams}
            {...defaultTextFieldProps}
            InputProps={inputProps}
            size="medium"
          />
        </>
      );
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [inputError, t, onToggleEventPreview, loading, searchPlaceholder]
  );

  return (
    <>
      <Autocomplete
        popupIcon={null}
        freeSolo={freeSolo}
        multiple
        options={options}
        autoComplete={false}
        clearOnBlur={false}
        onBlur={freeSolo ? handleBlurCommit : undefined}
        open={
          customRenderInput
            ? isOpen && !!query && (loading || options.length > 0)
            : isOpen && !!query && (loading || hasSearched)
        }
        onOpen={() => setIsOpen(true)}
        onClose={() => setIsOpen(false)}
        disabled={disabled}
        loading={loading}
        filterOptions={(x) => x}
        fullWidth
        noOptionsText={t("peopleSearch.noResults")}
        loadingText={t("peopleSearch.loading")}
        getOptionLabel={(option) => {
          if (typeof option === "object") {
            return option.displayName || option.email;
          } else {
            return option;
          }
        }}
        sx={{
          "& .MuiAutocomplete-inputRoot": {
            py: 0,
          },
        }}
        filterSelectedOptions
        value={selectedUsers}
        inputValue={query}
        onInputChange={(_event, value) => setQuery(value)}
        onChange={(event, value) => {
          const last = value[value.length - 1];
          if (typeof last === "string" && !isValidEmail(last.trim())) {
            const invalidEmailMessage = t("peopleSearch.invalidEmail").replace(
              "%{email}",
              last
            );
            setInputError(invalidEmailMessage);
            return;
          }
          setInputError(null);
          const mapped = value
            .map((v: string | User) =>
              typeof v === "string"
                ? { email: v.trim(), displayName: v.trim() }
                : v
            )
            .filter(
              (user, index, self) =>
                self.findIndex((u) => u.email === user.email) === index
            );
          onChange(event, mapped);
        }}
        slotProps={{
          ...customSlotProps,
          popper: {
            placement: "bottom-start",
            sx: { minWidth: "300px", ...customSlotProps?.popper?.sx },
            ...customSlotProps?.popper,
          },
        }}
        forcePopupIcon={false}
        disableClearable
        renderInput={(params) =>
          customRenderInput
            ? customRenderInput(params, query, setQuery)
            : defaultRenderInput(params)
        }
        renderOption={(props, option) => {
          if (selectedUsers.find((u) => u.email === option.email)) return null;
          const { key, ...otherProps } = props;
          const isResource = option.objectType === "resource";
          return (
            <ListItem key={key + option?.email} {...otherProps} disableGutters>
              <ListItemAvatar>
                {isResource ? (
                  <ResourceIcon avatarUrl={option.avatarUrl} />
                ) : (
                  <Avatar
                    {...stringAvatar(option.displayName || option.email)}
                  />
                )}
              </ListItemAvatar>
              <ListItemText
                primary={option.displayName}
                secondary={isResource ? "" : option.email}
                slotProps={{
                  primary: { variant: "body2" },
                  secondary: { variant: "caption" },
                }}
              />
            </ListItem>
          );
        }}
        renderValue={(value, getTagProps) =>
          value.map((option, index) => {
            const isString = typeof option === "string";
            const label = isString
              ? option
              : option.displayName || option.email;
            const chipColor = isString
              ? theme.palette.grey[200]
              : (option.color?.light ?? theme.palette.grey[200]);
            const textColor = getAccessiblePair(chipColor, theme);

            return (
              <Chip
                {...getTagProps({ index })}
                key={label}
                icon={
                  !isString && getChipIcon ? getChipIcon(option) : undefined
                }
                deleteIcon={<CloseIcon />}
                style={{
                  backgroundColor: chipColor,
                  color: textColor,
                }}
                label={label}
              />
            );
          })
        }
      />
      <SnackbarAlert
        open={snackbarOpen}
        setOpen={(open: boolean) => {
          setSnackbarOpen(open);
          if (!open) {
            setSnackbarMessage("");
          }
        }}
        message={snackbarMessage}
        severity="error"
      />
    </>
  );
}
