import "server-only";

import type { Metadata } from "next";
import Link from "next/link";
import { Search as SearchIcon } from "lucide-react";

import { Pagination } from "@/components/browse/pagination";
import { PosterGrid } from "@/components/browse/poster-grid";
import { SearchFilters } from "@/components/search/search-filters";
import { searchMulti } from "@/services/tmdb/search";
import { searchQuerySchema } from "@/lib/validation/schemas";
import { queryWithoutPage } from "@/lib/catalogue";

/**
 * /search
 *
 * Server-rendered so results are crawlable and shareable, with the refinement
 * bar driving the URL. A short query is *not* an error — the page just prompts
 * for more characters, which is friendlier than a 422.
 */

export const dynamic = "force-dynamic";

/** Below this, TMDB returns noise rather than matches. */
const MIN_QUERY_LENGTH = 2;

interface SearchPageProps {
  searchParams: Promise<Record<string, string | string[] | undefined>>;
}

export async function generateMetadata({ searchParams }: SearchPageProps): Promise<Metadata> {
  const raw = await searchParams;
  const parsed = searchQuerySchema.safeParse(firsts(raw));
  const q = parsed.success ? parsed.data.q : "";

  if (!q) {
    return {
      title: "Search",
      description: "Search movies and TV shows across the FlixTV catalogue.",
    };
  }

  return {
    title: `"${q}" — search results`,
    description: `Movies and TV shows matching “${q}” on FlixTV.`,
    // Result pages are thin, near-duplicate content; keep them out of the index.
    robots: { index: false, follow: true },
  };
}

export default async function SearchPage({ searchParams }: SearchPageProps) {
  const raw = await searchParams;
  const parsed = searchQuerySchema.safeParse(firsts(raw));
  const query = parsed.success ? parsed.data : searchQuerySchema.parse({});

  const tooShort = query.q.length > 0 && query.q.length < MIN_QUERY_LENGTH;
  const shouldSearch = query.q.length >= MIN_QUERY_LENGTH;

  const result = shouldSearch
    ? await searchMulti({
        query: query.q,
        type: query.type,
        page: query.page,
        year: query.year,
        ratingGte: query.ratingGte,
      })
    : null;

  const baseQuery = queryWithoutPage(raw);

  return (
    <main className="pb-24">
      <header className="border-b border-line pb-7 pt-8 md:pt-10">
        <div className="mx-auto w-full max-w-[1600px] px-4 sm:px-6">
          <p className="text-[12px] font-semibold uppercase tracking-[0.16em] text-brand-400">
            Search
          </p>
          <h1 className="mt-2 font-display text-3xl font-extrabold tracking-[-0.03em] text-ink md:text-4xl">
            {query.q ? (
              <>
                Results for <span className="text-ink-muted">“{query.q}”</span>
              </>
            ) : (
              "Find something to watch"
            )}
          </h1>
        </div>
      </header>

      <div className="mx-auto w-full max-w-[1600px] px-4 py-8 sm:px-6">
        {query.q ? (
          <SearchFilters
            resultCount={result?.totalResults}
            totalPages={result?.totalPages}
          />
        ) : null}

        {!shouldSearch ? (
          <EmptyState tooShort={tooShort} query={query.q} />
        ) : (
          <>
            <PosterGrid
              items={result?.items ?? []}
              priorityCount={4}
              emptyMessage={
                query.q
                  ? `Nothing matched “${query.q}”.`
                  : "Nothing to show here yet."
              }
            />
            <Pagination
              page={result?.page ?? 1}
              totalPages={result?.totalPages ?? 0}
              basePath="/search"
              query={baseQuery}
            />
          </>
        )}
      </div>
    </main>
  );
}

function EmptyState({ tooShort, query }: { tooShort: boolean; query: string }) {
  return (
    <div className="rounded-xl border border-dashed border-line-strong bg-surface-2/40 px-6 py-20 text-center">
      <span className="mx-auto grid size-12 place-items-center rounded-full bg-brand-500/12 text-brand-400 ring-1 ring-brand-500/20">
        <SearchIcon className="size-5" />
      </span>

      {query ? (
        <>
          <p className="mt-5 font-display text-lg font-bold text-ink">
            Keep typing — {MIN_QUERY_LENGTH} characters minimum
          </p>
          <p className="mt-1.5 text-[13px] text-ink-muted">
            {tooShort
              ? `“${query}” is too short to search. Add a couple more letters.`
              : "Use the search box in the header to look for a title."}
          </p>
        </>
      ) : (
        <>
          <p className="mt-5 font-display text-lg font-bold text-ink">What are you in the mood for?</p>
          <p className="mt-1.5 text-[13px] text-ink-muted">
            Search by title from the box above, or start from a shelf.
          </p>
          <div className="mt-7 flex flex-wrap items-center justify-center gap-2.5">
            <ShelfLink href="/movies">Browse movies</ShelfLink>
            <ShelfLink href="/tv">Browse TV shows</ShelfLink>
            <ShelfLink href="/anime">Browse anime</ShelfLink>
            <ShelfLink href="/genres">All genres</ShelfLink>
          </div>
        </>
      )}
    </div>
  );
}

function ShelfLink({ href, children }: { href: string; children: React.ReactNode }) {
  return (
    <Link
      href={href}
      className="rounded-pill border border-line px-3.5 py-1.5 text-[13px] font-semibold text-ink-soft transition-colors hover:border-white/30 hover:text-ink"
    >
      {children}
    </Link>
  );
}

/** Collapse `string | string[]` from Next's searchParams to a single value. */
function firsts(params: Record<string, string | string[] | undefined>): Record<string, string | undefined> {
  const out: Record<string, string | undefined> = {};
  for (const [key, value] of Object.entries(params)) {
    if (Array.isArray(value)) out[key] = value[0];
    else out[key] = value;
  }
  return out;
}
