"use client";

import { useCallback, useEffect, useMemo, useRef, 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];

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

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

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

export default function ReleasesExplorer({
  initial,
  initialUrlState,
  initialUrlHasParams,
  initialLists,
}: {
  initial?: Paged<Item>;
  initialUrlState?: {
    q: string;
    page: number;
    year: string;
    sort: "year_desc" | "year_asc" | "title" | "entry_id_desc";
    dLanguageId?: string;
    dMachinetypeId?: string; // keep as string for URL/state consistency
    filetypeId?: string;
    schemetypeId?: string;
    sourcetypeId?: string;
    casetypeId?: string;
    isDemo?: string; // "1" or "true"
  };
  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();

  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 [year, setYear] = useState<string>(initialUrlState?.year ?? "");
  const [sort, setSort] = useState<"year_desc" | "year_asc" | "title" | "entry_id_desc">(initialUrlState?.sort ?? "year_desc");

  // Download-based filters and their option lists
  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")));

  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 ?? []);
  const initialLoad = useRef(true);
  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 updateUrl = useCallback((nextPage = page) => {
    const params = new URLSearchParams();
    if (appliedQ) params.set("q", appliedQ);
    params.set("page", String(nextPage));
    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");
    const qs = params.toString();
    router.replace(qs ? `${pathname}?${qs}` : pathname);
  }, [appliedQ, casetypeId, dLanguageId, dMachinetypeIds, filetypeId, isDemo, page, pathname, router, schemetypeId, sort, sourcetypeId, year]);

  const fetchData = useCallback(async (query: string, p: number) => {
    setLoading(true);
    try {
      const params = new URLSearchParams();
      if (query) params.set("q", query);
      params.set("page", String(p));
      params.set("pageSize", String(pageSize));
      if (year) params.set("year", String(Number(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");
      const res = await fetch(`/api/zxdb/releases/search?${params.toString()}`);
      if (!res.ok) throw new Error(`Failed: ${res.status}`);
      const json: Paged<Item> = await res.json();
      setData(json);
    } catch (e) {
      console.error(e);
      setData({ items: [], page: 1, pageSize, total: 0 });
    } finally {
      setLoading(false);
    }
  }, [casetypeId, dLanguageId, dMachinetypeIds, filetypeId, isDemo, pageSize, schemetypeId, sort, sourcetypeId, year]);

  useEffect(() => {
    if (initial) {
      setData(initial);
      setPage(initial.page);
    }
  }, [initial]);

  const initialState = useMemo(() => ({
    q: initialUrlState?.q ?? "",
    year: initialUrlState?.year ?? "",
    sort: initialUrlState?.sort ?? "year_desc",
    dLanguageId: initialUrlState?.dLanguageId ?? "",
    dMachinetypeId: initialUrlState?.dMachinetypeId ?? "",
    filetypeId: initialUrlState?.filetypeId ?? "",
    schemetypeId: initialUrlState?.schemetypeId ?? "",
    sourcetypeId: initialUrlState?.sourcetypeId ?? "",
    casetypeId: initialUrlState?.casetypeId ?? "",
    isDemo: initialUrlState?.isDemo,
  }), [initialUrlState]);

  useEffect(() => {
    const initialPage = initial?.page ?? 1;
    if (
      initial &&
      page === initialPage &&
      initialState.q === appliedQ &&
      initialState.year === (year ?? "") &&
      sort === initialState.sort &&
      initialState.dLanguageId === dLanguageId &&
      parseMachineIds(initialState.dMachinetypeId).join(",") === dMachinetypeIds.join(",") &&
      initialState.filetypeId === filetypeId &&
      initialState.schemetypeId === schemetypeId &&
      initialState.sourcetypeId === sourcetypeId &&
      initialState.casetypeId === casetypeId &&
      (!!initialState.isDemo === isDemo)
    ) {
      if (initialLoad.current) {
        initialLoad.current = false;
        return;
      }
      updateUrl(page);
      return;
    }
    if (initialLoad.current) {
      initialLoad.current = false;
      if (initial && !initialUrlHasParams) return;
    }
    updateUrl(page);
    fetchData(appliedQ, page);
  }, [appliedQ, casetypeId, dLanguageId, dMachinetypeIds, fetchData, filetypeId, initial, initialState, initialUrlHasParams, isDemo, page, schemetypeId, sort, sourcetypeId, updateUrl, year]);

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

  // Load filter option lists on mount
  useEffect(() => {
    async function loadLists() {
      if (langs.length || machines.length || filetypes.length || schemes.length || sources.length || cases.length) return;
      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 {
        // ignore
      }
    }
    loadLists();
  }, [cases.length, filetypes.length, langs.length, machines.length, schemes.length, sources.length]);

  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 (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 `/zxdb/releases?${params.toString()}`;
  }, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);

  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 (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 `/zxdb/releases?${params.toString()}`;
  }, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);

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

      <ExplorerLayout
        title="Releases"
        subtitle={data ? `${data.total.toLocaleString()} results` : "Loading results..."}
        sidebar={(
          <FilterSidebar>
            <form className="d-flex flex-column gap-2" onSubmit={onSubmit}>
                <div>
                  <label className="form-label small text-secondary">Search title</label>
                  <input
                    type="text"
                    className="form-control"
                    placeholder="Filter by entry title..."
                    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">Year</label>
                  <input
                    type="number"
                    className="form-control"
                    placeholder="Any"
                    value={year}
                    onChange={(e) => { setYear(e.target.value); setPage(1); }}
                  />
                </div>
                <div>
                  <label className="form-label small text-secondary">DL Language</label>
                  <select className="form-select" 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>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="form-label small text-secondary">DL Machine</label>
                  <MultiSelectChips
                    options={machineOptions}
                    selected={dMachinetypeIds}
                    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);
                        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">File type</label>
                  <select className="form-select" 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>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="form-label small text-secondary">Scheme</label>
                  <select className="form-select" 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>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="form-label small text-secondary">Source</label>
                  <select className="form-select" 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>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="form-label small text-secondary">Case</label>
                  <select className="form-select" 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>
                    ))}
                  </select>
                </div>
                <div className="form-check">
                  <input id="demoCheck" className="form-check-input" type="checkbox" checked={isDemo} onChange={(e) => { setIsDemo(e.target.checked); setPage(1); }} />
                  <label className="form-check-label" htmlFor="demoCheck">Demo only</label>
                </div>
                <div>
                  <label className="form-label small text-secondary">Sort</label>
                  <select className="form-select" 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>
                  </select>
                </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 }}>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>
                        <div className="d-flex flex-column gap-1">
                          <Link href={`/zxdb/releases/${it.entryId}/${it.releaseSeq}`} className="link-underline link-underline-opacity-0">
                            {it.entryTitle || `Entry #${it.entryId}`}
                          </Link>
                        </div>
                      </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>
          )}
      </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>
    </div>
  );
}
