import { useRef, useState, useEffect, useCallback } from "react";
import { useSearchParams } from "react-router-dom";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  listDocuments,
  uploadDocument,
  deleteDocument,
  downloadDocument,
  viewDocument,
  getDocumentStatus,
  listCategories,
  createCategory,
  assignCategory,
  removeCategory,
  updateDocumentTitle,
  reprocessDocument,
  type DocumentOut,
  type CategoryOut,
  type DocumentListParams,
} from "../api/client";

function StatusBadge({ status }: { status: DocumentOut["status"] }) {
  const colors: Record<DocumentOut["status"], string> = {
    pending: "#f4a261",
    processing: "#2196f3",
    done: "#2a9d8f",
    failed: "#e63946",
  };
  return (
    <span style={{
      fontSize: 12,
      fontWeight: 600,
      padding: "2px 8px",
      borderRadius: 4,
      background: colors[status],
      color: "#fff",
    }}>
      {status}
    </span>
  );
}

// ── Category suggestions ────────────────────────────────────────────────────

interface SuggestionChipProps {
  name: string;
  existing: CategoryOut | undefined;
  onAccept: (name: string, existing: CategoryOut | undefined) => void;
  onDismiss: (name: string) => void;
}

function SuggestionChip({ name, existing, onAccept, onDismiss }: SuggestionChipProps) {
  return (
    <span style={{
      display: "inline-flex",
      alignItems: "center",
      gap: 5,
      fontSize: 12,
      background: "#fff8e1",
      border: "1px solid #f0c040",
      borderRadius: 4,
      padding: "3px 8px",
      marginRight: 6,
      marginBottom: 4,
    }}>
      <span>{name}</span>
      <button
        onClick={() => onAccept(name, existing)}
        style={{
          fontSize: 11,
          cursor: "pointer",
          padding: "1px 5px",
          background: existing ? "#dce8ff" : "#e8f5e9",
          border: "1px solid #aaa",
          borderRadius: 3,
        }}
        title={existing ? `Assign existing category "${name}"` : `Create and assign "${name}"`}
      >
        {existing ? "Assign" : "Create & Assign"}
      </button>
      <button
        onClick={() => onDismiss(name)}
        style={{ fontSize: 10, cursor: "pointer", background: "none", border: "none", color: "#999" }}
        title="Dismiss suggestion"
      >
        ✕
      </button>
    </span>
  );
}

// ── Inline title editor ─────────────────────────────────────────────────────

function InlineTitleEditor({
  docId,
  currentTitle,
  filename,
  onSaved,
}: {
  docId: string;
  currentTitle: string | null;
  filename: string;
  onSaved: () => void;
}) {
  const [editing, setEditing] = useState(false);
  const [value, setValue] = useState(currentTitle ?? "");

  const saveMut = useMutation({
    mutationFn: (t: string) => updateDocumentTitle(docId, t),
    onSuccess: () => { onSaved(); setEditing(false); },
  });

  if (!editing) {
    return (
      <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
        <span style={{ fontWeight: 500 }}>
          {currentTitle ?? <span style={{ color: "#aaa", fontStyle: "italic" }}>{filename}</span>}
        </span>
        <button
          onClick={(e) => { e.stopPropagation(); setValue(currentTitle ?? ""); setEditing(true); }}
          style={{ fontSize: 11, cursor: "pointer", color: "#888", background: "none", border: "1px solid #ddd", borderRadius: 3, padding: "0 5px" }}
          title="Edit title"
        >
          ✎
        </button>
      </span>
    );
  }

  return (
    <span onClick={(e) => e.stopPropagation()} style={{ display: "inline-flex", alignItems: "center", gap: 6, flex: 1 }}>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onKeyDown={(e) => {
          if (e.key === "Enter") saveMut.mutate(value);
          if (e.key === "Escape") { setEditing(false); setValue(currentTitle ?? ""); }
        }}
        style={{ fontSize: 14, padding: "2px 6px", border: "1px solid #888", borderRadius: 3, width: 280 }}
        autoFocus
      />
      <button
        onClick={() => saveMut.mutate(value)}
        disabled={saveMut.isPending}
        style={{ fontSize: 12, cursor: "pointer", padding: "2px 8px", background: "#222", color: "#fff", border: "none", borderRadius: 3 }}
      >
        {saveMut.isPending ? "…" : "Save"}
      </button>
      <button
        onClick={() => { setEditing(false); setValue(currentTitle ?? ""); }}
        style={{ fontSize: 12, cursor: "pointer", padding: "2px 6px", border: "1px solid #ccc", borderRadius: 3, background: "none" }}
      >
        ✕
      </button>
    </span>
  );
}

// ── Document row ────────────────────────────────────────────────────────────

function DocumentRow({
  doc,
  categories,
  onDelete,
}: {
  doc: DocumentOut;
  categories: CategoryOut[];
  onDelete: (id: string) => void;
}) {
  const [expanded, setExpanded] = useState(false);
  const qc = useQueryClient();

  let extractedData: Record<string, unknown> | null = null;
  if (doc.extracted_data) {
    try { extractedData = JSON.parse(doc.extracted_data); } catch { /* ignore */ }
  }

  // Suggested categories from AI — dismissed ones tracked locally
  const allSuggestions: string[] = Array.isArray(extractedData?.suggested_categories)
    ? (extractedData!.suggested_categories as string[])
    : [];
  const assignedNames = new Set(doc.categories.map((c) => c.name));
  const [dismissed, setDismissed] = useState<Set<string>>(new Set());

  const pendingSuggestions = allSuggestions.filter(
    (s) => !assignedNames.has(s) && !dismissed.has(s)
  );

  // Poll status while pending/processing
  const { data: liveStatus } = useQuery({
    queryKey: ["docStatus", doc.id],
    queryFn: () => getDocumentStatus(doc.id),
    refetchInterval: (query) => {
      const s = query.state.data?.status;
      return s === "pending" || s === "processing" ? 3000 : false;
    },
    enabled: doc.status === "pending" || doc.status === "processing",
  });

  useEffect(() => {
    if (liveStatus?.status === "done" || liveStatus?.status === "failed") {
      qc.invalidateQueries({ queryKey: ["documents"] });
    }
  }, [liveStatus?.status, qc]);

  const assignMut = useMutation({
    mutationFn: ({ catId }: { catId: string }) => assignCategory(doc.id, catId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["documents"] }),
  });

  const removeCatMut = useMutation({
    mutationFn: ({ catId }: { catId: string }) => removeCategory(doc.id, catId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["documents"] }),
  });

  const createAndAssignMut = useMutation({
    mutationFn: async (name: string) => {
      const cat = await createCategory(name);
      await assignCategory(doc.id, cat.id);
      return cat;
    },
    onSuccess: (_cat, name) => {
      setDismissed((prev) => new Set([...prev, name]));
      qc.invalidateQueries({ queryKey: ["documents"] });
      qc.invalidateQueries({ queryKey: ["categories"] });
    },
  });

  const reprocessMut = useMutation({
    mutationFn: () => reprocessDocument(doc.id),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["documents"] }),
  });

  const handleAcceptSuggestion = (name: string, existing: CategoryOut | undefined) => {
    if (existing) {
      assignMut.mutate({ catId: existing.id });
      setDismissed((prev) => new Set([...prev, name]));
    } else {
      createAndAssignMut.mutate(name);
    }
  };

  const assignedIds = new Set(doc.categories.map((c) => c.id));
  const unassigned = categories.filter((c) => !assignedIds.has(c.id));

  return (
    <div style={{ border: "1px solid #ddd", borderRadius: 6, marginBottom: 12 }}>
      {/* Row header */}
      <div
        style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", cursor: "pointer" }}
        onClick={() => setExpanded((e) => !e)}
      >
        <span style={{ flex: 1 }}>
          <InlineTitleEditor
            docId={doc.id}
            currentTitle={doc.title}
            filename={doc.filename}
            onSaved={() => qc.invalidateQueries({ queryKey: ["documents"] })}
          />
          {doc.title && (
            <span style={{ display: "block", fontSize: 11, color: "#999", marginTop: 1 }}>
              {doc.filename}
            </span>
          )}
        </span>
        <StatusBadge status={doc.status} />
        {doc.document_type && (
          <span style={{ fontSize: 12, color: "#555" }}>{doc.document_type}</span>
        )}
        <span style={{ fontSize: 12, color: "#999" }}>
          {(doc.file_size / 1024).toFixed(0)} KB
        </span>
        <button
          onClick={(e) => { e.stopPropagation(); viewDocument(doc.id); }}
          style={{ fontSize: 12, cursor: "pointer" }}
        >
          View
        </button>
        <button
          onClick={(e) => { e.stopPropagation(); downloadDocument(doc.id, doc.filename); }}
          style={{ fontSize: 12, cursor: "pointer" }}
        >
          Download
        </button>
        <button
          onClick={(e) => {
            e.stopPropagation();
            reprocessMut.mutate();
          }}
          disabled={reprocessMut.isPending || doc.status === "pending" || doc.status === "processing"}
          style={{
            fontSize: 12,
            cursor: reprocessMut.isPending || doc.status === "pending" || doc.status === "processing" ? "default" : "pointer",
            color: "#2a7ae2",
            opacity: reprocessMut.isPending || doc.status === "pending" || doc.status === "processing" ? 0.5 : 1,
          }}
          title="Re-run AI analysis on this document"
        >
          {reprocessMut.isPending ? "…" : "Re-analyse"}
        </button>
        <button
          onClick={(e) => {
            e.stopPropagation();
            if (confirm(`Delete "${doc.filename}"?`)) onDelete(doc.id);
          }}
          style={{ fontSize: 12, color: "#c00", cursor: "pointer" }}
        >
          Delete
        </button>
      </div>

      {/* Expanded detail */}
      {expanded && (
        <div style={{ padding: "0 16px 16px", borderTop: "1px solid #eee" }}>

          {/* Extracted fields (excluding internal-only keys) */}
          {extractedData && (
            <div style={{ marginTop: 10 }}>
              <strong>Extracted data:</strong>
              <table style={{ marginTop: 6, fontSize: 13, borderCollapse: "collapse" }}>
                <tbody>
                  {Object.entries(extractedData)
                    .filter(([k]) => k !== "tags" && k !== "suggested_categories")
                    .map(([k, v]) => (
                      <tr key={k}>
                        <td style={{ paddingRight: 16, color: "#666", verticalAlign: "top" }}>{k}</td>
                        <td>
                          {Array.isArray(v)
                            ? v.length === 0 ? "—" : JSON.stringify(v, null, 2)
                            : v !== null && v !== undefined && v !== "" ? String(v) : "—"}
                        </td>
                      </tr>
                    ))}
                </tbody>
              </table>
            </div>
          )}

          {/* Error */}
          {doc.error_message && (
            <div style={{ marginTop: 10, color: "#c00", fontSize: 13 }}>
              Error: {doc.error_message}
            </div>
          )}

          {/* Assigned categories */}
          <div style={{ marginTop: 12 }}>
            <strong style={{ fontSize: 13 }}>Categories:</strong>{" "}
            {doc.categories.map((c) => (
              <span key={c.id} style={{ fontSize: 12, background: "#dce8ff", borderRadius: 3, padding: "2px 6px", marginRight: 4 }}>
                {c.name}{" "}
                <button
                  onClick={() => removeCatMut.mutate({ catId: c.id })}
                  style={{ fontSize: 10, cursor: "pointer", color: "#555", background: "none", border: "none" }}
                >
                  x
                </button>
              </span>
            ))}
            {unassigned.length > 0 && (
              <select
                defaultValue=""
                onChange={(e) => {
                  if (e.target.value) assignMut.mutate({ catId: e.target.value });
                  e.target.value = "";
                }}
                style={{ fontSize: 12, marginLeft: 4 }}
              >
                <option value="">+ add category</option>
                {unassigned.map((c) => (
                  <option key={c.id} value={c.id}>{c.name}</option>
                ))}
              </select>
            )}
          </div>

          {/* AI-suggested categories — user must confirm each one */}
          {pendingSuggestions.length > 0 && (
            <div style={{ marginTop: 12 }}>
              <strong style={{ fontSize: 13 }}>Suggested by AI:</strong>
              <div style={{ marginTop: 6 }}>
                {pendingSuggestions.map((name) => {
                  const existing = categories.find(
                    (c) => c.name.toLowerCase() === name.toLowerCase()
                  );
                  return (
                    <SuggestionChip
                      key={name}
                      name={name}
                      existing={existing}
                      onAccept={handleAcceptSuggestion}
                      onDismiss={(n) => setDismissed((prev) => new Set([...prev, n]))}
                    />
                  );
                })}
              </div>
              <p style={{ fontSize: 11, color: "#999", margin: "4px 0 0" }}>
                "Assign" links an existing category · "Create &amp; Assign" creates it first · ✕ dismisses the suggestion
              </p>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ── Filter bar ──────────────────────────────────────────────────────────────

const SORT_OPTIONS = [
  { value: "created_at", label: "Upload date" },
  { value: "processed_at", label: "Processed date" },
  { value: "title", label: "Title" },
  { value: "filename", label: "Filename" },
  { value: "file_size", label: "File size" },
  { value: "document_type", label: "Type" },
  { value: "status", label: "Status" },
];

const STATUS_OPTIONS = ["pending", "processing", "done", "failed"];
const TYPE_OPTIONS = ["invoice", "bill", "receipt", "order", "expense", "revenue", "unknown"];

function FilterBar({
  params,
  activeCategory,
  onChange,
  onClearCategory,
}: {
  params: DocumentListParams;
  activeCategory: CategoryOut | undefined;
  onChange: (p: Partial<DocumentListParams>) => void;
  onClearCategory: () => void;
}) {
  const [searchInput, setSearchInput] = useState(params.search ?? "");

  useEffect(() => {
    const id = setTimeout(() => onChange({ search: searchInput || undefined, page: 1 }), 400);
    return () => clearTimeout(id);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchInput]);

  const anyFilterActive = !!(params.search || params.status || params.document_type || params.category_id);

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 16, alignItems: "center" }}>
      {activeCategory && (
        <span style={{
          display: "inline-flex",
          alignItems: "center",
          gap: 6,
          fontSize: 13,
          background: "#dce8ff",
          border: "1px solid #a0b8e8",
          borderRadius: 4,
          padding: "4px 10px",
        }}>
          Category: <strong>{activeCategory.name}</strong>
          <button
            onClick={onClearCategory}
            style={{ fontSize: 11, cursor: "pointer", background: "none", border: "none", color: "#555", lineHeight: 1 }}
            title="Remove category filter"
          >
            ✕
          </button>
        </span>
      )}

      <input
        value={searchInput}
        onChange={(e) => setSearchInput(e.target.value)}
        placeholder="Search title, filename…"
        style={{ padding: "6px 10px", fontSize: 13, border: "1px solid #ccc", borderRadius: 4, width: 220 }}
      />

      <select
        value={params.status ?? ""}
        onChange={(e) => onChange({ status: e.target.value || undefined, page: 1 })}
        style={{ padding: "6px 8px", fontSize: 13, border: "1px solid #ccc", borderRadius: 4 }}
      >
        <option value="">All statuses</option>
        {STATUS_OPTIONS.map((s) => (
          <option key={s} value={s}>{s}</option>
        ))}
      </select>

      <select
        value={params.document_type ?? ""}
        onChange={(e) => onChange({ document_type: e.target.value || undefined, page: 1 })}
        style={{ padding: "6px 8px", fontSize: 13, border: "1px solid #ccc", borderRadius: 4 }}
      >
        <option value="">All types</option>
        {TYPE_OPTIONS.map((t) => (
          <option key={t} value={t}>{t}</option>
        ))}
      </select>

      <select
        value={params.sort ?? "created_at"}
        onChange={(e) => onChange({ sort: e.target.value, page: 1 })}
        style={{ padding: "6px 8px", fontSize: 13, border: "1px solid #ccc", borderRadius: 4 }}
      >
        {SORT_OPTIONS.map((o) => (
          <option key={o.value} value={o.value}>{o.label}</option>
        ))}
      </select>

      <button
        onClick={() => onChange({ order: params.order === "asc" ? "desc" : "asc", page: 1 })}
        title={params.order === "asc" ? "Ascending — click to reverse" : "Descending — click to reverse"}
        style={{ padding: "6px 10px", fontSize: 13, border: "1px solid #ccc", borderRadius: 4, cursor: "pointer", background: "#fff" }}
      >
        {params.order === "asc" ? "↑ Asc" : "↓ Desc"}
      </button>

      {anyFilterActive && (
        <button
          onClick={() => {
            setSearchInput("");
            onChange({ search: undefined, status: undefined, document_type: undefined, page: 1 });
            onClearCategory();
          }}
          style={{ padding: "6px 10px", fontSize: 12, border: "1px solid #ddd", borderRadius: 4, cursor: "pointer", color: "#666", background: "#fafafa" }}
        >
          Clear filters
        </button>
      )}
    </div>
  );
}

// ── Pagination controls ──────────────────────────────────────────────────────

function Pagination({
  page,
  pages,
  total,
  perPage,
  onChange,
}: {
  page: number;
  pages: number;
  total: number;
  perPage: number;
  onChange: (p: number) => void;
}) {
  const start = (page - 1) * perPage + 1;
  const end = Math.min(page * perPage, total);

  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 16, fontSize: 13, color: "#555" }}>
      <button
        onClick={() => onChange(page - 1)}
        disabled={page <= 1}
        style={{ padding: "4px 10px", cursor: page > 1 ? "pointer" : "default", borderRadius: 4, border: "1px solid #ccc", background: "#fff" }}
      >
        ‹ Prev
      </button>
      <span>
        {start}–{end} of {total}
      </span>
      <button
        onClick={() => onChange(page + 1)}
        disabled={page >= pages}
        style={{ padding: "4px 10px", cursor: page < pages ? "pointer" : "default", borderRadius: 4, border: "1px solid #ccc", background: "#fff" }}
      >
        Next ›
      </button>
      <span style={{ color: "#aaa" }}>Page {page} / {pages}</span>
    </div>
  );
}

// ── Page ────────────────────────────────────────────────────────────────────

export default function DocumentsPage() {
  const qc = useQueryClient();
  const fileRef = useRef<HTMLInputElement>(null);
  const [newCatName, setNewCatName] = useState("");
  const [uploadError, setUploadError] = useState<string | null>(null);
  const [searchParams, setSearchParams] = useSearchParams();

  const categoryIdFromUrl = searchParams.get("category_id") ?? undefined;

  const [params, setParams] = useState<DocumentListParams>({
    page: 1,
    per_page: 20,
    sort: "created_at",
    order: "desc",
    category_id: categoryIdFromUrl,
  });

  // Sync category_id from URL into params
  useEffect(() => {
    setParams((prev) => ({ ...prev, category_id: categoryIdFromUrl, page: 1 }));
  }, [categoryIdFromUrl]);

  const updateParams = useCallback((patch: Partial<DocumentListParams>) => {
    setParams((prev) => ({ ...prev, ...patch }));
  }, []);

  const clearCategoryFilter = useCallback(() => {
    setSearchParams((sp) => {
      sp.delete("category_id");
      return sp;
    });
  }, [setSearchParams]);

  const { data: docPage, isLoading } = useQuery({
    queryKey: ["documents", params],
    queryFn: () => listDocuments(params),
  });

  const documents = docPage?.items ?? [];
  const total = docPage?.total ?? 0;
  const pages = docPage?.pages ?? 1;

  const { data: categories = [] } = useQuery({
    queryKey: ["categories"],
    queryFn: listCategories,
  });

  const activeCategory = categoryIdFromUrl
    ? categories.find((c) => c.id === categoryIdFromUrl)
    : undefined;

  const uploadMut = useMutation({
    mutationFn: uploadDocument,
    onSuccess: () => {
      setUploadError(null);
      qc.invalidateQueries({ queryKey: ["documents"] });
    },
    onError: (err: unknown) => {
      const msg =
        (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail ??
        "Upload failed";
      setUploadError(msg);
    },
  });

  const deleteMut = useMutation({
    mutationFn: deleteDocument,
    onSuccess: () => qc.invalidateQueries({ queryKey: ["documents"] }),
  });

  const createCatMut = useMutation({
    mutationFn: createCategory,
    onSuccess: () => {
      setNewCatName("");
      qc.invalidateQueries({ queryKey: ["categories"] });
    },
  });

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) uploadMut.mutate(file);
    e.target.value = "";
  };

  return (
    <>
      <div style={{ padding: 32, maxWidth: 960, margin: "0 auto" }}>
        <h1>Documents</h1>

        {/* Upload */}
        <div style={{ marginBottom: 24 }}>
          <input
            ref={fileRef}
            type="file"
            accept="application/pdf"
            style={{ display: "none" }}
            onChange={handleFileChange}
          />
          <button
            onClick={() => fileRef.current?.click()}
            disabled={uploadMut.isPending}
            style={{ padding: "8px 16px", cursor: "pointer" }}
          >
            {uploadMut.isPending ? "Uploading…" : "Upload PDF"}
          </button>
          {uploadError && (
            <span style={{ marginLeft: 12, color: "#c00", fontSize: 13 }}>{uploadError}</span>
          )}
        </div>

        {/* Category management */}
        <details style={{ marginBottom: 24 }}>
          <summary style={{ cursor: "pointer", fontWeight: 500 }}>Manage categories</summary>
          <div style={{ marginTop: 10, display: "flex", gap: 8, flexWrap: "wrap" }}>
            {categories.map((c) => (
              <span key={c.id} style={{ fontSize: 13, background: "#eee", borderRadius: 4, padding: "4px 10px" }}>
                {c.name}
              </span>
            ))}
          </div>
          <form
            style={{ marginTop: 10, display: "flex", gap: 8 }}
            onSubmit={(e) => {
              e.preventDefault();
              if (newCatName.trim()) createCatMut.mutate(newCatName.trim());
            }}
          >
            <input
              value={newCatName}
              onChange={(e) => setNewCatName(e.target.value)}
              placeholder="New category name"
              style={{ padding: "6px 10px", fontSize: 13 }}
            />
            <button type="submit" disabled={createCatMut.isPending} style={{ cursor: "pointer" }}>
              Add
            </button>
          </form>
        </details>

        {/* Filter bar */}
        <FilterBar
          params={params}
          activeCategory={activeCategory}
          onChange={updateParams}
          onClearCategory={clearCategoryFilter}
        />

        {/* Document list */}
        {isLoading ? (
          <p>Loading…</p>
        ) : documents.length === 0 ? (
          <p style={{ color: "#666" }}>
            {total === 0 && !params.search && !params.status && !params.document_type && !params.category_id
              ? "No documents yet. Upload a PDF to get started."
              : "No documents match the current filters."}
          </p>
        ) : (
          <>
            {documents.map((doc) => (
              <DocumentRow
                key={doc.id}
                doc={doc}
                categories={categories}
                onDelete={(id) => deleteMut.mutate(id)}
              />
            ))}
            {pages > 1 && (
              <Pagination
                page={params.page ?? 1}
                pages={pages}
                total={total}
                perPage={params.per_page ?? 20}
                onChange={(p) => updateParams({ page: p })}
              />
            )}
          </>
        )}
      </div>
    </>
  );
}
