"use client";

import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useId, useRef, useState, useTransition, type ReactNode } from "react";
import { Button, Card } from "@/components/ui";
import { inputClass } from "@/components/ui/form";
import { createFolderAction, deleteItemsAction, moveItemsAction, renameItemAction, setFolderOptionsAction } from "@/lib/actions/media";
import { isInside } from "@/lib/media/folders";
import { MediaCard, type MediaRow } from "./media-card";

export interface BrowserFolder { path: string; name: string; files: number; folders: number; noIndex: boolean }
export interface BrowserData { path: string; perPage: number; page: number; total: number; folders: BrowserFolder[]; files: MediaRow[]; allFolders: string[]; currentNoIndex: boolean }
export interface BrowserParams { q: string; sort: "name" | "type" | "size" | "date"; desc: boolean; view: "grid" | "list" }
const SORT_LABEL = { name: "Name", type: "Type", size: "Size", date: "Date Last Modified" } as const;
const selectCls = "rounded-md border border-border bg-card px-3 py-2 text-sm";

function Dialog({ open, onClose, title, children }: { open: boolean; onClose: () => void; title: string; children: ReactNode }) {
  const ref = useRef<HTMLDialogElement>(null);
  const tid = useId();
  useEffect(() => {
    const d = ref.current;
    if (!d) return;
    if (open && !d.open) d.showModal();
    if (!open && d.open) d.close();
  }, [open]);
  return (
    <dialog ref={ref} onClose={onClose} aria-labelledby={tid} className="m-auto w-[min(28rem,92vw)] rounded-xl border border-border bg-card p-5 text-foreground backdrop:bg-black/40">
      {open ? <div className="space-y-4"><h2 id={tid} className="text-base font-semibold">{title}</h2>{children}</div> : null}
    </dialog>
  );
}

/** ShowOff-style media manager: folder cards, breadcrumb, grid or list, sorting, drag-and-drop upload and a selection bar. */
export function MediaBrowser({ siteId, base, data, params, canUpload, canDelete, maxUploadMb }: { siteId: string; base: string; data: BrowserData; params: BrowserParams; canUpload: boolean; canDelete: boolean; maxUploadMb: number }) {
  const router = useRouter();
  const uid = useId();
  const [q, setQ] = useState(params.q);
  const [sel, setSel] = useState<Set<string>>(new Set());
  const [dialog, setDialog] = useState<null | "folder" | "rename" | "move" | "delete" | "options">(null);
  const [text, setText] = useState("");
  const [dest, setDest] = useState("");
  const [noIndex, setNoIndex] = useState(false);
  const [err, setErr] = useState<string | null>(null);
  const [msg, setMsg] = useState<{ ok: boolean; text: string } | null>(null);
  const [drag, setDrag] = useState(false);
  const [uploads, setUploads] = useState<{ name: string; state: "waiting" | "uploading" | "done" | string }[]>([]);
  const [pending, start] = useTransition();
  const fileInput = useRef<HTMLInputElement>(null);

  const href = (over: Partial<{ path: string; q: string; sort: string; desc: boolean; view: string; perPage: number; page: number }>) => {
    const v = { path: data.path, q: params.q, sort: params.sort, desc: params.desc, view: params.view, perPage: data.perPage, page: 1, ...over };
    const sp = new URLSearchParams();
    if (v.path) sp.set("path", v.path);
    if (v.q) sp.set("q", v.q);
    if (v.sort !== "name") sp.set("sort", v.sort);
    if (v.desc) sp.set("desc", "1");
    if (v.view !== "grid") sp.set("view", v.view);
    if (v.perPage !== 50) sp.set("perPage", String(v.perPage));
    if (v.page > 1) sp.set("page", String(v.page));
    const s = sp.toString();
    return s ? `${base}?${s}` : base;
  };

  useEffect(() => {
    if (q === params.q) return;
    const t = setTimeout(() => router.replace(href({ q })), 400);
    return () => clearTimeout(t);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- href only reads current props
  }, [q]);

  const key = (k: "f" | "d", v: string) => `${k}:${v}`;
  const toggle = (k: string) => setSel((s) => { const n = new Set(s); if (n.has(k)) n.delete(k); else n.add(k); return n; });
  const selFiles = data.files.filter((f) => sel.has(key("f", f.id)));
  const selFolders = data.folders.filter((f) => sel.has(key("d", f.path)));
  const count = selFiles.length + selFolders.length;
  const items = { fileIds: selFiles.map((f) => f.id), folders: selFolders.map((f) => f.path) };
  const pages = Math.max(1, Math.ceil(data.total / data.perPage));

  function run(f: () => Promise<{ ok: boolean; message?: string; error?: string; fieldErrors?: Record<string, string> }>, after?: () => void) {
    setErr(null);
    start(async () => {
      const r = await f();
      if (!r.ok) { setErr(r.fieldErrors?.name ?? r.error ?? "Something went wrong"); return; }
      setMsg({ ok: true, text: r.message ?? "Done" }); setDialog(null); setSel(new Set()); after?.(); router.refresh();
    });
  }

  async function upload(list: FileList | File[]) {
    const files = [...list];
    if (!files.length || !canUpload) return;
    setUploads(files.map((f) => ({ name: f.name, state: "waiting" })));
    let done = 0;
    for (let i = 0; i < files.length; i++) {
      setUploads((u) => u.map((x, j) => (j === i ? { ...x, state: "uploading" } : x)));
      const body = new FormData();
      body.set("siteId", siteId); body.set("file", files[i]!); body.set("folder", data.path);
      let state = "done";
      try {
        const res = await fetch("/api/media", { method: "POST", body });
        if (res.ok) done++; else state = ((await res.json().catch(() => ({}))) as { error?: string }).error ?? "Upload failed";
      } catch { state = "Network error"; }
      setUploads((u) => u.map((x, j) => (j === i ? { ...x, state } : x)));
    }
    setMsg({ ok: done === files.length, text: `${done} of ${files.length} uploaded` });
    if (done > 0) router.refresh();
  }

  function download() {
    selFiles.slice(0, 20).forEach((f, i) => setTimeout(() => { const a = document.createElement("a"); a.href = `/api/media/${f.id}?download=1`; a.download = f.filename; document.body.appendChild(a); a.click(); a.remove(); }, i * 250));
  }

  const crumbs = data.path ? data.path.split("/") : [];
  const moveTargets = ["", ...data.allFolders.filter((p) => !selFolders.some((f) => isInside(p, f.path)))];
  const optionFolders = selFolders.length ? selFolders.map((f) => f.path) : data.path ? [data.path] : [];

  return (
    <>
      <div className="mb-6 flex flex-wrap items-end justify-between gap-3">
        <div><h1 className="text-xl font-semibold tracking-tight">Media Manager</h1><p className="mt-1 text-sm text-muted-foreground">Upload and organise images and PDFs in folders.</p></div>
        {canUpload ? <Button variant="secondary" onClick={() => { setText(""); setErr(null); setDialog("folder"); }}>New Folder</Button> : null}
      </div>
      <p role="status" aria-live="polite" className={msg ? (msg.ok ? "mb-3 text-sm text-success" : "mb-3 text-sm text-danger") : "sr-only"}>{msg?.text}</p>

      <div className="mb-4 flex flex-wrap items-end justify-between gap-3">
        <div className="w-full max-w-xs">
          <label htmlFor={`${uid}-q`} className="sr-only">Search files</label>
          <input id={`${uid}-q`} type="search" value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search…" className={`${inputClass} w-full`} />
        </div>
        <div className="flex flex-wrap items-end gap-3">
          <div><label htmlFor={`${uid}-pp`} className="block text-xs font-medium">Records Per Page:</label><select id={`${uid}-pp`} className={selectCls} value={data.perPage} onChange={(e) => router.push(href({ perPage: Number(e.target.value) }))}>{[25, 50, 100, 250].map((n) => <option key={n} value={n}>{n}</option>)}</select></div>
          <div><label htmlFor={`${uid}-so`} className="block text-xs font-medium">Sort By:</label><select id={`${uid}-so`} className={selectCls} value={params.sort} onChange={(e) => router.push(href({ sort: e.target.value }))}>{(Object.keys(SORT_LABEL) as (keyof typeof SORT_LABEL)[]).map((k) => <option key={k} value={k}>{SORT_LABEL[k]}</option>)}</select></div>
          <div><label htmlFor={`${uid}-dir`} className="block text-xs font-medium">Order:</label><select id={`${uid}-dir`} className={selectCls} value={params.desc ? "desc" : "asc"} onChange={(e) => router.push(href({ desc: e.target.value === "desc" }))}><option value="asc">Ascending</option><option value="desc">Descending</option></select></div>
          <div role="group" aria-label="View by" className="flex gap-1">
            <Link href={href({ view: "grid" })} aria-current={params.view === "grid" ? "true" : undefined} aria-label="Grid view" className={`grid size-10 place-items-center rounded-md border border-border text-sm ${params.view === "grid" ? "bg-primary text-white" : "bg-card"}`}>▦</Link>
            <Link href={href({ view: "list" })} aria-current={params.view === "list" ? "true" : undefined} aria-label="List view" className={`grid size-10 place-items-center rounded-md border border-border text-sm ${params.view === "list" ? "bg-primary text-white" : "bg-card"}`}>☰</Link>
          </div>
        </div>
      </div>

      {canUpload ? (
        <div
          onDragOver={(e) => { e.preventDefault(); setDrag(true); }} onDragLeave={() => setDrag(false)}
          onDrop={(e) => { e.preventDefault(); setDrag(false); void upload(e.dataTransfer.files); }}
          className={`mb-4 rounded-xl border-2 border-dashed p-6 text-center ${drag ? "border-primary bg-primary/5" : "border-border bg-card"}`}
        >
          <p className="text-sm">Drag &amp; drop your images or PDFs here to upload them{data.path ? <> to <strong>{data.path}</strong></> : null}</p>
          <p className="my-1 text-xs text-muted-foreground">- or -</p>
          <input ref={fileInput} id={`${uid}-files`} aria-label="Select files to upload" type="file" multiple accept="image/png,image/jpeg,image/gif,image/webp,application/pdf" className="sr-only" onChange={(e) => { if (e.target.files) void upload(e.target.files); e.target.value = ""; }} />
          <Button onClick={() => fileInput.current?.click()}>Select Files</Button>
          <p className="mt-2 text-xs text-muted-foreground">PNG, JPEG, GIF, WebP or PDF, up to {maxUploadMb} MB each.</p>
          {uploads.length ? (
            <ul className="mx-auto mt-3 max-w-md space-y-1 text-left text-xs" aria-live="polite">
              {uploads.map((u, i) => <li key={i} className="flex justify-between gap-3"><span className="truncate">{u.name}</span><span className={u.state === "done" ? "text-success" : u.state === "waiting" || u.state === "uploading" ? "text-muted-foreground" : "text-danger"}>{u.state === "done" ? "Uploaded" : u.state === "uploading" ? "Uploading…" : u.state === "waiting" ? "Waiting" : u.state}</span></li>)}
            </ul>
          ) : null}
        </div>
      ) : null}

      <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
        <div>
          <nav aria-label="Folder path" className="text-sm"><Link href={href({ path: "", page: 1 })} className="text-primary underline">Home</Link>{crumbs.map((c, i) => { const p = crumbs.slice(0, i + 1).join("/"); return <span key={p}> / {i === crumbs.length - 1 ? <span aria-current="page">{c}</span> : <Link href={href({ path: p, page: 1 })} className="text-primary underline">{c}</Link>}</span>; })}{crumbs.length ? " /" : null}</nav>
          <p className="text-xs text-muted-foreground">{params.q ? `Search results across all folders · ` : ""}Total Records: {data.total}</p>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          {pages > 1 ? (
            <nav aria-label="Pagination" className="flex gap-1">
              {Array.from({ length: Math.min(pages, 12) }, (_, i) => i + 1).map((n) => <Link key={n} href={href({ page: n })} aria-current={n === data.page ? "page" : undefined} className={`grid size-9 place-items-center rounded-md text-sm ${n === data.page ? "bg-primary text-white" : "border border-border bg-card"}`}><span className="sr-only">Page </span>{n}</Link>)}
            </nav>
          ) : null}
          {canUpload && (selFolders.length > 0 || data.path) ? <Button variant="secondary" onClick={() => { setNoIndex(selFolders.length ? selFolders.every((f) => f.noIndex) : data.currentNoIndex); setErr(null); setDialog("options"); }}>Folder Options</Button> : null}
        </div>
      </div>

      {data.total === 0 ? (
        <Card className="p-8 text-center text-sm text-muted-foreground">{params.q ? "Nothing matches your search." : "This folder is empty."}</Card>
      ) : (
        <ul className={params.view === "list" ? "space-y-2" : "grid grid-cols-2 gap-4 md:grid-cols-3 xl:grid-cols-5"}>
          {data.folders.map((f) => {
            const on = sel.has(key("d", f.path));
            return (
              <li key={f.path} className={params.view === "list" ? `flex items-center gap-3 rounded-lg border bg-card px-3 py-2 ${on ? "border-primary" : "border-border"}` : `relative rounded-xl border bg-card ${on ? "border-primary ring-2 ring-primary/30" : "border-border"}`}>
                <input type="checkbox" checked={on} onChange={() => toggle(key("d", f.path))} aria-label={`Select folder ${f.name}`} className={params.view === "list" ? "size-4" : "absolute right-2 top-2 size-4"} />
                <Link href={href({ path: f.path, page: 1 })} className={params.view === "list" ? "flex min-w-0 flex-1 items-center gap-3" : "block px-3 py-6 text-center"}>
                  <span aria-hidden className={params.view === "list" ? "text-xl" : "block text-4xl"}>📁</span>
                  <span className={params.view === "list" ? "min-w-0 flex-1" : "mt-2 block"}><span className="block truncate text-sm font-medium">{f.name}</span><span className="block text-xs text-muted-foreground">Contains {f.files} {f.files === 1 ? "file" : "files"} / {f.folders} {f.folders === 1 ? "folder" : "folders"}{f.noIndex ? " · noindex" : ""}</span></span>
                </Link>
              </li>
            );
          })}
          {data.files.map((f) => <MediaCard key={f.id} row={f} view={params.view} canEdit={canUpload} canDelete={canDelete} selected={sel.has(key("f", f.id))} onToggle={() => toggle(key("f", f.id))} />)}
        </ul>
      )}

      {count > 0 ? (
        <div role="toolbar" aria-label="Selected items" className="fixed bottom-6 left-1/2 z-30 flex -translate-x-1/2 flex-wrap items-center gap-2 rounded-full border border-border bg-card px-4 py-2 shadow-lg">
          <span className="text-xs text-muted-foreground">{count} item(s) selected</span>
          {canUpload && count === 1 ? <Button variant="secondary" onClick={() => { setText(selFiles[0] ? selFiles[0].filename.replace(/\.[^.]+$/, "") : selFolders[0]!.name); setErr(null); setDialog("rename"); }}>Rename</Button> : null}
          {selFiles.length > 0 ? <Button variant="secondary" onClick={download}>Download</Button> : null}
          {canDelete ? <Button variant="danger" onClick={() => { setErr(null); setDialog("delete"); }}>Delete</Button> : null}
          {canUpload ? <Button variant="secondary" onClick={() => { setDest(data.path); setErr(null); setDialog("move"); }}>Move</Button> : null}
          <Button variant="ghost" onClick={() => setSel(new Set())}>Cancel</Button>
        </div>
      ) : null}

      <Dialog open={dialog === "folder"} onClose={() => setDialog(null)} title="New Folder">
        <form className="space-y-3" onSubmit={(e) => { e.preventDefault(); run(() => createFolderAction(siteId, data.path, text)); }}>
          <label htmlFor={`${uid}-nf`} className="block text-xs font-medium">Folder name{data.path ? ` (inside ${data.path})` : ""}</label>
          <input id={`${uid}-nf`} className={`${inputClass} w-full`} maxLength={40} value={text} onChange={(e) => setText(e.target.value)} aria-invalid={err ? true : undefined} aria-describedby={err ? `${uid}-nf-e` : undefined} autoFocus />
          {err ? <p id={`${uid}-nf-e`} role="alert" className="text-xs text-danger">{err}</p> : null}
          <div className="flex gap-2"><Button type="submit" disabled={pending}>Create</Button><Button variant="ghost" onClick={() => setDialog(null)}>Cancel</Button></div>
        </form>
      </Dialog>
      <Dialog open={dialog === "rename"} onClose={() => setDialog(null)} title="Rename">
        <form className="space-y-3" onSubmit={(e) => { e.preventDefault(); run(() => renameItemAction(siteId, selFiles[0] ? { kind: "file", id: selFiles[0].id, name: text } : { kind: "folder", path: selFolders[0]!.path, name: text })); }}>
          <label htmlFor={`${uid}-rn`} className="block text-xs font-medium">New name</label>
          <input id={`${uid}-rn`} className={`${inputClass} w-full`} maxLength={120} value={text} onChange={(e) => setText(e.target.value)} aria-invalid={err ? true : undefined} aria-describedby={err ? `${uid}-rn-e` : undefined} autoFocus />
          {err ? <p id={`${uid}-rn-e`} role="alert" className="text-xs text-danger">{err}</p> : null}
          <div className="flex gap-2"><Button type="submit" disabled={pending}>Rename</Button><Button variant="ghost" onClick={() => setDialog(null)}>Cancel</Button></div>
        </form>
      </Dialog>
      <Dialog open={dialog === "move"} onClose={() => setDialog(null)} title={`Move ${count} item(s)`}>
        <form className="space-y-3" onSubmit={(e) => { e.preventDefault(); run(() => moveItemsAction(siteId, items, dest)); }}>
          <label htmlFor={`${uid}-mv`} className="block text-xs font-medium">Move to</label>
          <select id={`${uid}-mv`} className={`${selectCls} w-full`} value={dest} onChange={(e) => setDest(e.target.value)}>{moveTargets.map((p) => <option key={p || "home"} value={p}>{p ? `Home / ${p.replaceAll("/", " / ")}` : "Home"}</option>)}</select>
          {err ? <p role="alert" className="text-xs text-danger">{err}</p> : null}
          <div className="flex gap-2"><Button type="submit" disabled={pending}>Move</Button><Button variant="ghost" onClick={() => setDialog(null)}>Cancel</Button></div>
        </form>
      </Dialog>
      <Dialog open={dialog === "delete"} onClose={() => setDialog(null)} title={`Delete ${count} item(s)?`}>
        <p className="text-sm text-muted-foreground">Files that are used on the website cannot be deleted, and folders must be empty. This cannot be undone.</p>
        {err ? <p role="alert" className="text-sm text-danger">{err}</p> : null}
        <div className="flex gap-2"><Button variant="danger" disabled={pending} onClick={() => run(() => deleteItemsAction(siteId, items))}>Confirm delete</Button><Button variant="ghost" onClick={() => setDialog(null)}>Cancel</Button></div>
      </Dialog>
      <Dialog open={dialog === "options"} onClose={() => setDialog(null)} title="Folder Options">
        <form className="space-y-3" onSubmit={(e) => { e.preventDefault(); run(() => setFolderOptionsAction(siteId, { folders: optionFolders, noIndex })); }}>
          <fieldset>
            <legend className="text-xs font-semibold">X-Robots-Tag</legend>
            <p className="text-xs text-muted-foreground">Tells search engines to exclude these files from search results.</p>
            <label className="mt-2 flex items-center gap-2 text-sm"><input type="checkbox" checked={noIndex} onChange={(e) => setNoIndex(e.target.checked)} /> noindex</label>
            <p className="mt-1 text-xs text-muted-foreground">Applies to non-image files in {selFolders.length ? "the selected folders" : `“${data.path}”`} and the folders inside them.</p>
          </fieldset>
          {err ? <p role="alert" className="text-xs text-danger">{err}</p> : null}
          <div className="flex gap-2"><Button type="submit" disabled={pending}>Save</Button><Button variant="ghost" onClick={() => setDialog(null)}>Cancel</Button></div>
        </form>
      </Dialog>
    </>
  );
}
