"use client";
import { useState, useCallback } from "react";
import { useDropzone } from "react-dropzone";
import useSWR from "swr";
import { api } from "@/lib/api";
import GlassCard from "@/components/GlassCard";
import PageExplainer from "@/components/PageExplainer";
import { formatDistanceToNow, parseISO } from "date-fns";
import { clsx } from "clsx";

function StatusDot({ status }: { status: string }) {
  const map: Record<string, string> = {
    done:       "bg-emerald-400",
    processing: "bg-blue-400 animate-pulse",
    queued:     "bg-amber-400 animate-pulse",
    error:      "bg-red-400",
  };
  return <span className={clsx("w-2 h-2 rounded-full shrink-0", map[status] ?? "bg-white/20")} />;
}

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}

export default function DocIngestPage() {
  const [uploading, setUploading] = useState<string[]>([]);
  const [searchQuery, setSearchQuery] = useState("");
  const [searchResults, setSearchResults] = useState<any[]>([]);
  const [searching, setSearching] = useState(false);

  const { data, mutate } = useSWR("docingest/documents", api.docingest.documents, { refreshInterval: 5000 });
  const docs = (data as any)?.documents ?? [];

  const onDrop = useCallback(async (files: File[]) => {
    for (const file of files) {
      setUploading(prev => [...prev, file.name]);
      const formData = new FormData();
      formData.append("file", file);
      const token = process.env.NEXT_PUBLIC_DASHBOARD_TOKEN || "";
      try {
        await fetch("/api/docingest/upload", {
          method: "POST",
          headers: token ? { Authorization: `Bearer ${token}` } : {},
          body: formData,
        });
        mutate();
      } catch (e) {
        console.error("Upload failed", e);
      } finally {
        setUploading(prev => prev.filter(n => n !== file.name));
      }
    }
  }, [mutate]);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: {
      "application/pdf": [".pdf"],
      "text/plain": [".txt"],
      "text/markdown": [".md"],
      "application/octet-stream": [".pdf"],  // Windows/Chrome sometimes reports PDFs as octet-stream
    },
  });

  const search = async () => {
    if (!searchQuery.trim()) return;
    setSearching(true);
    const res = await api.docingest.search(searchQuery) as any;
    setSearchResults(res?.results ?? []);
    setSearching(false);
  };

  const deleteDoc = async (id: number) => {
    await api.docingest.delete(id);
    mutate();
  };

  return (
    <div className="space-y-6 animate-fade-in">
      <div>
        <h1 className="text-2xl font-semibold text-white">DocIngest</h1>
        <p className="text-sm text-white/40 mt-0.5">Upload documents to build the knowledge base</p>
      </div>

      <PageExplainer sections={[
        { heading: "How ingestion works", rows: [
          { label: "Upload", meaning: "Drop a PDF, .txt, or .md file. It is chunked at ~800 tokens/chunk and embedded via Ollama nomic-embed-text (768-dim vectors) stored in public.corpus_chunks." },
          { label: "Queued (amber)", color: "bg-amber-400", meaning: "File received, waiting for the ingestion worker to process it." },
          { label: "Processing (blue)", color: "bg-blue-400", meaning: "Actively chunking and embedding. May take 30s–2min depending on file size." },
          { label: "Done (green)", color: "bg-emerald-400", meaning: "All chunks embedded and stored. File is searchable." },
          { label: "Error (red)", color: "bg-red-400", meaning: "Ingestion failed. Hover the row to see the error. Common causes: unsupported PDF encoding, Ollama not running." },
        ]},
        { heading: "Search", rows: [
          { label: "Similarity search", meaning: "Query is embedded and compared against all stored chunk vectors using cosine similarity. Returns the top-K most similar chunks." },
          { label: "Match %", meaning: "Cosine similarity as a percentage. 80%+ = strong match. Below 60% = likely unrelated." },
          { label: "Use case", meaning: "The agent can query this knowledge base when answering questions. Upload reference docs, runbooks, or corpus files to improve answers." },
        ]},
        { heading: "Limits & notes", rows: [
          { label: "Supported formats", meaning: "PDF, plain text (.txt), Markdown (.md). Max upload size set by nginx (default 100MB)." },
          { label: "Re-ingestion", meaning: "Uploading the same file again creates duplicate chunks. Delete the old document first if you want to replace it." },
          { label: "Delete", meaning: "Hover a document row to reveal the Delete button. Removes the document and all its chunks from the DB." },
        ]},
      ]} />

      {/* Upload zone */}
      <div {...getRootProps()} className={clsx(
        "glass rounded-2xl p-10 text-center cursor-pointer transition-all border-2 border-dashed",
        isDragActive ? "border-blue-500/60 bg-blue-500/8" : "border-white/10 hover:border-white/20"
      )}>
        <input {...getInputProps()} />
        <p className="text-white/60 text-sm">
          {isDragActive
            ? "Drop files here…"
            : "Drag & drop PDFs, .txt, or .md files — or click to browse"}
        </p>
        <p className="text-white/25 text-xs mt-1">PDFs are chunked at 800 tokens/chunk and embedded via nomic-embed-text</p>
      </div>

      {/* Upload progress */}
      {uploading.length > 0 && (
        <div className="space-y-2">
          {uploading.map(name => (
            <div key={name} className="glass rounded-xl px-4 py-3 flex items-center gap-3">
              <span className="w-2 h-2 rounded-full bg-blue-400 animate-pulse" />
              <span className="text-sm text-white/70">Uploading {name}…</span>
            </div>
          ))}
        </div>
      )}

      {/* Search */}
      <GlassCard className="p-5">
        <p className="text-xs text-white/40 uppercase tracking-wider mb-3">Search Knowledge Base</p>
        <div className="flex gap-2">
          <input
            value={searchQuery}
            onChange={e => setSearchQuery(e.target.value)}
            onKeyDown={e => e.key === "Enter" && search()}
            placeholder="What do you want to find?"
            className="flex-1 bg-white/5 border border-white/15 rounded-xl px-4 py-2.5 text-sm text-white placeholder-white/30 outline-none focus:border-blue-500/50"
          />
          <button onClick={search} disabled={searching} className={clsx("btn-primary text-white", searching && "opacity-60")}>
            {searching ? "Searching…" : "Search"}
          </button>
        </div>
        {searchResults.length > 0 && (
          <div className="mt-4 space-y-3">
            {searchResults.map((r, i) => (
              <div key={i} className="bg-white/3 rounded-xl p-4">
                <div className="flex items-center justify-between mb-2">
                  <span className="text-xs font-medium text-white/60">{r.filename}</span>
                  <span className="text-xs text-emerald-400 font-mono">{(r.similarity * 100).toFixed(0)}% match</span>
                </div>
                <p className="text-sm text-white/70 leading-relaxed">{r.content.slice(0, 300)}{r.content.length > 300 ? "…" : ""}</p>
              </div>
            ))}
          </div>
        )}
      </GlassCard>

      {/* Document list */}
      <GlassCard className="p-5">
        <p className="text-xs text-white/40 uppercase tracking-wider mb-4">
          Knowledge Base — {docs.length} document{docs.length !== 1 ? "s" : ""}
        </p>
        {docs.length > 0 ? (
          <div className="space-y-2">
            {docs.map((doc: any) => (
              <div key={doc.id} className="flex items-center gap-3 py-2.5 px-3 rounded-xl hover:bg-white/4 transition-colors group">
                <StatusDot status={doc.status} />
                <div className="flex-1 min-w-0">
                  <p className="text-sm text-white/80 truncate">{doc.filename}</p>
                  <p className="text-xs text-white/35">
                    {doc.page_count ? `${doc.page_count} pages · ` : ""}
                    {doc.chunk_count ? `${doc.chunk_count} chunks · ` : ""}
                    {doc.file_size_bytes ? formatBytes(doc.file_size_bytes) : ""}
                  </p>
                </div>
                {doc.status === "error" && (
                  <p className="text-xs text-red-400 max-w-xs truncate">{doc.error_message}</p>
                )}
                <span className="text-xs text-white/25">
                  {doc.created_at ? formatDistanceToNow(parseISO(doc.created_at), { addSuffix: true }) : ""}
                </span>
                <button
                  onClick={() => deleteDoc(doc.id)}
                  className="opacity-0 group-hover:opacity-100 text-xs text-red-400/70 hover:text-red-400 transition-all ml-2">
                  Delete
                </button>
              </div>
            ))}
          </div>
        ) : (
          <p className="text-sm text-white/25 py-8 text-center">No documents yet — upload a PDF to get started</p>
        )}
      </GlassCard>
    </div>
  );
}
