"use client";

import { toastApiError } from "@/lib/toast-api-error";
import {
  usePathname,
  useRouter,
  useSearchParams,
} from "next/navigation";
import Link from "next/link";
import { useEffect, useMemo, useState, useTransition } from "react";
import {
  ChevronLeft,
  ChevronRight,
  ChevronsLeft,
  ChevronsRight,
  LogIn,
  Pencil,
  Plus,
  Search,
  SquarePen,
  Users,
} from "lucide-react";
import { toast } from "sonner";

import type { AccessCustomerRow } from "@/app/customer/_lib/admin/access-types";
import { customerCsrfHeader } from "@/lib/customer-csrf.client";
import { ErrorBanner } from "@/components/shared/error-banner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

type PaginationMeta = {
  page: number;
  pageSize: number;
  totalCount: number;
  pageCount: number;
};

type CustomersPageClientProps = {
  tenant: string;
  initialCustomers: AccessCustomerRow[];
  groups: Array<{ group_id: number; name: string }>;
  initialErrorMessage: string | null;
  initialQ: string;
  pagination: PaginationMeta;
};

type RoleSheetMode = "single" | "bulk";

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

export function CustomersPageClient({
  tenant,
  initialCustomers,
  groups,
  initialErrorMessage,
  initialQ,
  pagination,
}: CustomersPageClientProps) {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const [isPending, startTransition] = useTransition();

  const [customers, setCustomers] = useState(initialCustomers);
  const [searchInput, setSearchInput] = useState(initialQ);
  const [selectedIds, setSelectedIds] = useState<number[]>([]);
  const [sheetOpen, setSheetOpen] = useState(false);
  const [sheetMode, setSheetMode] = useState<RoleSheetMode>("single");
  const [editingCustomer, setEditingCustomer] = useState<AccessCustomerRow | null>(null);
  const [groupId, setGroupId] = useState("");
  const [saving, setSaving] = useState(false);

  // Re-sync local state when the server sends a new page of data.
  //
  // These compare against the previous prop *during render* rather than in an
  // effect. An effect would commit and paint the stale value first, then set
  // state and render again; comparing during render lets React discard the
  // in-progress output and re-render immediately, so the stale row set is never
  // shown. Local edits (optimistic role/group updates, typing, selection) still
  // win until the server sends something new.
  const [syncedCustomers, setSyncedCustomers] = useState(initialCustomers);
  if (syncedCustomers !== initialCustomers) {
    setSyncedCustomers(initialCustomers);
    setCustomers(initialCustomers);
  }

  const [syncedQ, setSyncedQ] = useState(initialQ);
  if (syncedQ !== initialQ) {
    setSyncedQ(initialQ);
    setSearchInput(initialQ);
  }

  // Selection is per result set, so clear it whenever the page/size/query moves.
  const selectionScope = `${pagination.page}|${pagination.pageSize}|${initialQ}`;
  const [syncedSelectionScope, setSyncedSelectionScope] = useState(selectionScope);
  if (syncedSelectionScope !== selectionScope) {
    setSyncedSelectionScope(selectionScope);
    setSelectedIds([]);
  }

  useEffect(() => {
    const trimmed = searchInput.trim();
    if (trimmed === initialQ.trim()) {
      return;
    }

    const handle = window.setTimeout(() => {
      const params = new URLSearchParams(searchParams.toString());
      if (trimmed) {
        params.set("q", trimmed);
      } else {
        params.delete("q");
      }
      params.set("page", "1");
      if (!params.get("pageSize")) {
        params.set("pageSize", String(pagination.pageSize));
      }
      startTransition(() => {
        router.push(`${pathname}?${params.toString()}`);
      });
    }, 350);

    return () => window.clearTimeout(handle);
  }, [
    searchInput,
    initialQ,
    pathname,
    router,
    searchParams,
    pagination.pageSize,
  ]);

  function pushListParams(next: { page?: number; pageSize?: number; q?: string }) {
    const params = new URLSearchParams(searchParams.toString());
    const page = next.page ?? pagination.page;
    const pageSize = next.pageSize ?? pagination.pageSize;
    const q = next.q !== undefined ? next.q.trim() : initialQ.trim();

    params.set("page", String(page));
    params.set("pageSize", String(pageSize));
    if (q) {
      params.set("q", q);
    } else {
      params.delete("q");
    }

    startTransition(() => {
      router.push(`${pathname}?${params.toString()}`);
    });
  }

  const allSelected = customers.length > 0 && selectedIds.length === customers.length;
  const someSelected = selectedIds.length > 0 && selectedIds.length < customers.length;

  const selectedCustomers = useMemo(
    () => customers.filter((customer) => selectedIds.includes(customer.id)),
    [customers, selectedIds],
  );

  const rangeStart =
    pagination.totalCount === 0 ? 0 : (pagination.page - 1) * pagination.pageSize + 1;
  const rangeEnd = Math.min(pagination.page * pagination.pageSize, pagination.totalCount);

  function toggleCustomer(id: number, checked: boolean) {
    setSelectedIds((current) =>
      checked ? [...new Set([...current, id])] : current.filter((value) => value !== id),
    );
  }

  function toggleAll(checked: boolean) {
    setSelectedIds(checked ? customers.map((customer) => customer.id) : []);
  }

  function openEditRole(customer: AccessCustomerRow) {
    setSheetMode("single");
    setEditingCustomer(customer);
    setGroupId(customer.group_id ? String(customer.group_id) : "__none__");
    setSheetOpen(true);
  }

  function openBulkAssign() {
    if (selectedIds.length === 0) {
      toast.error("Select at least one customer.");
      return;
    }
    setSheetMode("bulk");
    setEditingCustomer(null);
    setGroupId("__none__");
    setSheetOpen(true);
  }

  async function handleSaveRole() {
    setSaving(true);
    try {
      const resolvedGroupId = groupId === "__none__" || groupId === "" ? null : Number(groupId);

      if (sheetMode === "bulk") {
        const response = await fetch(`/customer/${tenant}/admin/customers/bulk-role`, {
          method: "POST",
          credentials: "same-origin",
          headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
          body: JSON.stringify({
            customer_ids: selectedIds,
            group_id: resolvedGroupId,
          }),
        });
        const payload = (await response.json().catch(() => null)) as
          | {
              status?: string;
              message?: string;
              data?: { items?: AccessCustomerRow[]; updated_count?: number };
            }
          | null;

        if (!response.ok || payload?.status !== "success" || !payload.data?.items) {
          throw new Error(payload?.message ?? "Could not bulk-assign customer roles.");
        }

        const byId = new Map(payload.data.items.map((item) => [item.id, item]));
        setCustomers((current) =>
          current.map((row) => (byId.has(row.id) ? { ...row, ...byId.get(row.id)! } : row)),
        );
        toast.success(payload.message ?? "Roles assigned.");
        setSelectedIds([]);
        setSheetOpen(false);
        router.refresh();
        return;
      }

      if (!editingCustomer) {
        return;
      }

      const response = await fetch(`/customer/${tenant}/admin/customers/${editingCustomer.id}`, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
        body: JSON.stringify({ group_id: resolvedGroupId }),
      });
      const payload = (await response.json().catch(() => null)) as
        | { status?: string; message?: string; data?: AccessCustomerRow }
        | null;

      if (!response.ok || payload?.status !== "success" || !payload.data) {
        throw new Error(payload?.message ?? "Could not update customer role.");
      }

      setCustomers((current) =>
        current.map((row) => (row.id === payload.data!.id ? { ...row, ...payload.data! } : row)),
      );
      toast.success("Customer role updated.");
      setSheetOpen(false);
      router.refresh();
    } catch (error) {
      toastApiError(error, "Could not update customer role.");
    } finally {
      setSaving(false);
    }
  }

  async function handleImpersonate(customer: AccessCustomerRow) {
    if (!customer.customer_uid) {
      toast.error("Customer is missing a portal identifier.");
      return;
    }

    try {
      const response = await fetch(`/customer/${tenant}/admin/customers/impersonate`, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
        body: JSON.stringify({ customer_uid: customer.customer_uid }),
      });
      const payload = (await response.json().catch(() => null)) as
        | {
            status?: string;
            message?: string;
            data?: {
              launch_url?: string;
              open_in_new_tab?: boolean;
            };
          }
        | null;

      if (!response.ok || payload?.status !== "success") {
        throw new Error(payload?.message ?? "Could not start impersonation.");
      }

      const launchUrl = payload.data?.launch_url;
      if (!launchUrl) {
        throw new Error("Impersonation launch URL was not returned.");
      }

      const opened = window.open(launchUrl, "_blank", "noopener,noreferrer");
      if (!opened) {
        throw new Error("Pop-up blocked. Allow pop-ups for this site and try again.");
      }
    } catch (error) {
      toastApiError(error, "Could not impersonate customer.");
    }
  }

  if (initialErrorMessage) {
    return <ErrorBanner message={initialErrorMessage} />;
  }

  return (
    <div className={`flex flex-col gap-4${isPending ? " opacity-70" : ""}`}>
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <h1 className="text-2xl font-semibold tracking-tight">Customers</h1>
          <p className="text-sm text-muted-foreground">
            Assign roles to sub-customers, create new customers (with their own
            database under this parent), and impersonate them when allowed.
          </p>
        </div>
        <div className="flex flex-wrap gap-2">
          <Button asChild>
            <Link href={`/customer/${tenant}/admin/customers/create`}>
              <Plus />
              Create customer
            </Link>
          </Button>
          <Button
            type="button"
            variant="outline"
            onClick={openBulkAssign}
            disabled={selectedIds.length === 0}
          >
            <Users />
            Bulk assign role
            {selectedIds.length > 0 ? ` (${selectedIds.length})` : ""}
          </Button>
        </div>
      </div>

      <div className="relative max-w-md">
        <Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
        <Input
          value={searchInput}
          onChange={(event) => setSearchInput(event.target.value)}
          placeholder="Search name, email, company, subdomain…"
          className="pl-9"
          aria-label="Search customers"
        />
      </div>

      {selectedIds.length > 0 ? (
        <div className="flex flex-wrap items-center justify-between gap-3 rounded-xl border bg-muted/30 px-4 py-3 text-sm">
          <span>
            <span className="font-medium">{selectedIds.length}</span> customer
            {selectedIds.length === 1 ? "" : "s"} selected
          </span>
          <div className="flex gap-2">
            <Button type="button" variant="outline" size="sm" onClick={() => setSelectedIds([])}>
              Clear
            </Button>
            <Button type="button" size="sm" onClick={openBulkAssign}>
              Assign role
            </Button>
          </div>
        </div>
      ) : null}

      <div className="rounded-xl border">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead className="w-10">
                <Checkbox
                  checked={allSelected ? true : someSelected ? "indeterminate" : false}
                  onCheckedChange={(value) => toggleAll(value === true)}
                  aria-label="Select all customers"
                />
              </TableHead>
              <TableHead>Name</TableHead>
              <TableHead>Email</TableHead>
              <TableHead>Subdomain</TableHead>
              <TableHead>Type</TableHead>
              <TableHead>RM</TableHead>
              <TableHead>Created by</TableHead>
              <TableHead>Role</TableHead>
              <TableHead>Status</TableHead>
              <TableHead className="text-right">Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {customers.length === 0 ? (
              <TableRow>
                <TableCell colSpan={10} className="py-8 text-center text-muted-foreground">
                  {initialQ ? "No customers match your search." : "No sub-customers yet."}
                </TableCell>
              </TableRow>
            ) : (
              customers.map((customer) => {
                const checked = selectedIds.includes(customer.id);
                return (
                  <TableRow key={customer.id} data-state={checked ? "selected" : undefined}>
                    <TableCell>
                      <Checkbox
                        checked={checked}
                        onCheckedChange={(value) => toggleCustomer(customer.id, value === true)}
                        aria-label={`Select ${customer.name || customer.email}`}
                      />
                    </TableCell>
                    <TableCell className="font-medium">
                      <div className="flex flex-col">
                        <span>
                          {customer.name || customer.company_name || `Customer #${customer.id}`}
                        </span>
                        {customer.company_name && customer.name ? (
                          <span className="text-xs text-muted-foreground">{customer.company_name}</span>
                        ) : null}
                      </div>
                    </TableCell>
                    <TableCell>{customer.email}</TableCell>
                    <TableCell>
                      {customer.subdomain ?? <span className="text-muted-foreground">—</span>}
                    </TableCell>
                    <TableCell>
                      {customer.account_type_label ?? customer.account_type ?? (
                        <span className="text-muted-foreground">—</span>
                      )}
                    </TableCell>
                    <TableCell>
                      {customer.primary_rm_label ? (
                        <span className="text-sm">{customer.primary_rm_label}</span>
                      ) : (
                        <span className="text-muted-foreground">—</span>
                      )}
                    </TableCell>
                    <TableCell>
                      {customer.created_by_name ? (
                        <span className="text-sm">{customer.created_by_name}</span>
                      ) : (
                        <span className="text-muted-foreground">—</span>
                      )}
                    </TableCell>
                    <TableCell>
                      {customer.group_name ?? <span className="text-muted-foreground">No role</span>}
                    </TableCell>
                    <TableCell>
                      <Badge variant={customer.status === "active" ? "secondary" : "outline"}>
                        {customer.status}
                      </Badge>
                    </TableCell>
                    <TableCell className="text-right">
                      <div className="inline-flex items-center justify-end gap-2">
                        <Button asChild size="sm" variant="outline">
                          <Link href={`/customer/${tenant}/admin/customers/${customer.id}/edit`}>
                            <SquarePen />
                            Edit
                          </Link>
                        </Button>
                        <Button
                          size="sm"
                          variant="outline"
                          type="button"
                          onClick={() => openEditRole(customer)}
                        >
                          <Pencil />
                          Role
                        </Button>
                        {customer.can_impersonate && customer.customer_uid ? (
                          <Button
                            size="sm"
                            variant="outline"
                            type="button"
                            onClick={() => void handleImpersonate(customer)}
                          >
                            <LogIn />
                            Impersonate
                          </Button>
                        ) : null}
                      </div>
                    </TableCell>
                  </TableRow>
                );
              })
            )}
          </TableBody>
        </Table>
      </div>

      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <p className="text-sm text-muted-foreground">
          {pagination.totalCount === 0
            ? "0 customers"
            : `Showing ${rangeStart}–${rangeEnd} of ${pagination.totalCount} customer${pagination.totalCount === 1 ? "" : "s"}`}
        </p>
        <div className="flex w-full flex-wrap items-center justify-end gap-4 sm:w-auto">
          <div className="flex items-center gap-2">
            <Label htmlFor="customers-page-size" className="text-sm font-medium whitespace-nowrap">
              Rows per page
            </Label>
            <Select
              value={String(pagination.pageSize)}
              onValueChange={(value) =>
                pushListParams({ page: 1, pageSize: Number(value) })
              }
            >
              <SelectTrigger size="sm" className="w-20" id="customers-page-size">
                <SelectValue />
              </SelectTrigger>
              <SelectContent side="top">
                {PAGE_SIZES.map((size) => (
                  <SelectItem key={size} value={String(size)}>
                    {size}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div className="text-sm font-medium tabular-nums">
            Page {pagination.page} of {Math.max(pagination.pageCount, 1)}
          </div>
          <div className="flex items-center gap-1">
            <Button
              variant="outline"
              size="icon"
              className="size-8"
              type="button"
              disabled={pagination.page <= 1 || isPending}
              onClick={() => pushListParams({ page: 1 })}
              aria-label="First page"
            >
              <ChevronsLeft className="size-4" />
            </Button>
            <Button
              variant="outline"
              size="icon"
              className="size-8"
              type="button"
              disabled={pagination.page <= 1 || isPending}
              onClick={() => pushListParams({ page: pagination.page - 1 })}
              aria-label="Previous page"
            >
              <ChevronLeft className="size-4" />
            </Button>
            <Button
              variant="outline"
              size="icon"
              className="size-8"
              type="button"
              disabled={pagination.page >= pagination.pageCount || isPending}
              onClick={() => pushListParams({ page: pagination.page + 1 })}
              aria-label="Next page"
            >
              <ChevronRight className="size-4" />
            </Button>
            <Button
              variant="outline"
              size="icon"
              className="size-8"
              type="button"
              disabled={pagination.page >= pagination.pageCount || isPending}
              onClick={() => pushListParams({ page: pagination.pageCount })}
              aria-label="Last page"
            >
              <ChevronsRight className="size-4" />
            </Button>
          </div>
        </div>
      </div>

      <Sheet
        open={sheetOpen}
        onOpenChange={(open) => {
          setSheetOpen(open);
          if (!open) {
            setEditingCustomer(null);
          }
        }}
      >
        <SheetContent className="flex h-full !w-[min(480px,calc(100vw-1rem))] !max-w-none flex-col gap-0 overflow-hidden p-0">
          <SheetHeader className="shrink-0 border-b px-6 py-5 pr-14">
            <SheetTitle>
              {sheetMode === "bulk" ? "Bulk assign role" : "Assign role"}
            </SheetTitle>
            <SheetDescription>
              {sheetMode === "bulk"
                ? `Apply one role to ${selectedIds.length} selected customer${selectedIds.length === 1 ? "" : "s"}.`
                : editingCustomer
                  ? `Assign a role for ${editingCustomer.name || editingCustomer.email || `#${editingCustomer.id}`}.`
                  : "Assign a role created under Admin → Roles."}
            </SheetDescription>
          </SheetHeader>

          <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-6 py-6">
            {sheetMode === "bulk" ? (
              <div className="rounded-lg border bg-muted/20 p-3 text-sm">
                <p className="mb-2 font-medium">Selected customers</p>
                <ul className="max-h-40 space-y-1 overflow-y-auto text-muted-foreground">
                  {selectedCustomers.map((customer) => (
                    <li key={customer.id}>
                      {customer.name || customer.company_name || customer.email || `#${customer.id}`}
                    </li>
                  ))}
                </ul>
              </div>
            ) : null}

            <div className="space-y-2">
              <Label htmlFor="customer-role">Role</Label>
              <Select value={groupId || "__none__"} onValueChange={setGroupId}>
                <SelectTrigger id="customer-role">
                  <SelectValue placeholder="Select a role" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="__none__">No role (view only)</SelectItem>
                  {groups.map((group) => (
                    <SelectItem key={group.group_id} value={String(group.group_id)}>
                      {group.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <p className="text-xs text-muted-foreground">
                Create and edit roles under Admin → Roles. Include Create/Update/Delete per module
                when the customer should mutate holdings.
              </p>
            </div>
          </div>

          <SheetFooter className="shrink-0 border-t px-6 py-4 sm:flex-row sm:justify-end">
            <Button type="button" variant="outline" onClick={() => setSheetOpen(false)} disabled={saving}>
              Cancel
            </Button>
            <Button type="button" onClick={() => void handleSaveRole()} disabled={saving}>
              {saving ? "Saving…" : sheetMode === "bulk" ? "Assign to selected" : "Save role"}
            </Button>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  );
}
