"use client";

import { useEffect, useState } from "react";

import { ProductFiltersPanel } from "@/components/products/ProductFilters";
import { ProductGridSkeleton, SearchResultGrid } from "@/components/products/ProductGrid";
import { ProductSearch } from "@/components/products/ProductSearch";
import { ProductSort } from "@/components/products/ProductSort";
import { CloseIcon, EmptyState, ErrorState, Spinner } from "@/components/ui";
import { useProductSearch } from "@/hooks/useProductSearch";
import type { CatalogFacets } from "@/lib/products";
import type { ProductFilters, ProductSort as ProductSortValue } from "@/types/search";

/**
 * The product listing experience: search box, filters, sort control, result
 * count and the responsive grid of results.
 *
 * State lives in `useProductSearch`; this component only renders it. The initial
 * values come from the URL, so a shared link opens on the same results.
 */
export function ProductsExplorer({
  facets,
  initialQuery = "",
  initialFilters = {},
  initialSort = "relevance",
}: {
  facets: CatalogFacets;
  initialQuery?: string;
  initialFilters?: ProductFilters;
  initialSort?: ProductSortValue;
}) {
  const search = useProductSearch({ initialQuery, initialFilters, initialSort });
  const [filtersOpen, setFiltersOpen] = useState(false);

  const hasQuery = search.input.trim().length > 0;
  const results = search.data?.results ?? [];
  const count = search.data?.meta.total ?? 0;

  // Close the mobile filter sheet when navigating to a product.
  useEffect(() => {
    if (!filtersOpen) return;
    function onKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") setFiltersOpen(false);
    }
    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, [filtersOpen]);

  return (
    <div className="space-y-6">
      <ProductSearch
        value={search.input}
        onChange={search.setInput}
        onClear={search.clear}
        isLoading={search.isRefreshing}
        suggestions={search.data?.suggestions ?? []}
      />

      <div className="flex flex-wrap items-center justify-between gap-3">
        <p className="text-sm text-slate-600" aria-live="polite">
          {search.error ? (
            "Search failed"
          ) : (
            <>
              Showing <strong className="text-slate-900">{count}</strong>{" "}
              {count === 1 ? "product" : "products"}
              {hasQuery ? (
                <>
                  {" "}
                  for <span className="font-medium text-slate-900">“{search.input.trim()}”</span>
                </>
              ) : (
                " in the catalogue"
              )}
              {search.meta ? (
                <span className="ml-1.5 text-xs text-slate-400">
                  ({search.meta.took}ms · {search.meta.provider === "local-lsa" ? "offline vectors" : search.meta.provider})
                  {search.meta.providerFallback ? " · fallback" : ""}
                </span>
              ) : null}
            </>
          )}
        </p>

        <div className="flex items-center gap-2">
          <button
            type="button"
            onClick={() => setFiltersOpen(true)}
            className="inline-flex items-center gap-1.5 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-700 transition-colors hover:border-slate-400 lg:hidden"
          >
            <svg viewBox="0 0 24 24" className="size-4" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
              <path d="M4 6h16M7 12h10M10 18h4" />
            </svg>
            Filters
            {hasFilters(search.filters) ? (
              <span className="ml-0.5 rounded-full bg-indigo-600 px-1.5 text-[11px] font-bold text-white">
                {countFilters(search.filters)}
              </span>
            ) : null}
          </button>

          <ProductSort
            value={search.sort}
            onChange={search.setSort}
            relevanceDisabled={!hasQuery}
          />
        </div>
      </div>

      <div className="grid gap-8 lg:grid-cols-[16rem_1fr]">
        <aside className="hidden lg:block">
          <div className="sticky top-24 rounded-2xl border border-slate-200 bg-white p-5">
            <ProductFiltersPanel
              facets={facets}
              filters={search.filters}
              onChange={search.setFilters}
              onReset={search.resetFilters}
              resultCount={count}
            />
          </div>
        </aside>

        <section aria-label="Product results" aria-busy={search.isLoading}>
          {search.error ? (
            <ErrorState message={search.error} onRetry={search.retry} />
          ) : search.isLoading && !search.data ? (
            <ProductGridSkeleton count={8} />
          ) : results.length === 0 ? (
            <EmptyState
              title="No products found"
              description={
                hasQuery
                  ? `Nothing in the catalogue matches “${search.input.trim()}”. Try a broader phrase — the search understands meaning, so “something for the gym” works too.`
                  : "No products match the selected filters. Try widening your price range or clearing a filter."
              }
              action={
                hasQuery ? (
                  <button
                    type="button"
                    onClick={search.clear}
                    className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2 focus-visible:outline-none"
                  >
                    Clear search
                  </button>
                ) : (
                  <button
                    type="button"
                    onClick={search.resetFilters}
                    className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2 focus-visible:outline-none"
                  >
                    Clear filters
                  </button>
                )
              }
            />
          ) : (
            <div className={search.isRefreshing ? "opacity-60 transition-opacity" : undefined}>
              {search.isRefreshing ? (
                <p className="mb-3 inline-flex items-center gap-2 text-xs text-slate-500">
                  <Spinner />
                  Updating results…
                </p>
              ) : null}
              <SearchResultGrid results={results} />
            </div>
          )}
        </section>
      </div>

      {filtersOpen ? (
        <div className="fixed inset-0 z-50 lg:hidden">
          <div
            className="absolute inset-0 bg-slate-900/40"
            onClick={() => setFiltersOpen(false)}
            aria-hidden="true"
          />
          <div
            role="dialog"
            aria-modal="true"
            aria-label="Filters"
            className="absolute inset-x-0 bottom-0 max-h-[85vh] overflow-y-auto rounded-t-3xl bg-white p-5 shadow-2xl"
          >
            <div className="mb-4 flex items-center justify-between">
              <h2 className="text-base font-semibold text-slate-900">Filters</h2>
              <button
                type="button"
                onClick={() => setFiltersOpen(false)}
                className="rounded-full p-2 text-slate-500 hover:bg-slate-100 focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:outline-none"
              >
                <CloseIcon />
                <span className="sr-only">Close filters</span>
              </button>
            </div>
            <ProductFiltersPanel
              facets={facets}
              filters={search.filters}
              onChange={search.setFilters}
              onReset={search.resetFilters}
              resultCount={count}
            />
            <button
              type="button"
              onClick={() => setFiltersOpen(false)}
              className="mt-6 w-full rounded-xl bg-indigo-600 px-4 py-3 text-sm font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2 focus-visible:outline-none"
            >
              Show {count} {count === 1 ? "product" : "products"}
            </button>
          </div>
        </div>
      ) : null}
    </div>
  );
}

function hasFilters(filters: ProductFilters): boolean {
  return countFilters(filters) > 0;
}

function countFilters(filters: ProductFilters): number {
  return Object.entries(filters).filter(
    ([, value]) => value !== undefined && value !== false && value !== "",
  ).length;
}
