import { Combobox } from "@headlessui/react";
import { useFetcher } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
import { useDebounce } from "react-use";
import type { UserSearchLoaderData } from "~/features/user-search/routes/u";
import { Avatar } from "./Avatar";
import { useTranslation } from "react-i18next";

type UserSearchUserItem = NonNullable<UserSearchLoaderData>["users"][number];

export function UserSearch({
  inputName,
  onChange,
  initialUserId,
  id,
  className,
  userIdsToOmit,
  required,
}: {
  inputName: string;
  onChange?: (user: UserSearchUserItem) => void;
  initialUserId?: number;
  id?: string;
  className?: string;
  userIdsToOmit?: Set<number>;
  required?: boolean;
}) {
  const { t } = useTranslation();
  const [selectedUser, setSelectedUser] =
    React.useState<UserSearchUserItem | null>(null);
  const queryFetcher = useFetcher<UserSearchLoaderData>();
  const initialUserFetcher = useFetcher<UserSearchLoaderData>();
  const [query, setQuery] = React.useState("");
  useDebounce(
    () => {
      if (!query) return;

      queryFetcher.load(`/u?q=${query}&limit=6`);
    },
    1000,
    [query],
  );

  // load initial user
  React.useEffect(() => {
    if (
      !initialUserId ||
      initialUserFetcher.state !== "idle" ||
      initialUserFetcher.data
    ) {
      return;
    }

    initialUserFetcher.load(`/u?q=${initialUserId}`);
  }, [initialUserId, initialUserFetcher]);
  React.useEffect(() => {
    if (!initialUserFetcher.data) return;

    setSelectedUser(initialUserFetcher.data.users[0]);
  }, [initialUserFetcher.data]);

  const allUsers = queryFetcher.data?.users ?? [];

  const users = allUsers.filter((u) => !userIdsToOmit?.has(u.id));
  const noMatches = queryFetcher.data && users.length === 0;

  const initialSelectionIsLoading = Boolean(
    initialUserId && !initialUserFetcher.data,
  );

  return (
    <div className="combobox-wrapper">
      {selectedUser && inputName ? (
        <input type="hidden" name={inputName} value={selectedUser.id} />
      ) : null}
      <Combobox
        value={selectedUser}
        onChange={(newUser) => {
          setSelectedUser(newUser);
          onChange?.(newUser!);
        }}
        disabled={initialSelectionIsLoading}
      >
        <Combobox.Input
          placeholder={
            initialSelectionIsLoading
              ? t("actions.loading")
              : "Search via name or ID..."
          }
          onChange={(event) => setQuery(event.target.value)}
          displayValue={(user: UserSearchUserItem) => user?.discordName ?? ""}
          className={clsx("combobox-input", className)}
          data-1p-ignore
          data-testid={`${inputName}-combobox-input`}
          id={id}
          required={required}
        />
        <Combobox.Options
          className={clsx("combobox-options", {
            empty: noMatches,
            hidden: !queryFetcher.data,
          })}
        >
          {noMatches ? (
            <div className="combobox-no-matches">
              {t("forms.errors.noSearchMatches")}{" "}
              <span className="combobox-emoji">🤔</span>
            </div>
          ) : null}
          {users.map((user, i) => (
            <Combobox.Option key={user.id} value={user} as={React.Fragment}>
              {({ active }) => (
                <li
                  className={clsx("combobox-item", { active })}
                  data-testid={`combobox-option-${i}`}
                >
                  <Avatar user={user} size="xs" />
                  <div>
                    <div className="stack xs horizontal items-center">
                      <span className="combobox-username">
                        {user.discordName}
                      </span>{" "}
                      {user.plusTier ? (
                        <span className="text-xxs">+{user.plusTier}</span>
                      ) : null}
                    </div>
                    {user.discordUniqueName ? (
                      <div className="text-xs">{user.discordUniqueName}</div>
                    ) : null}
                  </div>
                </li>
              )}
            </Combobox.Option>
          ))}
        </Combobox.Options>
      </Combobox>
    </div>
  );
}
