"use client";

import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useMemo, useState, useTransition } from "react";
import { SlidersHorizontal, X } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Select } from "@/components/ui/form";
import type { FilterOption } from "@/lib/types";
import { cn } from "@/lib/utils";

/**
 * Catalogue filter bar.
 *
 * State lives in the URL, never in React alone: every change rewrites the
 * query string and lets the Server Component re-render with fresh TMDB data.
 * That makes each filter combination a shareable, back-button-friendly link.
 */

export interface BrowseFiltersProps {
  genres: FilterOption[];
  sortOptions: FilterOption[];
  /** Hidden on the genre page, where the genre is already fixed by the route. */
  showGenre?: boolean;
  resultCount?: number;
}

const YEAR_MIN = 1920;
const currentYear = new Date().getFullYear();

export function BrowseFilters({
  genres,
  sortOptions,
  showGenre = true,
  resultCount,
}: BrowseFiltersProps) {
  const router = useRouter();
  const params = useSearchParams();
  const [pending, startTransition] = useTransition();
  const [open, setOpen] = useState(false);

  const current = useMemo(
    () => ({
      genre: params.get("genre") ?? "",
      sort: params.get("sort") ?? "",
      year: params.get("year") ?? "",
      ratingGte: params.get("ratingGte") ?? "",
    }),
    [params],
  );

  const apply = useCallback(
    (patch: Record<string, string>) => {
      const next = new URLSearchParams(params.toString());

      for (const [key, value] of Object.entries(patch)) {
        if (value) next.set(key, value);
        else next.delete(key);
      }

      // Any filter change invalidates the current page number.
      next.delete("page");

      const query = next.toString();
      startTransition(() => {
        router.replace(query ? `?${query}` : window.location.pathname, { scroll: false });
      });
    },
    [params, router],
  );

  const reset = useCallback(() => {
    startTransition(() => {
      router.replace(window.location.pathname, { scroll: false });
    });
  }, [router]);

  const activeCount = Object.entries(current).filter(([key, value]) => {
    if (!value) return false;
    // The implicit default sort should not read as "an active filter".
    if (key === "sort" && value === "popularity.desc") return false;
    return true;
  }).length;

  const controls = (
    <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
      {showGenre ? (
        <Select
          aria-label="Genre"
          value={current.genre}
          onChange={(e) => apply({ genre: e.target.value })}
          className="h-11 text-[13px]"
        >
          <option value="">All genres</option>
          {genres.map((g) => (
            <option key={g.value} value={g.value}>
              {g.label}
            </option>
          ))}
        </Select>
      ) : null}

      <Select
        aria-label="Sort by"
        value={current.sort}
        onChange={(e) => apply({ sort: e.target.value })}
        className="h-11 text-[13px]"
      >
        <option value="">Most popular</option>
        {sortOptions.map((s) => (
          <option key={s.value} value={s.value}>
            {s.label}
          </option>
        ))}
      </Select>

      <Select
        aria-label="Release year"
        value={current.year}
        onChange={(e) => apply({ year: e.target.value })}
        className="h-11 text-[13px]"
      >
        <option value="">Any year</option>
        {buildYears().map((y) => (
          <option key={y} value={String(y)}>
            {y}
          </option>
        ))}
      </Select>

      <Select
        aria-label="Minimum rating"
        value={current.ratingGte}
        onChange={(e) => apply({ ratingGte: e.target.value })}
        className="h-11 text-[13px]"
      >
        <option value="">Any rating</option>
        <option value="6">6+</option>
        <option value="7">7+</option>
        <option value="8">8+</option>
        <option value="9">9+</option>
      </Select>
    </div>
  );

  return (
    <div className="mb-7">
      <div className="flex items-center gap-3">
        <Button
          variant="secondary"
          size="sm"
          onClick={() => setOpen((v) => !v)}
          aria-expanded={open}
          aria-controls="browse-filter-panel"
          className="lg:hidden"
        >
          <SlidersHorizontal className="size-3.5" />
          Filters
          {activeCount > 0 ? (
            <span className="ml-0.5 grid size-4 place-items-center rounded-full bg-brand-500 text-[10px] font-bold text-white">
              {activeCount}
            </span>
          ) : null}
        </Button>

        {typeof resultCount === "number" ? (
          <p
            aria-live="polite"
            className={cn(
              "text-[13px] font-medium text-ink-muted transition-opacity",
              pending && "opacity-40",
            )}
          >
            {resultCount.toLocaleString("en-US")} {resultCount === 1 ? "title" : "titles"}
          </p>
        ) : null}

        {activeCount > 0 ? (
          <button
            type="button"
            onClick={reset}
            className="ml-auto inline-flex items-center gap-1.5 text-[13px] font-semibold text-ink-muted transition-colors hover:text-ink"
          >
            <X className="size-3.5" />
            Clear
          </button>
        ) : null}
      </div>

      {/* Desktop: always visible. Mobile: a disclosure panel. */}
      <div className="mt-3 hidden lg:block">{controls}</div>
      {open ? (
        <div id="browse-filter-panel" className="mt-3 lg:hidden">
          {controls}
        </div>
      ) : null}
    </div>
  );
}

function buildYears(): number[] {
  const years: number[] = [];
  for (let y = currentYear + 1; y >= YEAR_MIN; y -= 1) years.push(y);
  return years;
}
