"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import { Form, InputGroup, Button, Table, Alert } 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 } from "@/types/zxdb";
import { preferredMachineIds, parseMachineIds } from "@/utils/params";

const PAGE_SIZE = 20;

type Item = {
  entryId: number;
  releaseSeq: number;
  entryTitle: string;
  year: number | null;
  magrefCount: number;
};

export default function ReleasesExplorer({
  initial,
  initialUrlState,
  initialLists,
}: {
  initial?: PagedResult<Item>;
  initialUrlState?: {
    q: string;
    page: number;
    year: string;
    sort: "year_desc" | "year_asc" | "title" | "entry_id_desc";
    dLanguageId?: string;
    dMachinetypeId?: string;
    filetypeId?: string;
    schemetypeId?: string;
    sourcetypeId?: string;
    casetypeId?: string;
    isDemo?: string;
  };
  initialUrlHasParams?: boolean;
  initialLists?: {
    languages: { id: string; name: string }[];
    machinetypes: { id: number; name: string }[];
    filetypes: { id: number; name: string }[];
    schemetypes: { id: string; name: string }[];
    sourcetypes: { id: string; name: string }[];
    casetypes: { id: string; name: string }[];
  };
}) {
  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 [year, setYear] = useState<string>(initialUrlState?.year ?? "");
  const [sort, setSort] = useState<"year_desc" | "year_asc" | "title" | "entry_id_desc">(initialUrlState?.sort ?? "year_desc");
  const [dLanguageId, setDLanguageId] = useState<string>(initialUrlState?.dLanguageId ?? "");
  const [dMachinetypeIds, setDMachinetypeIds] = useState<number[]>(parseMachineIds(initialUrlState?.dMachinetypeId));
  const [filetypeId, setFiletypeId] = useState<string>(initialUrlState?.filetypeId ?? "");
  const [schemetypeId, setSchemetypeId] = useState<string>(initialUrlState?.schemetypeId ?? "");
  const [sourcetypeId, setSourcetypeId] = useState<string>(initialUrlState?.sourcetypeId ?? "");
  const [casetypeId, setCasetypeId] = useState<string>(initialUrlState?.casetypeId ?? "");
  const [isDemo, setIsDemo] = useState<boolean>(!!(initialUrlState?.isDemo && (initialUrlState.isDemo === "1" || initialUrlState.isDemo === "true")));

  // -- Filter lists --
  const [langs, setLangs] = useState<{ id: string; name: string }[]>(initialLists?.languages ?? []);
  const [machines, setMachines] = useState<{ id: number; name: string }[]>(initialLists?.machinetypes ?? []);
  const [filetypes, setFiletypes] = useState<{ id: number; name: string }[]>(initialLists?.filetypes ?? []);
  const [schemes, setSchemes] = useState<{ id: string; name: string }[]>(initialLists?.schemetypes ?? []);
  const [sources, setSources] = useState<{ id: string; name: string }[]>(initialLists?.sourcetypes ?? []);
  const [cases, setCases] = useState<{ id: string; name: string }[]>(initialLists?.casetypes ?? []);

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

  const isFirstRender = useRef(true);

  // Debounce timer for year input changes
  const yearDebounce = useRef<ReturnType<typeof setTimeout>>(undefined);

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

  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 = dMachinetypeIds.join(",") !== preferredMachineIds.join(",") ||
    dMachinetypeIds.length !== machines.length;

  // -- URL helpers --
  const buildParams = useCallback(
    (p: number) => {
      const params = new URLSearchParams();
      if (appliedQ) params.set("q", appliedQ);
      params.set("page", String(p));
      if (year) params.set("year", year);
      if (sort) params.set("sort", sort);
      if (dLanguageId) params.set("dLanguageId", dLanguageId);
      if (dMachinetypeIds.length > 0) params.set("dMachinetypeId", dMachinetypeIds.join(","));
      if (filetypeId) params.set("filetypeId", filetypeId);
      if (schemetypeId) params.set("schemetypeId", schemetypeId);
      if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
      if (casetypeId) params.set("casetypeId", casetypeId);
      if (isDemo) params.set("isDemo", "1");
      return params;
    },
    [appliedQ, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo],
  );

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

  // -- 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));
    doFetch(params);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [page, appliedQ, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);

  // 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 (langs.length || machines.length || filetypes.length || schemes.length || sources.length || cases.length) return;
    async function loadLists() {
      try {
        const [l, m, ft, sc, so, ca] = await Promise.all([
          fetch("/api/zxdb/languages", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/machinetypes", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/filetypes", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/schemetypes", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/sourcetypes", { cache: "force-cache" }).then((r) => r.json()),
          fetch("/api/zxdb/casetypes", { cache: "force-cache" }).then((r) => r.json()),
        ]);
        setLangs(l.items ?? []);
        setMachines(m.items ?? []);
        setFiletypes(ft.items ?? []);
        setSchemes(sc.items ?? []);
        setSources(so.items ?? []);
        setCases(ca.items ?? []);
      } catch { /* filter lists are non-critical */ }
    }
    loadLists();
  }, [cases.length, filetypes.length, langs.length, machines.length, schemes.length, sources.length]);

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

  function onYearChange(value: string) {
    setYear(value);
    clearTimeout(yearDebounce.current);
    yearDebounce.current = setTimeout(() => setPage(1), 400);
  }

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

  function resetFilters() {
    setQ("");
    setAppliedQ("");
    setYear("");
    setSort("year_desc");
    setDLanguageId("");
    setDMachinetypeIds(preferredMachineIds.slice());
    setFiletypeId("");
    setSchemetypeId("");
    setSourcetypeId("");
    setCasetypeId("");
    setIsDemo(false);
    setPage(1);
  }

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

      <ExplorerLayout
        title="Releases"
        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="Year" badge={year || undefined}>
                <Form.Control
                  type="number"
                  size="sm"
                  placeholder="Any"
                  value={year}
                  onChange={(e) => onYearChange(e.target.value)}
                />
              </FilterSection>

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

              <FilterSection
                label="Machine"
                badge={`${dMachinetypeIds.length} selected`}
                defaultOpen={false}
              >
                <MultiSelectChips
                  options={machineOptions}
                  selected={dMachinetypeIds}
                  collapsible
                  onToggle={(id) => {
                    setDMachinetypeIds((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={() => { setDMachinetypeIds(machineOptions.map((m) => m.id)); setPage(1); }}
                    disabled={dMachinetypeIds.length === machineOptions.length}
                  >
                    All
                  </Button>
                  <Button
                    variant="outline-secondary"
                    size="sm"
                    onClick={() => { setDMachinetypeIds(preferredMachineIds.slice()); setPage(1); }}
                    disabled={dMachinetypeIds.join(",") === preferredMachineIds.join(",")}
                  >
                    Default
                  </Button>
                </div>
              </FilterSection>

              <FilterSection label="Download filters" defaultOpen={false} badge={
                [filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo ? "demo" : ""].filter(Boolean).length
                  ? `${[filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo ? "demo" : ""].filter(Boolean).length} active`
                  : undefined
              }>
                <div className="d-flex flex-column gap-2">
                  <Form.Select size="sm" value={filetypeId} onChange={(e) => { setFiletypeId(e.target.value); setPage(1); }}>
                    <option value="">All file types</option>
                    {filetypes.map((ft) => (
                      <option key={ft.id} value={ft.id}>{ft.name}</option>
                    ))}
                  </Form.Select>
                  <Form.Select size="sm" value={schemetypeId} onChange={(e) => { setSchemetypeId(e.target.value); setPage(1); }}>
                    <option value="">All schemes</option>
                    {schemes.map((s) => (
                      <option key={s.id} value={s.id}>{s.name}</option>
                    ))}
                  </Form.Select>
                  <Form.Select size="sm" value={sourcetypeId} onChange={(e) => { setSourcetypeId(e.target.value); setPage(1); }}>
                    <option value="">All sources</option>
                    {sources.map((s) => (
                      <option key={s.id} value={s.id}>{s.name}</option>
                    ))}
                  </Form.Select>
                  <Form.Select size="sm" value={casetypeId} onChange={(e) => { setCasetypeId(e.target.value); setPage(1); }}>
                    <option value="">All cases</option>
                    {cases.map((c) => (
                      <option key={c.id} value={c.id}>{c.name}</option>
                    ))}
                  </Form.Select>
                  <Form.Check
                    id="demoCheck"
                    label="Demo only"
                    checked={isDemo}
                    onChange={(e) => { setIsDemo(e.target.checked); setPage(1); }}
                  />
                </div>
              </FilterSection>

              <FilterSection label="Sort">
                <Form.Select size="sm" value={sort} onChange={(e) => { setSort(e.target.value as typeof sort); setPage(1); }}>
                  <option value="year_desc">Newest</option>
                  <option value="year_asc">Oldest</option>
                  <option value="title">Title</option>
                  <option value="entry_id_desc">Entry ID</option>
                </Form.Select>
              </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>
                    {dMachinetypeIds
                      .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 }}>Entry ID</th>
                    <th>Title</th>
                    <th style={{ width: 140 }}>Release #</th>
                    <th style={{ width: 110 }}>Places</th>
                    <th style={{ width: 100 }}>Year</th>
                  </tr>
                </thead>
                <tbody>
                  {data.items.map((it) => (
                    <tr key={`${it.entryId}-${it.releaseSeq}`}>
                      <td>
                        <EntryLink id={it.entryId} />
                      </td>
                      <td>
                        <Link href={`/zxdb/releases/${it.entryId}/${it.releaseSeq}`} className="link-underline link-underline-opacity-0">
                          {it.entryTitle || `Entry #${it.entryId}`}
                        </Link>
                      </td>
                      <td>
                        <Link href={`/zxdb/releases/${it.entryId}/${it.releaseSeq}`}>
                          #{it.releaseSeq}
                        </Link>
                      </td>
                      <td>
                        {it.magrefCount > 0 ? (
                          <span className="badge text-bg-secondary">{it.magrefCount}</span>
                        ) : (
                          <span className="text-secondary">-</span>
                        )}
                      </td>
                      <td>{it.year ?? <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}
      />
    </div>
  );
}
