"use client";

import { useEffect, useRef, useState } from "react";
import { useQuery, keepPreviousData } from "@tanstack/react-query";
import { ArrowLeft, MapPin, Plus, Search, Store } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { getWrappingPlaces } from "@/services/orders";
import { queryKeys } from "@/lib/query-keys";
import { cn } from "@/lib/cn";
import { friendlyErrorMessage } from "@/lib/errors";
import { useT } from "@/i18n";
import type { WrappingPlace } from "@/types/order";

/** Enough rows to scan without scrolling, with the rest a search away. */
const RESULTS_PER_PAGE = 8;

/**
 * Which wrapping shop this order goes through: one already on record, or a new one.
 *
 * A store registers its wrapping shop once at integration, and until now the back office could
 * not see any of them — so raising a wrapped order meant retyping an address and a pin somebody
 * had already entered, with a slip in the coordinates producing a collection leg routed to the
 * wrong street rather than an error.
 *
 * **Closed until asked for, then searched rather than listed.** The register grows with every
 * store that signs up, so a control that renders all of them works at a dozen and quietly stops
 * working at three hundred — and most of the time nobody is looking for a shop at all. The
 * search bar opens it; the server pages and filters; the panel shows a screenful and says how
 * many more there are. Nothing is fetched until it opens.
 *
 * **Picking one only fills the form.** The order copies the address and pin, so a chosen shop
 * and a hand-typed one produce the same order, and a store that later moves its wrapping shop
 * does not rewrite where past orders went.
 */
export function WrappingPlacePicker({
  place,
  addingNew,
  invalid,
  onChoose,
  onAddNew,
  onReset,
}: {
  /** The shop chosen from the register, or null. */
  place: WrappingPlace | null;
  /** Whether the manual fields are open beneath this. */
  addingNew: boolean;
  /** The form was submitted with no shop named either way. */
  invalid?: boolean;
  onChoose: (place: WrappingPlace) => void;
  onAddNew: () => void;
  onReset: () => void;
}) {
  const t = useT();
  const [query, setQuery] = useState("");
  const [debounced, setDebounced] = useState("");
  const [open, setOpen] = useState(false);
  const panel = 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 halfway down a form.
   */
  useEffect(() => {
    if (!open) {
      return;
    }

    function onPointerDown(event: MouseEvent) {
      if (!panel.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 choosing = place === null && !addingNew;

  const filters = { rows: RESULTS_PER_PAGE, page: 1, search: debounced || undefined };
  const result = useQuery({
    queryKey: queryKeys.orders.wrappingPlaces(filters),
    queryFn: () => getWrappingPlaces(filters),
    // Only once the register is open. A chosen shop, a new one being typed, or a form nobody
    // has touched here all need nothing from the server.
    enabled: choosing && open,
    // The rows stay put while a new search lands, so the list does not blink on every keystroke.
    placeholderData: keepPreviousData,
  });

  const places = result.data?.Model ?? [];
  const total = result.data?.Records ?? 0;
  const hidden = Math.max(0, total - places.length);

  // ── a shop is chosen ──────────────────────────────────────────────────────
  if (place !== null) {
    return (
      <div className="flex items-start gap-3 rounded-xl border border-primary/40 bg-primary/5 p-4">
        <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground">
          <Store className="h-[18px] w-[18px]" aria-hidden />
        </span>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm font-semibold text-foreground">
            {place.name ?? t("orders.wrappingUnnamed")}
          </p>
          <p className="mt-0.5 truncate text-xs text-muted-foreground">{place.address}</p>
          <p className="mt-1.5 inline-flex items-center gap-1 rounded-full bg-surface px-2 py-0.5 text-[0.7rem] font-medium text-muted-foreground">
            <MapPin className="h-3 w-3" aria-hidden />
            {t("orders.wrappingUsedBy", { count: String(place.stores) })}
          </p>
        </div>
        <Button type="button" variant="outline" size="sm" onClick={onReset}>
          {t("orders.storeChange")}
        </Button>
      </div>
    );
  }

  // ── the manual fields are open below ──────────────────────────────────────
  if (addingNew) {
    return (
      <div className="flex flex-wrap items-center gap-3 rounded-xl border border-dashed p-4">
        {/*
          A real back control, leading and outlined. This was a trailing ghost button and people
          could not find it: borderless grey text at the far edge, with nothing saying it goes
          anywhere. Somebody who opened the new-shop fields by mistake had no way back.
        */}
        <Button type="button" variant="outline" size="sm" onClick={onReset} className="gap-1.5">
          <ArrowLeft className="h-3.5 w-3.5 rtl:rotate-180" aria-hidden />
          {t("orders.wrappingPickInstead")}
        </Button>
        <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
          <Plus className="h-4 w-4" aria-hidden />
        </span>
        <p className="min-w-0 flex-1 text-sm font-medium text-foreground">
          {t("orders.wrappingAddNew")}
        </p>
      </div>
    );
  }

  // ── choosing ──────────────────────────────────────────────────────────────
  return (
    <div
      ref={panel}
      className={cn(
        "overflow-hidden rounded-xl border",
        invalid && "border-destructive/60 bg-destructive/5",
      )}
    >
      <div className={cn("relative", open && "border-b")}>
        <Search
          className="pointer-events-none absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"
          aria-hidden
        />
        <Input
          value={query}
          onChange={(event) => {
            setQuery(event.target.value);
            setOpen(true);
          }}
          onFocus={() => setOpen(true)}
          placeholder={t("orders.wrappingSearch")}
          aria-label={t("orders.wrappingSearch")}
          aria-expanded={open}
          role="combobox"
          className="h-11 rounded-none border-0 ps-9 shadow-none focus-visible:ring-0"
        />
      </div>

      {open ? (
        <div className="max-h-64 overflow-y-auto">
          {result.isPending ? (
            <div className="grid gap-1.5 p-3">
              <Skeleton className="h-11 w-full" />
              <Skeleton className="h-11 w-full" />
              <Skeleton className="h-11 w-full" />
            </div>
          ) : result.isError ? (
            <p className="p-3.5 text-xs text-destructive">{friendlyErrorMessage(result.error, t)}</p>
          ) : places.length === 0 ? (
            <p className="p-3.5 text-sm text-muted-foreground">
              {debounced ? t("common.noResults") : t("orders.wrappingNoneYet")}
            </p>
          ) : (
            <ul role="list" className="divide-y">
              {places.map((option) => (
                <li key={`${option.address}-${option.lat}-${option.lng}`}>
                  <button
                    type="button"
                    onClick={() => {
                      setOpen(false);
                      onChoose(option);
                    }}
                    className="flex w-full items-center gap-3 px-3.5 py-2.5 text-start transition-colors hover:bg-primary-soft"
                  >
                    <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
                      <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">
                        {option.name ?? t("orders.wrappingUnnamed")}
                      </span>
                      <span className="block truncate text-xs text-muted-foreground">
                        {option.address}
                      </span>
                    </span>
                    <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[0.7rem] font-medium text-muted-foreground">
                      {option.stores}
                    </span>
                  </button>
                </li>
              ))}
            </ul>
          )}

          {hidden > 0 ? (
            <p className="border-t bg-muted/30 px-3.5 py-2 text-[0.7rem] text-muted-foreground">
              {t("orders.wrappingMore", { count: String(hidden) })}
            </p>
        ) : null}
      </div>
      ) : null}

      {/*
        Pinned under the list rather than competing with it. The first order through a shop
        nobody has registered is an ordinary thing to be doing, so it is always reachable —
        but it is the second answer, not the first.
      */}
      <button
        type="button"
        onClick={onAddNew}
        className="flex w-full items-center gap-2.5 border-t bg-surface px-3.5 py-2.5 text-start text-sm font-medium transition-colors hover:bg-primary-soft"
      >
        <span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md border border-dashed text-muted-foreground">
          <Plus className="h-3.5 w-3.5" aria-hidden />
        </span>
        {t("orders.wrappingAddNew")}
      </button>
    </div>
  );
}
