"use client";

import { useEffect, useRef, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Ban, Check, Store } from "lucide-react";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { getStoreClients } from "@/services/store-clients";
import { queryKeys } from "@/lib/query-keys";
import { cn } from "@/lib/cn";
import { friendlyErrorMessage } from "@/lib/errors";
import { useT } from "@/i18n";
import type { StoreClient } from "@/types/store";

/*
 * Wider than the list needs, because `is_live` is strained out here rather than by the API: a
 * page of eight that happened to be eight paused shops would read as "no stores" while live ones
 * waited on page two. The list scrolls, so asking for more costs a little JSON and nothing else.
 */
const RESULTS_PER_PAGE = 20;

/**
 * Which shop an order is being opened for, or none at all.
 *
 * **Closed until asked for.** Most orders the back office raises belong to no store, so a list of
 * shops sitting open under every new order is a wall of names nobody needed — and it loads them
 * on a form where the usual answer is "none". The search box opens it; what is currently chosen
 * stays on screen either way, so the form always says what it will submit.
 *
 * Nothing is fetched until it opens, which is the other half of the same point: an order typed
 * without a store costs no request at all.
 *
 * **Only live stores are offered.** The API refuses an order naming a suspended or paused shop,
 * so listing one here would be an invitation to a 422. The filter asks for approved stores and
 * `is_live` strains out the ones that are approved but paused, which the list endpoint cannot
 * express on its own.
 *
 * "No store" is a row rather than an absent choice, because it is a real answer — an order the
 * company carries for nobody but itself — and it has to be as reachable as the others, including
 * for somebody undoing a pick they made by mistake.
 */
export function StorePicker({
  value,
  onChange,
}: {
  value: string | null;
  onChange: (value: string | null) => void;
}) {
  const t = useT();
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");
  const [debounced, setDebounced] = useState("");

  /*
   * The shop behind `value`, kept from the moment it was picked.
   *
   * The parent holds a uuid, and the closed picker has to name the choice without the list that
   * produced it — so the row travels with the selection rather than being looked up again.
   * Guarded by the uuid matching, so a parent that clears the field does not leave a stale name
   * on screen.
   */
  const [chosen, setChosen] = useState<StoreClient | null>(null);
  const selected = chosen && chosen.uuid === value ? chosen : null;

  const container = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handle = window.setTimeout(() => setDebounced(query), 350);
    return () => window.clearTimeout(handle);
  }, [query]);

  /*
   * Closing. `mousedown` rather than `click` so a press that starts outside closes before the
   * list can move under the cursor, and Escape because a keyboard user must not be trapped in
   * an open list mid-form.
   */
  useEffect(() => {
    if (!open) {
      return;
    }

    function onPointerDown(event: MouseEvent) {
      if (!container.current?.contains(event.target as Node)) {
        setOpen(false);
      }
    }

    function onKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") {
        setOpen(false);
      }
    }

    document.addEventListener("mousedown", onPointerDown);
    document.addEventListener("keydown", onKeyDown);

    return () => {
      document.removeEventListener("mousedown", onPointerDown);
      document.removeEventListener("keydown", onKeyDown);
    };
  }, [open]);

  const filters = {
    page: 1,
    rows: RESULTS_PER_PAGE,
    status: "approved" as const,
    search: debounced || undefined,
  };
  const result = useQuery({
    queryKey: queryKeys.storeClients.list(filters),
    queryFn: () => getStoreClients(filters),
    // Nothing is asked of the API until somebody opens the list.
    enabled: open,
  });

  const stores = (result.data?.Model ?? []).filter((store) => store.is_live);

  function choose(store: StoreClient | null) {
    setChosen(store);
    onChange(store?.uuid ?? null);
    setQuery("");
    setOpen(false);
  }

  return (
    <div ref={container} className="relative grid gap-2">
      <Input
        value={query}
        onChange={(event) => {
          setQuery(event.target.value);
          setOpen(true);
        }}
        onFocus={() => setOpen(true)}
        placeholder={t("orders.storeSearch")}
        aria-label={t("orders.storeSearch")}
        aria-expanded={open}
        role="combobox"
        {...(open ? { "aria-controls": "store-picker-list" } : {})}
      />

      {/* What the form will submit, whether or not the list is open. */}
      {!open ? (
        <button
          type="button"
          onClick={() => setOpen(true)}
          className="flex items-center gap-2 rounded-md border border-dashed px-3 py-2 text-start text-sm transition-colors hover:bg-primary-soft"
        >
          <span
            className={cn(
              "flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
              selected ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground",
            )}
          >
            {selected ? (
              <Store className="h-3.5 w-3.5" aria-hidden />
            ) : (
              <Ban className="h-3.5 w-3.5" aria-hidden />
            )}
          </span>
          <span className="min-w-0 flex-1 truncate">
            {selected ? selected.name : t("orders.noStore")}
          </span>
          <span className="shrink-0 text-xs text-muted-foreground">{t("orders.storeChange")}</span>
        </button>
      ) : (
        <div id="store-picker-list" role="listbox" className="max-h-56 overflow-y-auto rounded-md border">
          <button
            type="button"
            onClick={() => choose(null)}
            role="option"
            aria-selected={value === null}
            className={cn(
              "flex w-full items-center gap-3 border-b px-3 py-2 text-start transition-colors hover:bg-primary-soft",
              value === null && "bg-primary-soft",
            )}
          >
            <span
              className={cn(
                "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground",
                value === null && "bg-primary text-primary-foreground",
              )}
            >
              {value === null ? (
                <Check className="h-4 w-4" aria-hidden />
              ) : (
                <Ban className="h-4 w-4" aria-hidden />
              )}
            </span>
            <span className="min-w-0 flex-1">
              <span className="block truncate text-sm font-medium text-foreground">
                {t("orders.noStore")}
              </span>
              <span className="block text-xs text-muted-foreground">{t("orders.noStoreHint")}</span>
            </span>
          </button>

          {result.isPending ? (
            <div className="grid gap-1 p-2">
              <Skeleton className="h-9 w-full" />
              <Skeleton className="h-9 w-full" />
              <Skeleton className="h-9 w-full" />
            </div>
          ) : result.isError ? (
            <p className="p-3 text-xs text-destructive">{friendlyErrorMessage(result.error, t)}</p>
          ) : stores.length === 0 ? (
            <p className="p-3 text-sm text-muted-foreground">
              {debounced ? t("common.noResults") : t("orders.noLiveStores")}
            </p>
          ) : (
            <ul role="none">
              {stores.map((store) => {
                const isSelected = store.uuid === value;
                return (
                  <li key={store.uuid}>
                    <button
                      type="button"
                      onClick={() => choose(store)}
                      role="option"
                      aria-selected={isSelected}
                      className={cn(
                        "flex w-full items-center gap-3 px-3 py-2 text-start transition-colors hover:bg-primary-soft",
                        isSelected && "bg-primary-soft",
                      )}
                    >
                      <span
                        className={cn(
                          "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground",
                          isSelected && "bg-primary text-primary-foreground",
                        )}
                      >
                        {isSelected ? (
                          <Check className="h-4 w-4" aria-hidden />
                        ) : (
                          <Store className="h-4 w-4" aria-hidden />
                        )}
                      </span>
                      <span className="min-w-0 flex-1">
                        <span className="block truncate text-sm font-medium text-foreground">
                          {store.name}
                        </span>
                        <span className="block truncate text-xs text-muted-foreground" dir="ltr">
                          {store.slug}
                        </span>
                      </span>
                    </button>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
      )}
    </div>
  );
}
