import { useCallback, useEffect, useMemo, useState } from "react";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { Building2, Check, Copy, Eye, EyeOff, Filter, Loader2, Plus, Search, Users, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";
import { EmptyState, PageHeader, SectionCard } from "@/components/ledger/Primitives";
import { Pill } from "@/components/ledger/StatusBadge";
import { useAuthSession } from "@/hooks/useAuthSession";
import {
  CLIENT_PAGE_SIZES,
  clientsListSearch,
  type ClientsSearch,
  validateClientsSearch,
} from "@app/lib/clients-search";
import { fetchClientsApi, createClientApi, type ClientListItem, type CreatedClient } from "@/lib/api";
import { useLedger } from "@/lib/ledger-store";
import { clientSnelStartStatus, snelstartStatusLabel, snelstartStatusTone } from "@/lib/snelstart";
import { Route as ClientsRoute } from "./clients";

export const Route = createFileRoute("/clients/")({
  validateSearch: validateClientsSearch,
  head: () => ({
    meta: [
      { title: "Clients — Amanah" },
      { name: "description", content: "Manage your administraties, SnelStart connections and open work." },
    ],
  }),
  component: ClientsPage,
});

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;
}

function ClientsPage() {
  const navigate = useNavigate({ from: ClientsRoute.fullPath });
  const listSearch = ClientsRoute.useSearch();
  const { page, pageSize, search = "", connected = false } = listSearch;
  const listSearchParams = useMemo(() => clientsListSearch(listSearch), [listSearch]);
  const session = useAuthSession("firm");
  const { refreshState } = useLedger();

  const [rows, setRows] = useState<ClientListItem[]>([]);
  const [total, setTotal] = useState(0);
  const [totalPages, setTotalPages] = useState(0);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [searchInput, setSearchInput] = useState(search);
  const [addOpen, setAddOpen] = useState(false);
  const [newName, setNewName] = useState("");
  const [newCompany, setNewCompany] = useState("");
  const [newKvk, setNewKvk] = useState("");
  const [newEmail, setNewEmail] = useState("");
  const [newPassword, setNewPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [showPassword, setShowPassword] = useState(false);
  const [saving, setSaving] = useState(false);
  const [formError, setFormError] = useState<string | null>(null);
  const [created, setCreated] = useState<CreatedClient | null>(null);
  const [copied, setCopied] = useState(false);

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

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

  const load = useCallback(async () => {
    if (!session?.email) return;
    setLoading(true);
    setError(null);
    setRows([]);
    try {
      const res = await fetchClientsApi(
        { platform: "firm", email: session.email },
        { page, pageSize, search, connected },
      );
      setRows(Array.isArray(res?.items) ? res.items : []);
      setTotal(res?.total ?? 0);
      setTotalPages(res?.total_pages ?? 0);
    } catch (err) {
      setRows([]);
      setTotal(0);
      setTotalPages(0);
      setError(err instanceof Error ? err.message : "Could not load clients.");
    } finally {
      setLoading(false);
    }
  }, [session?.email, page, pageSize, search, connected]);

  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]);

  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 hasFilters = Boolean(search) || connected;

  const resetAddForm = () => {
    setNewName("");
    setNewCompany("");
    setNewKvk("");
    setNewEmail("");
    setNewPassword("");
    setConfirmPassword("");
    setShowPassword(false);
    setFormError(null);
    setCreated(null);
    setCopied(false);
  };

  const handleAddClient = async () => {
    const name = newName.trim();
    const email = newEmail.trim().toLowerCase();
    if (!name || !email || !session?.email) return;
    if (!email.includes("@")) {
      setFormError("Please enter a valid email address.");
      return;
    }
    if (newPassword.length < 8) {
      setFormError("Password must be at least 8 characters.");
      return;
    }
    if (newPassword !== confirmPassword) {
      setFormError("Passwords do not match.");
      return;
    }
    setSaving(true);
    setFormError(null);
    try {
      const next = await createClientApi(
        {
          name,
          companyName: newCompany.trim(),
          email,
          password: newPassword,
          kvk: newKvk.trim(),
        },
        { platform: "firm", email: session.email, actor: session.name ?? "" },
      );
      setNewPassword("");
      setConfirmPassword("");
      setCreated(next);
      await refreshState();
      updateSearch({ page: 1 });
      await load();
    } catch (err) {
      setFormError(err instanceof Error ? err.message : "Could not create this client.");
    } finally {
      setSaving(false);
    }
  };

  const copyLoginEmail = async () => {
    const value = created?.portal?.email || created?.email || newEmail;
    if (!value) return;
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1500);
    } catch {
      setFormError("Could not copy the login email.");
    }
  };

  const renderTableBody = () => {
    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 clients...
        </div>
      );
    }

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

    if (total === 0 && !hasFilters) {
      return (
        <EmptyState
          icon={Users}
          title="No clients yet"
          description="Add your first client to get started."
          action={
            <Button variant="brand" onClick={() => setAddOpen(true)}>
              <Plus size={16} /> Add client
            </Button>
          }
        />
      );
    }

    if (rows.length === 0) {
      return (
        <EmptyState
          icon={Users}
          title="No clients found"
          description="No clients match your current filters."
          action={
            <Button
              variant="outline"
              onClick={() => {
                setSearchInput("");
                updateSearch({ search: undefined, connected: false, page: 1 });
              }}
            >
              Clear filters
            </Button>
          }
        />
      );
    }

    return (
      <>
        <div className="overflow-x-auto">
          <table className="w-full min-w-[920px] text-[13.5px]">
            <thead className="sticky top-0 border-b border-border bg-muted/50 text-[11.5px] tracking-wide text-muted-foreground uppercase">
              <tr>
                <th className="px-5 py-3 text-left font-semibold">Client</th>
                <th className="px-4 py-3 text-left font-semibold">Portal email</th>
                <th className="px-4 py-3 text-left font-semibold">KvK</th>
                <th className="px-4 py-3 text-right font-semibold">Documents</th>
                <th className="px-4 py-3 text-right font-semibold">Open documents</th>
                <th className="px-4 py-3 text-right font-semibold">Open queries</th>
                <th className="px-4 py-3 text-left font-semibold">Last upload</th>
                <th className="px-4 py-3 text-left font-semibold">Last activity</th>
                <th className="px-4 py-3 text-left font-semibold">SnelStart</th>
                <th className="px-5 py-3 text-left font-semibold">Status</th>
                <th className="px-5 py-3 text-right font-semibold"> </th>
              </tr>
            </thead>
            <tbody className="divide-y divide-border">
              {rows.map((client) => {
                const lastActivityLabel = client.lastActivityAt
                  ? new Date(client.lastActivityAt).toLocaleDateString()
                  : "—";
                return (
                  <tr
                    key={client.id}
                    className="cursor-pointer transition-colors hover:bg-muted/50"
                    onClick={() =>
                      navigate({
                        to: "/clients/$id",
                        params: { id: client.id },
                        search: listSearchParams,
                      })
                    }
                  >
                    <td className="px-5 py-3">
                      <Link
                        to="/clients/$id"
                        params={{ id: client.id }}
                        search={listSearchParams}
                        className="flex items-center gap-2.5 font-medium hover:text-primary"
                        onClick={(event) => event.stopPropagation()}
                      >
                        <span className="flex size-8 items-center justify-center rounded-md bg-muted text-muted-foreground">
                          <Building2 size={16} aria-hidden="true" />
                        </span>
                        {client.name}
                      </Link>
                    </td>
                    <td className="px-4 py-3 text-muted-foreground">{client.email || "Not invited"}</td>
                    <td className="num px-4 py-3 text-muted-foreground">{client.kvk}</td>
                    <td className="num px-4 py-3 text-right">{client.documentCount}</td>
                    <td className="num px-4 py-3 text-right">{client.openDocuments}</td>
                    <td className="num px-4 py-3 text-right">{client.openQueries}</td>
                    <td className="px-4 py-3 text-muted-foreground">{client.lastUpload || "—"}</td>
                    <td className="px-4 py-3 text-muted-foreground">{lastActivityLabel}</td>
                    <td className="px-4 py-3">
                      <Pill tone={snelstartStatusTone(clientSnelStartStatus(client))}>
                        {snelstartStatusLabel(clientSnelStartStatus(client))}
                      </Pill>
                    </td>
                    <td className="px-5 py-3">
                      <Pill tone={client.openQueries > 0 ? "amber" : "neutral"}>
                        {client.openQueries > 0 ? "Awaiting client" : "In control"}
                      </Pill>
                    </td>
                    <td className="px-5 py-3 text-right" onClick={(event) => event.stopPropagation()}>
                      <Button size="sm" variant="outline" asChild>
                        <Link
                          to="/clients/$id"
                          params={{ id: client.id }}
                          search={listSearchParams}
                        >
                          View
                        </Link>
                      </Button>
                    </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} client{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: CLIENT_PAGE_SIZES.includes(Number(value) as (typeof CLIENT_PAGE_SIZES)[number])
                      ? Number(value)
                      : 20,
                    page: 1,
                  })
                }
              >
                <SelectTrigger className="h-8 w-[72px] bg-surface">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {CLIENT_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="Clients"
        subtitle="Manage your administraties and bookkeeping workflows."
        actions={
          <>
            <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 clients"
                aria-label="Search clients"
                className="h-9 w-56 bg-surface pl-8"
              />
            </div>
            <Button
              variant={connected ? "default" : "outline"}
              onClick={() => updateSearch({ connected: !connected, page: 1 })}
              aria-pressed={connected}
            >
              <Filter size={16} /> SnelStart connected
            </Button>
            <Button onClick={() => setAddOpen(true)}>
              <Plus size={16} /> Add client
            </Button>
          </>
        }
      />

      <SectionCard>{renderTableBody()}</SectionCard>

      <Dialog
        open={addOpen}
        onOpenChange={(open) => {
          setAddOpen(open);
          if (!open) resetAddForm();
        }}
      >
        <DialogContent className="sm:max-w-[460px]">
          {created ? (
            <>
              <DialogHeader>
                <DialogTitle>Client created successfully</DialogTitle>
              </DialogHeader>
              <div className="space-y-4 py-2">
                <p className="text-[13px] text-muted-foreground">
                  A Client Portal login is ready. Share the email with the client. The password is
                  temporary — they can change it from their profile after signing in.
                </p>
                <dl className="space-y-3 rounded-lg border border-border bg-muted/40 px-4 py-3">
                  <div>
                    <dt className="text-[11.5px] font-semibold tracking-wide text-muted-foreground uppercase">
                      Client name
                    </dt>
                    <dd className="mt-1 text-[13.5px] text-foreground">
                      {created.portal?.name || newName}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-[11.5px] font-semibold tracking-wide text-muted-foreground uppercase">
                      Company
                    </dt>
                    <dd className="mt-1 text-[13.5px] text-foreground">
                      {created.portal?.company || created.name || "—"}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-[11.5px] font-semibold tracking-wide text-muted-foreground uppercase">
                      Email
                    </dt>
                    <dd className="mt-1 text-[13.5px] text-foreground">
                      {created.portal?.email || created.email}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-[11.5px] font-semibold tracking-wide text-muted-foreground uppercase">
                      Portal status
                    </dt>
                    <dd className="mt-1">
                      <Pill tone="green">Ready to sign in</Pill>
                    </dd>
                  </div>
                  <div>
                    <dt className="text-[11.5px] font-semibold tracking-wide text-muted-foreground uppercase">
                      SnelStart
                    </dt>
                    <dd className="mt-1">
                      <Pill tone={snelstartStatusTone("not_connected")}>
                        {snelstartStatusLabel("not_connected")}
                      </Pill>
                    </dd>
                  </div>
                </dl>
              </div>
              <DialogFooter className="gap-2 sm:justify-between">
                <Button variant="outline" onClick={() => void copyLoginEmail()}>
                  {copied ? <Check size={16} /> : <Copy size={16} />}
                  {copied ? "Copied" : "Copy login email"}
                </Button>
                <Button
                  onClick={() => {
                    setAddOpen(false);
                    resetAddForm();
                  }}
                >
                  Done
                </Button>
              </DialogFooter>
            </>
          ) : (
            <>
              <DialogHeader>
                <DialogTitle>Add client</DialogTitle>
              </DialogHeader>
              <div className="space-y-4 py-2">
                <div className="space-y-2">
                  <Label htmlFor="client-full-name">Full name</Label>
                  <Input
                    id="client-full-name"
                    value={newName}
                    onChange={(e) => setNewName(e.target.value)}
                    placeholder="Jane Doe"
                    autoComplete="name"
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="client-company">Company name</Label>
                  <Input
                    id="client-company"
                    value={newCompany}
                    onChange={(e) => setNewCompany(e.target.value)}
                    placeholder="Company or administratie"
                    autoComplete="organization"
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="client-email">Email / username</Label>
                  <Input
                    id="client-email"
                    type="email"
                    value={newEmail}
                    onChange={(e) => setNewEmail(e.target.value)}
                    placeholder="client@company.com"
                    autoComplete="off"
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="client-password">Temporary password</Label>
                  <div className="relative">
                    <Input
                      id="client-password"
                      type={showPassword ? "text" : "password"}
                      value={newPassword}
                      onChange={(e) => setNewPassword(e.target.value)}
                      placeholder="At least 8 characters"
                      autoComplete="new-password"
                      minLength={8}
                      className="pr-10"
                    />
                    <button
                      type="button"
                      onClick={() => setShowPassword((v) => !v)}
                      className="absolute top-1/2 right-2.5 -translate-y-1/2 rounded-md p-1 text-muted-foreground transition-colors hover:text-foreground"
                      aria-label={showPassword ? "Hide password" : "Show password"}
                    >
                      {showPassword ? <EyeOff size={16} aria-hidden="true" /> : <Eye size={16} aria-hidden="true" />}
                    </button>
                  </div>
                </div>
                <div className="space-y-2">
                  <Label htmlFor="client-confirm-password">Confirm password</Label>
                  <Input
                    id="client-confirm-password"
                    type={showPassword ? "text" : "password"}
                    value={confirmPassword}
                    onChange={(e) => setConfirmPassword(e.target.value)}
                    placeholder="Repeat temporary password"
                    autoComplete="new-password"
                    minLength={8}
                  />
                </div>
                <div className="space-y-2">
                  <Label htmlFor="client-kvk">KvK number (optional)</Label>
                  <Input
                    id="client-kvk"
                    value={newKvk}
                    onChange={(e) => setNewKvk(e.target.value)}
                    placeholder="12345678"
                  />
                </div>
                <div className="space-y-2">
                  <Label>SnelStart connection</Label>
                  <div>
                    <Pill tone={snelstartStatusTone("not_connected")}>
                      {snelstartStatusLabel("not_connected")}
                    </Pill>
                  </div>
                  <p className="text-[12px] text-muted-foreground">
                    New clients start disconnected. Connect SnelStart later from Settings.
                  </p>
                </div>
                {formError ? <p className="text-[13px] text-destructive">{formError}</p> : null}
              </div>
              <DialogFooter>
                <Button variant="outline" onClick={() => setAddOpen(false)}>
                  Cancel
                </Button>
                <Button
                  disabled={
                    !newName.trim() ||
                    !newEmail.trim() ||
                    newPassword.length < 8 ||
                    saving
                  }
                  onClick={() => void handleAddClient()}
                >
                  {saving ? "Creating..." : "Add client"}
                </Button>
              </DialogFooter>
            </>
          )}
        </DialogContent>
      </Dialog>
    </div>
  );
}
