"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import { Form, InputGroup, Button, Table, Alert, Badge } from "react-bootstrap";
import { Search } from "react-bootstrap-icons";
import EntryLink from "../components/EntryLink";
import { usePathname, useRouter } from "next/navigation";
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
import ExplorerLayout from "@/components/explorer/ExplorerLayout";
import FilterSidebar from "@/components/explorer/FilterSidebar";
import FilterSection from "@/components/explorer/FilterSection";
import MultiSelectChips from "@/components/explorer/MultiSelectChips";
import Pagination from "@/components/explorer/Pagination";
import useSearchFetch from "@/hooks/useSearchFetch";
import type { PagedResult, EntryFacets, EntrySearchScope } from "@/types/zxdb";
import { preferredMachineIds, parseMachineIds } from "@/utils/params";

const PAGE_SIZE = 20;

type Item = {
  id: number;
  title: string;
  isXrated: number;
  genreId: number | null;
  genreName?: string | null;
  machinetypeId: number | null;
  machinetypeName?: string | null;
  languageId: string | null;
  languageName?: string | null;
};

export default function EntriesExplorer({
  initial,
  initialGenres,
  initialLanguages,
  initialMachines,
  initialFacets,
  initialUrlState,
}: {
  initial?: PagedResult<Item>;
  initialGenres?: { id: number; name: string }[];
  initialLanguages?: { id: string; name: string }[];
  initialMachines?: { id: number; name: string }[];
  initialFacets?: EntryFacets | null;
  initialUrlState?: {
    q: string;
    page: number;
    genreId: string | number | "";
    languageId: string | "";
    machinetypeId: string;
    sort: "title" | "id_desc";
    scope?: EntrySearchScope;
  };
}) {
  const router = useRouter();
  const pathname = usePathname();

  // -- Search state --
  const [q, setQ] = useState(initialUrlState?.q ?? "");
  const [appliedQ, setAppliedQ] = useState(initialUrlState?.q ?? "");
  const [page, setPage] = useState(initial?.page ?? initialUrlState?.page ?? 1);
  const [genreId, setGenreId] = useState<number | "">(
    initialUrlState?.genreId === "" ? "" : initialUrlState?.genreId ? Number(initialUrlState.genreId) : ""
  );
  const [languageId, setLanguageId] = useState<string | "">(initialUrlState?.languageId ?? "");
  const [machinetypeIds, setMachinetypeIds] = useState<number[]>(parseMachineIds(initialUrlState?.machinetypeId));
  const [sort, setSort] = useState<"title" | "id_desc">(initialUrlState?.sort ?? "id_desc");
  const [scope, setScope] = useState<EntrySearchScope>(initialUrlState?.scope ?? "title");
  const [facets, setFacets] = useState<EntryFacets | null>(initialFacets ?? null);

  // -- Filter lists --
  const [genres, setGenres] = useState<{ id: number; name: string }[]>(initialGenres ?? []);
  const [languages, setLanguages] = useState<{ id: string; name: string }[]>(initialLanguages ?? []);
  const [machines, setMachines] = useState<{ id: number; name: string }[]>(initialMachines ?? []);

  // Capture facets from the API response alongside paged results
  const handleExtra = useCallback((json: Record<string, unknown>) => {
    if (json.facets) setFacets(json.facets as EntryFacets);
  }, []);

  // -- Fetch with abort control --
  const { data, loading, error, fetch: doFetch, syncData } = useSearchFetch<Item>(
    "/api/zxdb/search",
    initial ?? null,
    handleExtra,
  );

  // Skip initial fetch when SSR data already matches
  const isFirstRender = useRef(true);

  const totalPages = useMemo(
    () => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1),
    [data],
  );

  // -- URL helpers --
  const buildParams = useCallback(
    (p: number) => {
      const params = new URLSearchParams();
      if (appliedQ) params.set("q", appliedQ);
      params.set("page", String(p));
      if (genreId !== "") params.set("genreId", String(genreId));
      if (languageId !== "") params.set("languageId", String(languageId));
      if (machinetypeIds.length > 0) params.set("machinetypeId", machinetypeIds.join(","));
      if (sort) params.set("sort", sort);
      if (scope !== "title") params.set("scope", scope);
      return params;
    },
    [appliedQ, genreId, languageId, machinetypeIds, sort, scope],
  );

  const buildHref = useCallback(
    (p: number) => {
      const qs = buildParams(p).toString();
      return qs ? `${pathname}?${qs}` : pathname;
    },
    [buildParams, pathname],
  );

  // -- Derived --
  const orderedMachines = useMemo(() => {
    const seen = new Set(preferredMachineIds);
    const preferred = preferredMachineIds.map((id) => machines.find((m) => m.id === id)).filter(Boolean) as { id: number; name: string }[];
    const rest = machines.filter((m) => !seen.has(m.id));
    return [...preferred, ...rest];
  }, [machines]);

  const machineOptions = useMemo(
    () => orderedMachines.map((m) => ({ id: m.id, label: m.name })),
    [orderedMachines],
  );

  const hasNonDefaultMachineFilter = machinetypeIds.join(",") !== preferredMachineIds.join(",") ||
    machinetypeIds.length !== machines.length;

  // -- Fetch + URL sync on filter/page changes --
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      router.replace(buildHref(page), { scroll: false });
      return;
    }

    router.replace(buildHref(page), { scroll: false });

    const params = buildParams(page);
    params.set("pageSize", String(PAGE_SIZE));
    params.set("facets", "true");
    doFetch(params);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [page, genreId, languageId, machinetypeIds, sort, scope, appliedQ]);

  // Sync SSR data when navigating (browser back/forward)
  useEffect(() => {
    if (initial) syncData(initial);
  }, [initial, syncData]);

  // Load filter lists on mount if not provided by server
  useEffect(() => {
    if (initialGenres && initialLanguages && initialMachines) return;
    async function loadLists() {
      try {
        const [g, l, m] = await Promise.all([
          fetch("/api/zxdb/genres", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/languages", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/machinetypes", { cache: "force-cache" }).then((r) => r.json()),
        ]);
        setGenres(g.items ?? []);
        setLanguages(l.items ?? []);
        setMachines(m.items ?? []);
      } catch { /* filter lists are non-critical */ }
    }
    loadLists();
  }, [initialGenres, initialLanguages, initialMachines]);

  function onSubmit(e: React.FormEvent) {
    e.preventDefault();
    setAppliedQ(q);
    setPage(1);
  }

  function searchAllMachines() {
    setMachinetypeIds(machineOptions.map((m) => m.id));
    setPage(1);
  }

  function resetFilters() {
    setQ("");
    setAppliedQ("");
    setGenreId("");
    setLanguageId("");
    setMachinetypeIds(preferredMachineIds.slice());
    setSort("id_desc");
    setScope("title");
    setPage(1);
  }

  return (
    <div>
      <ZxdbBreadcrumbs
        items={[
          { label: "ZXDB", href: "/zxdb" },
          { label: "Entries" },
        ]}
      />

      <ExplorerLayout
        title="Entries"
        subtitle={data ? `${data.total.toLocaleString()} results` : "Loading results..."}
        sidebar={(
          <FilterSidebar onReset={resetFilters} loading={loading}>
            <Form onSubmit={onSubmit} className="d-flex flex-column gap-2">
              <InputGroup>
                <Form.Control
                  type="text"
                  placeholder="Search titles..."
                  value={q}
                  onChange={(e) => setQ(e.target.value)}
                />
                <Button variant="primary" type="submit" disabled={loading}>
                  <Search size={14} />
                </Button>
              </InputGroup>

              <FilterSection label="Genre" badge={genreId !== "" ? genres.find((g) => g.id === Number(genreId))?.name : undefined}>
                <Form.Select size="sm" value={genreId} onChange={(e) => { setGenreId(e.target.value === "" ? "" : Number(e.target.value)); setPage(1); }}>
                  <option value="">All genres</option>
                  {genres.map((g) => (
                    <option key={g.id} value={g.id}>{g.name}</option>
                  ))}
                </Form.Select>
              </FilterSection>

              <FilterSection label="Language" badge={languageId !== "" ? languages.find((l) => l.id === languageId)?.name : undefined}>
                <Form.Select size="sm" value={languageId} onChange={(e) => { setLanguageId(e.target.value); setPage(1); }}>
                  <option value="">All languages</option>
                  {languages.map((l) => (
                    <option key={l.id} value={l.id}>{l.name}</option>
                  ))}
                </Form.Select>
              </FilterSection>

              <FilterSection
                label="Machine"
                badge={`${machinetypeIds.length} selected`}
                defaultOpen={false}
              >
                <MultiSelectChips
                  options={machineOptions}
                  selected={machinetypeIds}
                  collapsible
                  onToggle={(id) => {
                    setMachinetypeIds((current) => {
                      const next = new Set(current);
                      if (next.has(id)) {
                        next.delete(id);
                      } else {
                        next.add(id);
                      }
                      const order = machineOptions.map((item) => item.id);
                      const filtered = order.filter((value) => next.has(value));
                      return filtered.length ? filtered : preferredMachineIds.slice();
                    });
                    setPage(1);
                  }}
                />
                <div className="d-flex gap-2 mt-1">
                  <Button
                    variant="outline-secondary"
                    size="sm"
                    onClick={() => { setMachinetypeIds(machineOptions.map((m) => m.id)); setPage(1); }}
                    disabled={machinetypeIds.length === machineOptions.length}
                  >
                    All
                  </Button>
                  <Button
                    variant="outline-secondary"
                    size="sm"
                    onClick={() => { setMachinetypeIds(preferredMachineIds.slice()); setPage(1); }}
                    disabled={machinetypeIds.join(",") === preferredMachineIds.join(",")}
                  >
                    Default
                  </Button>
                </div>
              </FilterSection>

              <FilterSection label="Sort &amp; Scope">
                <Form.Select size="sm" className="mb-2" value={sort} onChange={(e) => { setSort(e.target.value as "title" | "id_desc"); setPage(1); }}>
                  <option value="title">Title (A-Z)</option>
                  <option value="id_desc">Newest</option>
                </Form.Select>
                <Form.Select size="sm" value={scope} onChange={(e) => { setScope(e.target.value as EntrySearchScope); setPage(1); }}>
                  <option value="title">Titles only</option>
                  <option value="title_aliases">Titles + Aliases</option>
                  <option value="title_aliases_origins">Titles + Aliases + Origins</option>
                </Form.Select>
              </FilterSection>

              {facets && (facets.flags.hasAliases > 0 || facets.flags.hasOrigins > 0) && (
                <FilterSection label="Facets">
                  <div className="d-flex flex-wrap gap-1">
                    <Button
                      size="sm"
                      variant={scope === "title_aliases" ? "primary" : "outline-secondary"}
                      onClick={() => { setScope("title_aliases"); setPage(1); }}
                      disabled={facets.flags.hasAliases === 0}
                    >
                      Aliases <Badge bg="light" text="dark" className="ms-1">{facets.flags.hasAliases}</Badge>
                    </Button>
                    <Button
                      size="sm"
                      variant={scope === "title_aliases_origins" ? "primary" : "outline-secondary"}
                      onClick={() => { setScope("title_aliases_origins"); setPage(1); }}
                      disabled={facets.flags.hasOrigins === 0}
                    >
                      Origins <Badge bg="light" text="dark" className="ms-1">{facets.flags.hasOrigins}</Badge>
                    </Button>
                  </div>
                </FilterSection>
              )}

              {error && <Alert variant="danger" className="py-1 px-2 small mb-0">{error}</Alert>}
            </Form>
          </FilterSidebar>
        )}
      >
        <div className={loading ? "opacity-50" : ""} style={{ transition: "opacity 0.15s" }}>
          {data && data.items.length === 0 && !loading && (
            <Alert variant="warning">
              No results found.
              {hasNonDefaultMachineFilter && (
                <span>
                  {" "}Filtering by{" "}
                  <strong>
                    {machinetypeIds
                      .map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`)
                      .join(", ")}
                  </strong>
                  {" "}&mdash;{" "}
                  <Alert.Link onClick={searchAllMachines}>
                    search all machines
                  </Alert.Link>?
                </span>
              )}
            </Alert>
          )}
          {data && data.items.length > 0 && (
            <div className="table-responsive">
              <Table striped hover className="align-middle">
                <thead>
                  <tr>
                    <th style={{ width: 80 }}>ID</th>
                    <th>Title</th>
                    <th style={{ width: 160 }}>Genre</th>
                    <th style={{ width: 160 }}>Machine</th>
                    <th style={{ width: 120 }}>Language</th>
                  </tr>
                </thead>
                <tbody>
                  {data.items.map((it) => (
                    <tr key={it.id}>
                      <td><EntryLink id={it.id} /></td>
                      <td><EntryLink id={it.id} title={it.title} /></td>
                      <td>
                        {it.genreId != null ? (
                          it.genreName ? (
                            <Link href={`/zxdb/genres/${it.genreId}`}>{it.genreName}</Link>
                          ) : (
                            <span>{it.genreId}</span>
                          )
                        ) : (
                          <span className="text-secondary">-</span>
                        )}
                      </td>
                      <td>
                        {it.machinetypeId != null ? (
                          it.machinetypeName ? (
                            <Link href={`/zxdb/machinetypes/${it.machinetypeId}`}>{it.machinetypeName}</Link>
                          ) : (
                            <span>{it.machinetypeId}</span>
                          )
                        ) : (
                          <span className="text-secondary">-</span>
                        )}
                      </td>
                      <td>
                        {it.languageId ? (
                          it.languageName ? (
                            <Link href={`/zxdb/languages/${it.languageId}`}>{it.languageName}</Link>
                          ) : (
                            <span>{it.languageId}</span>
                          )
                        ) : (
                          <span className="text-secondary">-</span>
                        )}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </Table>
            </div>
          )}
        </div>
      </ExplorerLayout>

      <Pagination
        page={data?.page ?? page}
        totalPages={totalPages}
        loading={loading}
        buildHref={buildHref}
        onPageChange={setPage}
      />

      <hr />
      <div className="d-flex flex-wrap gap-2">
        <Link className="btn btn-sm btn-outline-secondary" href="/zxdb/labels">Browse Labels</Link>
        <Link className="btn btn-sm btn-outline-secondary" href="/zxdb/genres">Browse Genres</Link>
        <Link className="btn btn-sm btn-outline-secondary" href="/zxdb/languages">Browse Languages</Link>
        <Link className="btn btn-sm btn-outline-secondary" href="/zxdb/machinetypes">Browse Machines</Link>
      </div>
    </div>
  );
}
