"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { CorpusSummary, DocumentMeta, GroundingSummary, corpusSummary, fetchGroundingSummary, fetchSuggestions, ingestFile, ingestUrl } from "@/lib/api";
import { displaySource, timeAgo } from "@/lib/format";
import { CorpusManageModal } from "@/components/CorpusManageModal";
import { IngestSources } from "@/components/IngestSources";

const ACCEPT = ".pdf,.docx,.html,.htm,.md,.txt,.wav,.mp3,.m4a";
const TYPES = ["PDF", "DOCX", "HTML", "Markdown", "TXT", "WAV", "MP3", "M4A"];

interface Recent {
  name: string;
  docs: number;
  chunks: number;
  skipped: number;
  ok: boolean;
}

export function IngestPanel({ onAsk, onDeleted }: { onAsk?: (q: string) => void; onDeleted?: () => void }) {
  const [status, setStatus] = useState<{ kind: "ok" | "err" | "info"; msg: string } | null>(null);
  const [busy, setBusy] = useState(false);
  const [drag, setDrag] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const [summary, setSummary] = useState<CorpusSummary | null>(null);
  const [groundingSummary, setGroundingSummary] = useState<GroundingSummary | null>(null);
  const [showReindexInfo, setShowReindexInfo] = useState(false);
  const [samples, setSamples] = useState<string[]>([]);
  const [recent, setRecent] = useState<Recent[]>([]);
  const [showManage, setShowManage] = useState(false);
  const [manageAction, setManageAction] = useState<"clear_all" | "delete_last" | undefined>(undefined);
  const [urlOpen, setUrlOpen] = useState(false);
  const [urlValue, setUrlValue] = useState("");
  const urlInputRef = useRef<HTMLInputElement>(null);

  const refresh = useCallback(async () => {
    try {
      const [s, sug] = await Promise.all([corpusSummary(), fetchSuggestions(3)]);
      setSummary(s);
      setSamples(sug.suggestions);
    } catch {
      /* backend may be warming up */
    }
    try {
      const gs = await fetchGroundingSummary();
      setGroundingSummary(gs);
    } catch {
      /* visual grounding endpoint unavailable */
    }
  }, []);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  const upload = useCallback(
    async (file: File) => {
      setBusy(true);
      setStatus({ kind: "info", msg: `Uploading ${file.name} — parsing → chunking → indexing…` });
      try {
        const r = await ingestFile(file);
        setStatus({
          kind: "ok",
          msg: `✓ Indexed ${r.documents} document(s), ${r.chunks} chunk(s)${
            r.skipped ? `, ${r.skipped} skipped` : ""
          }.`,
        });
        setRecent((prev) =>
          [{ name: file.name, docs: r.documents, chunks: r.chunks, skipped: r.skipped || 0, ok: true }, ...prev].slice(0, 5),
        );
        await refresh();
      } catch (err) {
        setStatus({ kind: "err", msg: `✗ ${(err as Error).message}` });
        setRecent((prev) => [{ name: file.name, docs: 0, chunks: 0, skipped: 0, ok: false }, ...prev].slice(0, 5));
      } finally {
        setBusy(false);
      }
    },
    [refresh],
  );

  function onDrop(e: React.DragEvent) {
    e.preventDefault();
    setDrag(false);
    const file = e.dataTransfer.files?.[0];
    if (file && !busy) void upload(file);
  }

  const submitUrl = useCallback(async () => {
    const url = urlValue.trim();
    if (!url || busy) return;
    setBusy(true);
    setStatus({ kind: "info", msg: `Fetching ${url} — extracting → chunking → indexing…` });
    try {
      const r = await ingestUrl(url);
      setStatus({
        kind: "ok",
        msg: r.unchanged
          ? `✓ Already indexed (unchanged): ${r.title || r.url}`
          : `✓ Indexed “${r.title || r.url}” — ${r.chunks} chunk(s).`,
      });
      setRecent((prev) => [{ name: r.title || url, docs: r.documents, chunks: r.chunks, skipped: r.skipped || 0, ok: true }, ...prev].slice(0, 5));
      setUrlValue("");
      setUrlOpen(false);
      await refresh();
    } catch (err) {
      setStatus({ kind: "err", msg: `✗ ${(err as Error).message}` });
    } finally {
      setBusy(false);
    }
  }, [urlValue, busy, refresh]);

  const openUrl = useCallback(() => {
    setUrlOpen(true);
    setTimeout(() => urlInputRef.current?.focus(), 50);
  }, []);

  const failed = summary?.failed_files || [];
  const docs: DocumentMeta[] = (summary?.document_titles || []).map((title, i) => ({
    doc_id: `doc_${i}`,
    source: title,
    title: displaySource(title),
    source_type: "unknown",
  }));

  return (
    <div className="space-y-4">
      <IngestSources onUpload={() => fileInputRef.current?.click()} onWebUrl={openUrl} />

      {/* Web URL input */}
      {urlOpen && (
        <div className="card-inset space-y-2">
          <label className="text-xs font-semibold text-fg2">Add a web page</label>
          <div className="flex gap-2">
            <input
              ref={urlInputRef}
              type="url"
              inputMode="url"
              value={urlValue}
              onChange={(e) => setUrlValue(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === "Enter") void submitUrl();
                if (e.key === "Escape") setUrlOpen(false);
              }}
              placeholder="https://example.com/article"
              disabled={busy}
              className="min-w-0 flex-1 rounded-lg border border-edge bg-panel2 px-3 py-1.5 text-sm text-fg placeholder:text-fg3 focus:border-brand/50 focus:outline-none disabled:opacity-60"
            />
            <button
              type="button"
              onClick={() => void submitUrl()}
              disabled={busy || !urlValue.trim()}
              className="btn-cta shrink-0 px-3 py-1.5 text-xs disabled:opacity-50"
            >
              {busy ? "Fetching…" : "Fetch"}
            </button>
            <button
              type="button"
              onClick={() => setUrlOpen(false)}
              className="btn-outline shrink-0 px-2.5 py-1.5 text-xs text-fg3"
              aria-label="Cancel"
            >
              ✕
            </button>
          </div>
          <p className="text-[10px] text-fg3">
            The page&apos;s main content is extracted and indexed with its URL as the citation source.
            Private/internal hosts are blocked.
          </p>
        </div>
      )}
      {/* corpus stats */}
      {summary && (
        <div className="card-inset">
          <div className="mb-2 flex items-center justify-between">
            <h3 className="text-sm font-semibold text-fg">Corpus</h3>
            <div className="flex items-center gap-2">
              <button onClick={() => void refresh()} className="text-[11px] text-fg3 hover:text-brand">
                ↻ Refresh
              </button>
              {summary.indexed && (
                <button
                  onClick={() => { setManageAction(undefined); setShowManage(true); }}
                  className="rounded-md border border-bad/40 px-2 py-0.5 text-[11px] text-bad transition hover:bg-bad/10"
                >
                  Manage
                </button>
              )}
            </div>
          </div>
          <div className="grid grid-cols-3 gap-1.5 text-center">
            <div className="stat">
              <div className="stat-value">{summary.indexed_document_count}</div>
              <div className="stat-label">docs</div>
            </div>
            <div className="stat">
              <div className="stat-value">{summary.vector_count}</div>
              <div className="stat-label">vectors</div>
            </div>
            <div className="stat">
              <div className="stat-value">{summary.entity_count}</div>
              <div className="stat-label">entities</div>
            </div>
          </div>
          <p className="mt-2 text-[11px] text-fg3">Last indexed: {timeAgo(summary.last_indexed)}</p>

          {/* indexed document list */}
          {summary.document_titles.length > 0 && (
            <div className="mt-3">
              <p className="stat-label mb-1">Indexed documents</p>
              <ul className="space-y-1">
                {summary.document_titles.slice(0, 8).map((title) => (
                  <li key={title} className="flex items-center justify-between text-xs">
                    <span className="truncate text-fg2" title={title}>
                      📄 {displaySource(title)}
                    </span>
                    <span className="ml-2 shrink-0 text-fg3">{(summary.source_types as Record<string,number>)?.pdf ? "pdf" : ""}</span>
                  </li>
                ))}
                {summary.document_titles.length > 8 && (
                  <li className="text-[11px] text-fg3">
                    … {summary.document_titles.length - 8} more
                  </li>
                )}
              </ul>
            </div>
          )}

          {/* quick delete actions */}
          {summary.indexed && (
            <div className="mt-3 flex flex-wrap gap-2">
              <button
                onClick={() => { setManageAction("delete_last"); setShowManage(true); }}
                className="flex-1 rounded-lg border border-bad/30 py-1.5 text-xs text-bad transition hover:bg-bad/10"
              >
                Delete last document
              </button>
              <button
                onClick={() => { setManageAction("clear_all"); setShowManage(true); }}
                className="flex-1 rounded-lg border border-bad/30 py-1.5 text-xs text-bad transition hover:bg-bad/10"
              >
                Clear all corpus
              </button>
            </div>
          )}

          {!summary.indexed && (
            <p className="mt-3 rounded-lg border border-edge bg-panel2 p-2.5 text-xs text-fg3">
              No documents indexed yet. Upload a file to begin.
            </p>
          )}
        </div>
      )}

      {/* visual grounding status */}
      {groundingSummary && (
        <div className="card-inset space-y-2">
          <div className="flex items-center justify-between">
            <h3 className="text-sm font-semibold text-fg">Visual Grounding</h3>
            <span className={`pill ${groundingSummary.enabled ? "pill-ok" : "pill-neutral"}`}>
              <span
                className={`inline-block h-1.5 w-1.5 rounded-full ${groundingSummary.enabled ? "bg-ok" : "bg-edge2"}`}
              />
              {groundingSummary.enabled ? "enabled" : "disabled"}
            </span>
          </div>
          {groundingSummary.total_docs > 0 && (
            <div className="grid grid-cols-3 gap-1.5 text-center">
              <div className="stat">
                <div className="stat-value">{groundingSummary.total_docs}</div>
                <div className="stat-label">total</div>
              </div>
              <div className="stat">
                <div className="stat-value text-ok">{groundingSummary.grounded_docs}</div>
                <div className="stat-label">grounded</div>
              </div>
              <div className="stat">
                <div className={`stat-value ${groundingSummary.needs_reindex > 0 ? "text-warn" : "text-ok"}`}>
                  {groundingSummary.needs_reindex}
                </div>
                <div className="stat-label">need reindex</div>
              </div>
            </div>
          )}
          {groundingSummary.needs_reindex > 0 && (
            <div>
              <button
                onClick={() => setShowReindexInfo((v) => !v)}
                className="w-full rounded-lg border border-warn/40 py-1.5 text-xs text-warn transition hover:bg-warn/10"
              >
                {showReindexInfo ? "Hide" : "Reindex for visual grounding ↓"}
              </button>
              {showReindexInfo && (
                <div className="mt-2 rounded-lg border border-edge bg-panel2 p-2.5 text-[11px] text-fg3 space-y-1">
                  <p className="font-semibold text-fg2">How to enable visual grounding</p>
                  <p>
                    {groundingSummary.needs_reindex} document
                    {groundingSummary.needs_reindex !== 1 ? "s were" : " was"} indexed before visual
                    grounding metadata was available.
                  </p>
                  <p>
                    Re-upload those documents above — the indexer will automatically attach bounding-box
                    and page metadata so citations show highlighted source regions.
                  </p>
                </div>
              )}
            </div>
          )}
          {groundingSummary.total_docs === 0 && (
            <p className="text-[11px] text-fg3">
              Upload a PDF to enable source highlights with bounding-box grounding.
            </p>
          )}
        </div>
      )}

      {/* drag-and-drop zone */}
      <label
        onDragOver={(e) => {
          e.preventDefault();
          setDrag(true);
        }}
        onDragLeave={() => setDrag(false)}
        onDrop={onDrop}
        className={`flex cursor-pointer flex-col items-center justify-center gap-1 rounded-xl border-2 border-dashed p-6 text-center transition ${
          drag ? "border-brand bg-brand/10" : "border-edge2 hover:border-brand/50 hover:bg-panel2"
        }`}
      >
        <span className="text-2xl" aria-hidden>
          {busy ? "⏳" : "⬆️"}
        </span>
        <span className="text-sm font-medium text-fg">
          {busy ? "Indexing…" : "Drag a file here, or click to upload"}
        </span>
        <span className="text-[11px] text-fg3">Chunked with source spans → hybrid index + graph</span>
        <input
          ref={fileInputRef}
          type="file"
          className="hidden"
          disabled={busy}
          accept={ACCEPT}
          onChange={(e) => {
            const f = e.target.files?.[0];
            if (f) void upload(f);
          }}
        />
      </label>

      {/* indexing progress */}
      {busy && (
        <div className="h-1 w-full overflow-hidden rounded-full bg-edge">
          <div className="h-1 w-1/3 animate-pulse rounded-full bg-brand" />
        </div>
      )}

      {/* supported types */}
      <div>
        <p className="stat-label mb-1">Supported file types</p>
        <div className="flex flex-wrap gap-1">
          {TYPES.map((t) => (
            <span key={t} className="tag">
              {t}
            </span>
          ))}
        </div>
      </div>

      {status && (
        <p
          className={`text-sm ${
            status.kind === "ok" ? "text-ok" : status.kind === "err" ? "text-bad" : "text-fg3"
          }`}
        >
          {status.msg}
        </p>
      )}

      {/* recent uploads */}
      {recent.length > 0 && (
        <div className="card-inset">
          <p className="stat-label mb-1.5">Recent uploads</p>
          <ul className="space-y-1 text-xs">
            {recent.map((r, i) => (
              <li key={i} className="flex items-center justify-between gap-2">
                <span className="truncate text-fg2" title={r.name}>
                  {r.ok ? "✓" : "✕"} {displaySource(r.name)}
                </span>
                <span className={r.ok ? "text-fg3" : "text-bad"}>
                  {r.ok ? `${r.docs}d · ${r.chunks}c${r.skipped ? ` · ${r.skipped} skip` : ""}` : "failed"}
                </span>
              </li>
            ))}
          </ul>
        </div>
      )}

      {/* failed ingestion files (from corpus) */}
      {failed.length > 0 && (
        <div className="evidence-weak rounded-xl border p-2.5">
          <p className="stat-label mb-1 text-warn">Failed to ingest ({failed.length})</p>
          <ul className="space-y-0.5 text-xs text-fg2">
            {failed.slice(0, 5).map((f) => (
              <li key={f} className="truncate" title={f}>
                ⚠ {displaySource(f)}
              </li>
            ))}
          </ul>
        </div>
      )}

      {/* sample questions the current corpus can actually answer */}
      {samples.length > 0 && (
        <div>
          <p className="stat-label mb-1">Try these on the current corpus</p>
          <div className="flex flex-wrap gap-1.5">
            {samples.map((q) => (
              <button
                key={q}
                onClick={() => onAsk?.(q)}
                className="rounded-full border border-edge bg-panel2 px-3 py-1 text-xs text-fg2 transition hover:border-brand hover:text-brand"
              >
                {q}
              </button>
            ))}
          </div>
        </div>
      )}

      {/* Corpus manage modal */}
      {showManage && (
        <CorpusManageModal
          onClose={() => { setShowManage(false); setManageAction(undefined); }}
          onDeleted={() => {
            void refresh();
            setShowManage(false);
            setManageAction(undefined);
            onDeleted?.();
          }}
          initialAction={manageAction}
        />
      )}
    </div>
  );
}
