import { useCallback, useEffect, useMemo, useState } from "react";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { CheckCheck, ChevronRight, FileStack, Filter, Loader2, Search, Upload, X } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";
import { DocumentUploadDialog } from "@/components/ledger/DocumentUploadDialog";
import { EmptyState, PageHeader, SectionCard } from "@/components/ledger/Primitives";
import { StatusBadge, DuplicateBadge } from "@/components/ledger/StatusBadge";
import { ConfidenceIndicator } from "@/components/ledger/ConfidenceIndicator";
import { useAuthSession } from "@/hooks/useAuthSession";
import {
  bulkApproveApi,
  bulkNeedsReviewApi,
  fetchInvoicesApi,
  type InvoiceListItem,
} from "@/lib/api";
import { eur, useLedger } from "@/lib/ledger-store";
import type { InvoiceStatus } from "@/lib/ledger-types";

const PAGE_SIZES = [10, 20, 50] as const;

const STATUSES: InvoiceStatus[] = [
  "Processing",
  "New",
  "AI processed",
  "Needs review",
  "Auto-ready",
  "Approved",
  "Rejected",
  "Synced",
  "Sync failed",
];

type InboxSearch = {
  page: number;
  pageSize: number;
  search?: string;
  status?: string;
  client?: string;
  confidence?: string;
  date?: string;
};

function parsePage(value: unknown): number {
  const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
}

function parsePageSize(value: unknown): number {
  const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
  return PAGE_SIZES.includes(parsed as (typeof PAGE_SIZES)[number]) ? parsed : 20;
}

function validateInboxSearch(search: Record<string, unknown>): InboxSearch {
  const status = typeof search.status === "string" && search.status.trim() ? search.status : "queue";
  const client = typeof search.client === "string" && search.client.trim() ? search.client : undefined;
  const confidence =
    typeof search.confidence === "string" && search.confidence.trim() ? search.confidence : undefined;
  const date = typeof search.date === "string" && search.date.trim() ? search.date : undefined;
  const q = typeof search.search === "string" && search.search.trim() ? search.search : undefined;
  return {
    page: parsePage(search.page),
    pageSize: parsePageSize(search.pageSize),
    search: q,
    status,
    client,
    confidence,
    date,
  };
}

function pageNumbers(current: number, totalPages: number): (number | "ellipsis")[] {
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, index) => index + 1);
  }
  const pages: (number | "ellipsis")[] = [1];
  if (current > 3) pages.push("ellipsis");
  const start = Math.max(2, current - 1);
  const end = Math.min(totalPages - 1, current + 1);
  for (let page = start; page <= end; page += 1) {
    pages.push(page);
  }
  if (current < totalPages - 2) pages.push("ellipsis");
  pages.push(totalPages);
  return pages;
}

export const Route = createFileRoute("/inbox")({
  validateSearch: validateInboxSearch,
  head: () => ({
    meta: [
      { title: "Document inbox — Abdelouhab" },
      { name: "description", content: "Review, approve and prepare documents for SnelStart." },
      { property: "og:title", content: "Document inbox — Abdelouhab" },
      { property: "og:description", content: "Process uploaded invoices with AI booking suggestions." },
    ],
  }),
  component: InboxPage,
});

function InboxPage() {
  const navigate = useNavigate({ from: Route.fullPath });
  const { page, pageSize, search = "", status = "queue", client, confidence, date } = Route.useSearch();
  const clientFilter = client ?? "all";
  const confidenceFilter = confidence ?? "all";
  const { clients, clientName, uploadDocumentFile, refreshState } = useLedger();
  const session = useAuthSession("firm");
  const [rows, setRows] = useState<InvoiceListItem[]>([]);
  const [total, setTotal] = useState(0);
  const [totalPages, setTotalPages] = useState(0);
  const [hasAny, setHasAny] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [searchInput, setSearchInput] = useState(search);
  const [selected, setSelected] = useState<string[]>([]);
  const [uploadOpen, setUploadOpen] = useState(false);
  const [busy, setBusy] = useState(false);

  useEffect(() => {
    setSearchInput(search);
  }, [search]);

  const updateSearch = useCallback(
    (patch: Partial<InboxSearch>) => {
      void navigate({
        search: (current) => ({
          page: patch.page ?? current.page,
          pageSize: patch.pageSize ?? current.pageSize,
          search: patch.search !== undefined ? patch.search : current.search,
          status: patch.status !== undefined ? patch.status : current.status,
          client: patch.client !== undefined ? patch.client : current.client,
          confidence: patch.confidence !== undefined ? patch.confidence : current.confidence,
          date: patch.date !== undefined ? patch.date : current.date,
        }),
        replace: true,
      });
    },
    [navigate],
  );

  const load = useCallback(async () => {
    if (!session?.email) return;
    setLoading(true);
    setError(null);
    setRows([]);
    try {
      const res = await fetchInvoicesApi(
        { platform: "firm", email: session.email },
        {
          page,
          pageSize,
          search,
          status,
          clientId: clientFilter,
          confidence: confidenceFilter,
          date,
        },
      );
      setRows(Array.isArray(res?.items) ? res.items : []);
      setTotal(res?.total ?? 0);
      setTotalPages(res?.total_pages ?? 0);
      setHasAny(Boolean(res?.has_any));
    } catch (err) {
      setRows([]);
      setTotal(0);
      setTotalPages(0);
      setHasAny(false);
      setError(err instanceof Error ? err.message : "Could not load documents.");
    } finally {
      setLoading(false);
    }
  }, [session?.email, page, pageSize, search, status, clientFilter, confidenceFilter, date]);

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

  useEffect(() => {
    const timer = window.setTimeout(() => {
      const next = searchInput.trim();
      if (next === (search ?? "")) return;
      updateSearch({ search: next || undefined, page: 1 });
    }, 300);
    return () => window.clearTimeout(timer);
  }, [searchInput, search, updateSearch]);

  useEffect(() => {
    if (totalPages > 0 && page > totalPages) {
      updateSearch({ page: totalPages });
    }
  }, [page, totalPages, updateSearch]);

  const hasExtraFilters = Boolean(search) || clientFilter !== "all" || Boolean(date) || confidenceFilter !== "all";
  const rangeStart = total === 0 ? 0 : (page - 1) * pageSize + 1;
  const rangeEnd = total === 0 ? 0 : Math.min(page * pageSize, total);
  const pages = useMemo(() => pageNumbers(page, totalPages), [page, totalPages]);
  const allSelected = rows.length > 0 && rows.every((r) => selected.includes(r.id));
  const firmActor = session?.email
    ? { platform: "firm" as const, email: session.email, actor: session.name ?? "" }
    : null;

  const handleBulkApprove = async () => {
    if (!firmActor || selected.length === 0) return;
    setBusy(true);
    try {
      await bulkApproveApi(selected, firmActor);
      toast.success(`${selected.length} invoices approved`);
      setSelected([]);
      await refreshState();
      await load();
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Bulk approve failed");
    } finally {
      setBusy(false);
    }
  };

  const handleBulkNeedsReview = async () => {
    if (!firmActor || selected.length === 0) return;
    setBusy(true);
    try {
      await bulkNeedsReviewApi(selected, firmActor);
      toast(`${selected.length} invoices marked as needs review`);
      setSelected([]);
      await refreshState();
      await load();
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Bulk update failed");
    } finally {
      setBusy(false);
    }
  };

  const clearFilters = () => {
    setSearchInput("");
    updateSearch({
      search: undefined,
      status: "all",
      client: undefined,
      confidence: undefined,
      date: undefined,
      page: 1,
    });
  };

  const renderBody = () => {
    if (loading) {
      return (
        <div className="flex items-center gap-3 px-5 py-12 text-[13px] text-muted-foreground">
          <Loader2 size={16} className="animate-spin text-brand" aria-hidden="true" />
          Loading documents...
        </div>
      );
    }

    if (error) {
      return (
        <EmptyState
          icon={X}
          title="Unable to load documents"
          description={error}
          action={
            <Button variant="outline" onClick={() => void load()}>
              Try again
            </Button>
          }
        />
      );
    }

    if (!hasAny && !hasExtraFilters && (status === "queue" || status === "all")) {
      return (
        <EmptyState
          icon={FileStack}
          title="No documents yet"
          description="Uploaded invoices and receipts will appear here."
          action={
            <Button variant="brand" onClick={() => setUploadOpen(true)}>
              <Upload size={16} /> Upload document
            </Button>
          }
        />
      );
    }

    if (rows.length === 0 && status === "queue" && !hasExtraFilters) {
      return (
        <EmptyState
          icon={FileStack}
          title="You're all caught up"
          description="No documents currently require your attention."
          action={
            <Button variant="outline" onClick={() => updateSearch({ status: "all", page: 1 })}>
              View all documents
            </Button>
          }
        />
      );
    }

    if (rows.length === 0) {
      return (
        <EmptyState
          icon={FileStack}
          title="No documents found"
          description="No invoices match your current filters."
          action={
            <Button variant="outline" onClick={clearFilters}>
              Clear filters
            </Button>
          }
        />
      );
    }

    return (
      <>
        <div className="overflow-x-auto">
          <table className="w-full min-w-[980px] text-[13.5px]">
            <thead className="border-b border-border bg-muted/50 text-[11.5px] tracking-wide text-muted-foreground uppercase">
              <tr>
                <th className="w-10 px-4 py-3">
                  <Checkbox
                    checked={allSelected}
                    aria-label="Select all documents"
                    onCheckedChange={(v) => setSelected(v ? rows.map((r) => r.id) : [])}
                  />
                </th>
                <th className="px-3 py-3 text-left font-semibold">File</th>
                <th className="px-3 py-3 text-left font-semibold">Client</th>
                <th className="px-3 py-3 text-left font-semibold">Supplier</th>
                <th className="px-3 py-3 text-right font-semibold">Amount</th>
                <th className="px-3 py-3 text-left font-semibold">Uploaded</th>
                <th className="px-3 py-3 text-left font-semibold">Confidence</th>
                <th className="px-3 py-3 text-left font-semibold">Status</th>
                <th className="w-10 px-3 py-3" />
              </tr>
            </thead>
            <tbody className="divide-y divide-border">
              {rows.map((i) => (
                <tr key={i.id} className="transition-colors hover:bg-muted/50">
                  <td className="px-4 py-3">
                    <Checkbox
                      checked={selected.includes(i.id)}
                      aria-label={`Select ${i.file}`}
                      onCheckedChange={(v) =>
                        setSelected((prev) => (v ? [...prev, i.id] : prev.filter((x) => x !== i.id)))
                      }
                    />
                  </td>
                  <td className="max-w-[220px] truncate px-3 py-3">
                    <Link
                      to="/invoices/$id"
                      params={{ id: i.id }}
                      className="font-medium hover:text-primary"
                    >
                      {i.file}
                    </Link>
                  </td>
                  <td className="px-3 py-3 text-muted-foreground">
                    {i.clientName || clientName(i.clientId)}
                  </td>
                  <td className="px-3 py-3">
                    {i.supplier?.trim() && i.supplier !== "Pending extraction" ? (
                      i.supplier
                    ) : (
                      <span className="text-muted-foreground">Not available</span>
                    )}
                  </td>
                  <td className="num px-3 py-3 text-right font-medium">
                    {i.total > 0 ? eur(i.total) : <span className="text-muted-foreground">—</span>}
                  </td>
                  <td className="px-3 py-3 text-muted-foreground">{i.uploadedAt}</td>
                  <td className="px-3 py-3">
                    {i.confidence > 0 ? (
                      <ConfidenceIndicator value={i.confidence} compact />
                    ) : i.status === "Processing" || i.status === "New" ? (
                      <span className="inline-flex items-center gap-1.5 text-[12px] text-muted-foreground">
                        <Loader2 size={12} className="animate-spin text-brand" aria-hidden="true" />
                        Reading…
                      </span>
                    ) : (
                      <span className="text-[12px] text-muted-foreground">Not processed</span>
                    )}
                  </td>
                  <td className="px-3 py-3">
                    <div className="flex flex-wrap items-center gap-1.5">
                      <StatusBadge status={i.status} />
                      <DuplicateBadge status={i.duplicateStatus} />
                    </div>
                  </td>
                  <td className="px-3 py-3">
                    <Link
                      to="/invoices/$id"
                      params={{ id: i.id }}
                      aria-label={`Open ${i.file}`}
                      className="text-muted-foreground hover:text-primary"
                    >
                      <ChevronRight size={16} />
                    </Link>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>

        <div className="flex flex-col gap-4 border-t border-border px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
          <div className="flex flex-wrap items-center gap-3 text-[13px] text-muted-foreground">
            <span>
              Showing {rangeStart}–{rangeEnd} of {total} document{total === 1 ? "" : "s"}
            </span>
            <div className="flex items-center gap-2">
              <span className="text-[12px]">Per page</span>
              <Select
                value={String(pageSize)}
                onValueChange={(value) =>
                  updateSearch({
                    pageSize: PAGE_SIZES.includes(Number(value) as (typeof PAGE_SIZES)[number])
                      ? Number(value)
                      : 20,
                    page: 1,
                  })
                }
              >
                <SelectTrigger className="h-8 w-[72px] bg-surface">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {PAGE_SIZES.map((size) => (
                    <SelectItem key={size} value={String(size)}>
                      {size}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>

          {totalPages > 1 ? (
            <Pagination className="mx-0 w-auto justify-end">
              <PaginationContent>
                <PaginationItem>
                  <PaginationPrevious
                    href="#"
                    className={page <= 1 ? "pointer-events-none opacity-50" : undefined}
                    onClick={(event) => {
                      event.preventDefault();
                      if (page > 1) updateSearch({ page: page - 1 });
                    }}
                  />
                </PaginationItem>
                {pages.map((item, index) =>
                  item === "ellipsis" ? (
                    <PaginationItem key={`ellipsis-${index}`}>
                      <PaginationEllipsis />
                    </PaginationItem>
                  ) : (
                    <PaginationItem key={item}>
                      <PaginationLink
                        href="#"
                        isActive={item === page}
                        onClick={(event) => {
                          event.preventDefault();
                          if (item !== page) updateSearch({ page: item });
                        }}
                      >
                        {item}
                      </PaginationLink>
                    </PaginationItem>
                  ),
                )}
                <PaginationItem>
                  <PaginationNext
                    href="#"
                    className={page >= totalPages ? "pointer-events-none opacity-50" : undefined}
                    onClick={(event) => {
                      event.preventDefault();
                      if (page < totalPages) updateSearch({ page: page + 1 });
                    }}
                  />
                </PaginationItem>
              </PaginationContent>
            </Pagination>
          ) : null}
        </div>
      </>
    );
  };

  return (
    <div className="space-y-6">
      <PageHeader
        eyebrow="Workspace"
        title="Document inbox"
        subtitle="Review, approve and prepare documents for SnelStart."
        actions={
          <>
            <Button
              variant="outline"
              disabled={selected.length === 0 || busy}
              onClick={() => void handleBulkNeedsReview()}
            >
              <Filter size={16} /> Mark needs review
            </Button>
            <Button disabled={selected.length === 0 || busy} onClick={() => void handleBulkApprove()}>
              <CheckCheck size={16} /> Bulk approve{selected.length ? ` (${selected.length})` : ""}
            </Button>
          </>
        }
      />

      <div className="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-border bg-surface px-4 py-4">
        <div>
          <p className="text-[14px] font-semibold text-foreground">Upload for a client</p>
          <p className="text-[13px] text-muted-foreground">
            Add a new invoice or receipt to the processing queue.
          </p>
        </div>
        <Button onClick={() => setUploadOpen(true)}>
          <Upload size={16} /> Upload document
        </Button>
      </div>

      <DocumentUploadDialog
        open={uploadOpen}
        onOpenChange={setUploadOpen}
        platform="firm"
        clients={clients}
        defaultClientId={clientFilter === "all" ? clients[0]?.id : clientFilter}
        actor={session?.name ?? "Sara"}
        email={session?.email ?? ""}
        onUpload={async (file, options) => {
          const invoice = await uploadDocumentFile(file, {
            platform: "firm",
            email: session?.email ?? "",
            clientId: options.clientId,
            actor: session?.name ?? "Sara",
            onProgress: options.onProgress,
          });
          await load();
          return invoice;
        }}
      />

      <SectionCard>
        <div className="flex flex-wrap items-center gap-2 border-b border-border px-4 py-3">
          <div className="relative">
            <Search
              size={16}
              className="pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-muted-foreground"
              aria-hidden="true"
            />
            <Input
              value={searchInput}
              onChange={(e) => setSearchInput(e.target.value)}
              placeholder="Search documents"
              aria-label="Search documents"
              className="h-9 w-56 bg-surface pl-8"
            />
          </div>
          <Select
            value={status}
            onValueChange={(value) => updateSearch({ status: value, page: 1 })}
          >
            <SelectTrigger className="h-9 w-[150px] bg-surface" aria-label="Filter by status">
              <SelectValue placeholder="Status" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="queue">Review queue</SelectItem>
              <SelectItem value="all">All statuses</SelectItem>
              {STATUSES.map((s) => (
                <SelectItem key={s} value={s}>
                  {s}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Select
            value={clientFilter}
            onValueChange={(value) => updateSearch({ client: value === "all" ? undefined : value, page: 1 })}
          >
            <SelectTrigger className="h-9 w-[170px] bg-surface" aria-label="Filter by client">
              <SelectValue placeholder="Client" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All clients</SelectItem>
              {clients.map((c) => (
                <SelectItem key={c.id} value={c.id}>
                  {c.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Select
            value={confidenceFilter}
            onValueChange={(value) =>
              updateSearch({ confidence: value === "all" ? undefined : value, page: 1 })
            }
          >
            <SelectTrigger className="h-9 w-[150px] bg-surface" aria-label="Filter by confidence">
              <SelectValue placeholder="Confidence" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">Any confidence</SelectItem>
              <SelectItem value="high">High (85%+)</SelectItem>
              <SelectItem value="medium">Medium (65–84%)</SelectItem>
              <SelectItem value="low">Low (&lt;65%)</SelectItem>
            </SelectContent>
          </Select>
          <Input
            type="date"
            value={date ?? ""}
            onChange={(e) => updateSearch({ date: e.target.value || undefined, page: 1 })}
            aria-label="Filter by upload date"
            className="h-9 w-[160px] bg-surface"
          />
        </div>

        {renderBody()}
      </SectionCard>
    </div>
  );
}
