"use client";

import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
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 MultiSelectChips from "@/components/explorer/MultiSelectChips";

const preferredMachineIds = [27, 26, 8, 9];

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;
};

type SearchScope = "title" | "title_aliases" | "title_aliases_origins";

type Paged<T> = {
  items: T[];
  page: number;
  pageSize: number;
  total: number;
};

type EntryFacets = {
  genres: { id: number; name: string; count: number }[];
  languages: { id: string; name: string; count: number }[];
  machinetypes: { id: number; name: string; count: number }[];
  flags: { hasAliases: number; hasOrigins: number };
};

export default function EntriesExplorer({
  initial,
  initialGenres,
  initialLanguages,
  initialMachines,
  initialFacets,
  initialUrlState,
}: {
  initial?: Paged<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?: SearchScope;
  };
}) {
  const parseMachineIds = (value?: string) => {
    if (!value) return preferredMachineIds.slice();
    const ids = value
      .split(",")
      .map((id) => Number(id.trim()))
      .filter((id) => Number.isFinite(id) && id > 0);
    return ids.length ? ids : preferredMachineIds.slice();
  };

  const router = useRouter();
  const pathname = usePathname();

  const [q, setQ] = useState(initialUrlState?.q ?? "");
  const [appliedQ, setAppliedQ] = useState(initialUrlState?.q ?? "");
  const [page, setPage] = useState(initial?.page ?? initialUrlState?.page ?? 1);
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState<Paged<Item> | null>(initial ?? null);
  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 ?? []);
  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<SearchScope>(initialUrlState?.scope ?? "title");
  const [facets, setFacets] = useState<EntryFacets | null>(initialFacets ?? null);
  const preferredMachineNames = useMemo(() => {
    if (!machines.length) return preferredMachineIds.map((id) => `#${id}`);
    return preferredMachineIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
  }, [machines]);
  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 pageSize = 20;
  const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
  const activeFilters = useMemo(() => {
    const chips: string[] = [];
    if (appliedQ) chips.push(`q: ${appliedQ}`);
    if (genreId !== "") {
      const name = genres.find((g) => g.id === Number(genreId))?.name ?? `#${genreId}`;
      chips.push(`genre: ${name}`);
    }
    if (languageId !== "") {
      const name = languages.find((l) => l.id === languageId)?.name ?? languageId;
      chips.push(`lang: ${name}`);
    }
    if (machinetypeIds.length > 0) {
      const names = machinetypeIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
      chips.push(`machine: ${names.join(", ")}`);
    }
    if (scope === "title_aliases") chips.push("scope: titles + aliases");
    if (scope === "title_aliases_origins") chips.push("scope: titles + aliases + origins");
    return chips;
  }, [appliedQ, genreId, languageId, machinetypeIds, scope, genres, languages, machines]);

  function updateUrl(nextPage = page) {
    const params = new URLSearchParams();
    if (appliedQ) params.set("q", appliedQ);
    params.set("page", String(nextPage));
    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);
    const qs = params.toString();
    router.replace(qs ? `${pathname}?${qs}` : pathname);
  }

  async function fetchData(query: string, p: number, withFacets: boolean) {
    setLoading(true);
    try {
      const params = new URLSearchParams();
      if (query) params.set("q", query);
      params.set("page", String(p));
      params.set("pageSize", String(pageSize));
      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);
      if (withFacets) params.set("facets", "true");
      const res = await fetch(`/api/zxdb/search?${params.toString()}`);
      if (!res.ok) throw new Error(`Failed: ${res.status}`);
      const json = await res.json();
      setData(json);
      if (withFacets && json.facets) {
        setFacets(json.facets as EntryFacets);
      }
    } catch (e) {
      console.error(e);
      setData({ items: [], page: 1, pageSize, total: 0 });
    } finally {
      setLoading(false);
    }
  }

  // Sync from SSR payload on navigation
  useEffect(() => {
    if (initial) {
      setData(initial);
      setPage(initial.page);
    }
  }, [initial]);

  // Client fetch when filters/paging/sort change; also keep URL in sync
  useEffect(() => {
    // Avoid extra fetch if SSR already matches this exact default state
    const initialPage = initial?.page ?? 1;
    if (
      initial &&
      page === initialPage &&
      (initialUrlState?.q ?? "") === appliedQ &&
      (initialUrlState?.genreId === "" ? "" : Number(initialUrlState?.genreId ?? "")) === (genreId === "" ? "" : Number(genreId)) &&
      (initialUrlState?.languageId ?? "") === (languageId ?? "") &&
      parseMachineIds(initialUrlState?.machinetypeId).join(",") === machinetypeIds.join(",") &&
      sort === (initialUrlState?.sort ?? "id_desc") &&
      (initialUrlState?.scope ?? "title") === scope
    ) {
      updateUrl(page);
      return;
    }
    updateUrl(page);
    fetchData(appliedQ, page, true);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [page, genreId, languageId, machinetypeIds, sort, scope, appliedQ]);

  // Load filter lists on mount only 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 {}
    }
    loadLists();
  }, [initialGenres, initialLanguages, initialMachines]);

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

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

  const prevHref = useMemo(() => {
    const params = new URLSearchParams();
    if (appliedQ) params.set("q", appliedQ);
    params.set("page", String(Math.max(1, (data?.page ?? 1) - 1)));
    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 `/zxdb/entries?${params.toString()}`;
  }, [appliedQ, data?.page, genreId, languageId, machinetypeIds, sort, scope]);

  const nextHref = useMemo(() => {
    const params = new URLSearchParams();
    if (appliedQ) params.set("q", appliedQ);
    params.set("page", String(Math.max(1, (data?.page ?? 1) + 1)));
    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 `/zxdb/entries?${params.toString()}`;
  }, [appliedQ, data?.page, genreId, languageId, machinetypeIds, sort, scope]);

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

      <ExplorerLayout
        title="Entries"
        subtitle={data ? `${data.total.toLocaleString()} results` : "Loading results..."}
        chips={activeFilters}
        onClearChips={resetFilters}
        sidebar={(
          <FilterSidebar>
            <form className="d-flex flex-column gap-2" onSubmit={onSubmit}>
              <div>
                <label className="form-label small text-secondary">Search</label>
                <input
                  type="text"
                  className="form-control"
                  placeholder="Search titles..."
                  value={q}
                  onChange={(e) => setQ(e.target.value)}
                />
              </div>
              <div className="d-grid">
                <button className="btn btn-primary" type="submit" disabled={loading}>Search</button>
              </div>
              <div>
                <label className="form-label small text-secondary">Genre</label>
                <select className="form-select" 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>
                  ))}
                </select>
              </div>
              <div>
                <label className="form-label small text-secondary">Language</label>
                <select className="form-select" 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>
                  ))}
                </select>
              </div>
              <div>
                <label className="form-label small text-secondary">Machine</label>
                <MultiSelectChips
                  options={machineOptions}
                  selected={machinetypeIds}
                  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);
                      return order.filter((value) => next.has(value));
                    });
                    setPage(1);
                  }}
                />
                <div className="form-text">Preferred: {preferredMachineNames.join(", ")}</div>
              </div>
              <div>
                <label className="form-label small text-secondary">Sort</label>
                <select className="form-select" 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>
                </select>
              </div>
              <div>
                <label className="form-label small text-secondary">Search scope</label>
                <select className="form-select" value={scope} onChange={(e) => { setScope(e.target.value as SearchScope); setPage(1); }}>
                  <option value="title">Titles</option>
                  <option value="title_aliases">Titles + Aliases</option>
                  <option value="title_aliases_origins">Titles + Aliases + Origins</option>
                </select>
              </div>
              {facets && (
                <div>
                  <div className="text-secondary small mb-1">Facets</div>
                  <div className="d-flex flex-wrap gap-2">
                    <button
                      type="button"
                      className={`btn btn-sm ${scope === "title_aliases" ? "btn-primary" : "btn-outline-secondary"}`}
                      onClick={() => { setScope("title_aliases"); setPage(1); }}
                      disabled={facets.flags.hasAliases === 0}
                      title="Show results that match aliases"
                    >
                      Has aliases ({facets.flags.hasAliases})
                    </button>
                    <button
                      type="button"
                      className={`btn btn-sm ${scope === "title_aliases_origins" ? "btn-primary" : "btn-outline-secondary"}`}
                      onClick={() => { setScope("title_aliases_origins"); setPage(1); }}
                      disabled={facets.flags.hasOrigins === 0}
                      title="Show results that match origins"
                    >
                      Has origins ({facets.flags.hasOrigins})
                    </button>
                  </div>
                </div>
              )}
              {loading && <div className="text-secondary small">Loading...</div>}
            </form>
          </FilterSidebar>
        )}
      >
        {data && data.items.length === 0 && !loading && (
          <div className="alert alert-warning">No results.</div>
        )}
        {data && data.items.length > 0 && (
          <div className="table-responsive">
            <table className="table table-striped table-hover 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>
        )}
      </ExplorerLayout>

      <div className="d-flex align-items-center gap-2 mt-4">
        <span>Page {data?.page ?? 1} / {totalPages}</span>
        <div className="ms-auto d-flex gap-2">
          <Link
            className={`btn btn-outline-secondary ${!data || (data.page <= 1) ? "disabled" : ""}`}
            aria-disabled={!data || data.page <= 1}
            href={prevHref}
            onClick={(e) => {
              if (!data || data.page <= 1) return;
              e.preventDefault();
              setPage((p) => Math.max(1, p - 1));
            }}
          >
            Prev
          </Link>
          <Link
            className={`btn btn-outline-secondary ${!data || (data.page >= totalPages) ? "disabled" : ""}`}
            aria-disabled={!data || data.page >= totalPages}
            href={nextHref}
            onClick={(e) => {
              if (!data || data.page >= totalPages) return;
              e.preventDefault();
              setPage((p) => Math.min(totalPages, p + 1));
            }}
          >
            Next
          </Link>
        </div>
      </div>

      <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>
  );
}
