"use client";

import { toastApiError } from "@/lib/toast-api-error";
import { adminMutationJson } from "@/app/customer/_lib/admin/admin-client-fetch";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import {
  LogIn,
  LogOut,
  Pencil,
  Plus,
  Trash2,
} from "lucide-react";
import { toast } from "sonner";

import type { AccessUserRow } from "@/app/customer/_lib/admin/access-types";
import { customerCsrfHeader } from "@/lib/customer-csrf.client";
import { ErrorBanner } from "@/components/shared/error-banner";
import { DeleteConfirmDialog } from "@/components/shared/delete-confirm-dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  AdminListPaginationBar,
  AdminListSearchInput,
} from "@/app/customer/[tenant]/admin/_components/admin-list-shell";
import {
  useAdminListQuery,
  type AdminListPagination,
} from "@/app/customer/[tenant]/admin/_components/use-admin-list-query";
import { adminPaths } from "@/app/customer/_lib/admin/admin-paths";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";


type UsersPageClientProps = {
  tenant: string;
  initialUsers: AccessUserRow[];
  groups: Array<{ group_id: number; name: string }>;
  initialErrorMessage: string | null;
  canManageUsers?: boolean;
  canAssignRoles?: boolean;
  initialQ: string;
  pagination: AdminListPagination;
};

type UserFormState = {
  email: string;
  first_name: string;
  last_name: string;
  password: string;
  group_id: string;
  status: string;
};

export function UsersPageClient({
  tenant,
  initialUsers,
  groups,
  initialErrorMessage,
  canManageUsers = false,
  canAssignRoles = false,
  initialQ,
  pagination,
}: UsersPageClientProps) {
  const paths = adminPaths(tenant);
  const router = useRouter();
  const {
    searchInput,
    setSearchInput,
    isPending,
    pushListParams,
    rangeStart,
    rangeEnd,
  } = useAdminListQuery({ initialQ, pagination });

  const [users, setUsers] = useState(initialUsers);

  // Re-sync local state when the server sends a new page of data. Comparing
  // against the previous prop during render (rather than in an effect) lets
  // React discard the in-progress output and re-render immediately, so a
  // stale row set is never painted before the fresh one arrives.
  const [syncedUsers, setSyncedUsers] = useState(initialUsers);
  if (syncedUsers !== initialUsers) {
    setSyncedUsers(initialUsers);
    setUsers(initialUsers);
  }

  const [dialogOpen, setDialogOpen] = useState(false);
  const [editingUser, setEditingUser] = useState<AccessUserRow | null>(null);
  const [revokeTarget, setRevokeTarget] = useState<AccessUserRow | null>(null);
  const [form, setForm] = useState<UserFormState>({
    email: "",
    first_name: "",
    last_name: "",
    password: "",
    group_id: "",
    status: "active",
  });
  const [saving, setSaving] = useState(false);
  const createHref = paths.usersCreate();

  function openEdit(user: AccessUserRow) {
    setEditingUser(user);
    setForm({
      email: user.email,
      first_name: user.first_name ?? "",
      last_name: user.last_name ?? "",
      password: "",
      group_id: user.group_id ? String(user.group_id) : "",
      status: user.status,
    });
    setDialogOpen(true);
  }

  async function handleSave() {
    if (!editingUser) {
      return;
    }

    if (!form.email.trim()) {
      toast.error("Email is required.");
      return;
    }

    setSaving(true);
    try {
      const result = await adminMutationJson(
        paths.user(editingUser.id),
        {
          method: "POST",
          credentials: "same-origin",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
            ...customerCsrfHeader(tenant),
          },
          body: JSON.stringify({
            email: form.email.trim(),
            first_name: form.first_name.trim(),
            last_name: form.last_name.trim(),
            password: form.password.trim() || undefined,
            ...(canAssignRoles
              ? {
                  group_id:
                    form.group_id && form.group_id !== "__none__"
                      ? Number(form.group_id)
                      : null,
                }
              : {}),
            status: form.status,
            phone: editingUser.phone ?? "",
          }),
        },
        "Could not save user.",
      );
      if (!result.ok) return;

      toast.success(result.message ?? "User saved.");
      setDialogOpen(false);
      router.refresh();
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete(user: AccessUserRow) {
    if (!window.confirm(`Delete user "${user.name}"?`)) {
      return;
    }

    const result = await adminMutationJson(
      paths.user(user.id),
      { method: "DELETE", credentials: "same-origin" },
      "Could not delete user.",
    );
    if (!result.ok) return;

    setUsers((current) => current.filter((item) => item.id !== user.id));
    toast.success(result.message ?? "User deleted.");
    router.refresh();
  }

  async function handleRevokeSessions() {
    if (!revokeTarget) return;

    const result = await adminMutationJson(
      paths.userRevokeSessions(revokeTarget.id),
      {
        method: "POST",
        credentials: "same-origin",
        headers: {
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
      },
      "Could not end sessions.",
    );
    if (!result.ok) {
      throw new Error(result.message ?? "Could not end sessions.");
    }

    toast.success(result.message ?? "Sessions ended.");
    router.refresh();
  }

  async function handleImpersonate(user: AccessUserRow) {
    if (!user.customer_uid) {
      toast.error("User is missing a portal identifier.");
      return;
    }

    const result = await adminMutationJson<{
      data?: { launch_url?: string; open_in_new_tab?: boolean };
    }>(
      paths.usersImpersonate(),
      {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
        body: JSON.stringify({ customer_uid: user.customer_uid }),
      },
      "Could not impersonate user.",
    );
    if (!result.ok) return;

    const launchUrl = result.data?.data?.launch_url;
    if (!launchUrl) {
      toastApiError(new Error("Impersonation launch URL was not returned."), "Could not impersonate user.");
      return;
    }

    // No `noopener` here: it makes `window.open` return null even on success, which turned
    // every launch into a false "pop-up blocked" error. The opener is severed explicitly.
    const opened = window.open(launchUrl, "_blank");
    if (!opened) {
      toastApiError(
        new Error("Pop-up blocked. Allow pop-ups for this site and try again."),
        "Could not impersonate user.",
      );
      return;
    }
    try {
      opened.opener = null;
    } catch {
      // Browser refused the assignment; the tab is on our own portal origin regardless.
    }
  }

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

  // Hide the whole Actions column rather than rendering an empty header with no cell
  // content when this viewer can't manage users and none of the visible rows are
  // impersonable by them (e.g. a Role without "Impersonate sub-users" granted).
  const showActionsColumn =
    canManageUsers || users.some((user) => user.can_impersonate && user.customer_uid && user.permissions?.impersonate);

  return (
    <div className={`flex flex-col gap-4${isPending ? " opacity-70" : ""}`}>
      <div className="flex items-center justify-between gap-3">
        <div>
          <h1 className="text-2xl font-semibold tracking-tight">Users</h1>
          <p className="text-sm text-muted-foreground">
            {canManageUsers
              ? "Manage sub-users and assign each user a permission role."
              : "Portal users are managed by the corporate account."}
          </p>
        </div>
        {canManageUsers ? (
          <Button asChild>
            <Link href={createHref}>
              <Plus />
              New user
            </Link>
          </Button>
        ) : null}
      </div>

      <AdminListSearchInput
        value={searchInput}
        onChange={setSearchInput}
        placeholder="Search name, email, phone…"
        aria-label="Search users"
      />

      <div className="rounded-xl border">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Name</TableHead>
              <TableHead>Email</TableHead>
              <TableHead>Role</TableHead>
              <TableHead>Status</TableHead>
              <TableHead>Presence</TableHead>
              {showActionsColumn ? <TableHead className="text-right">Actions</TableHead> : null}
            </TableRow>
          </TableHeader>
          <TableBody>
            {users.length === 0 ? (
              <TableRow>
                <TableCell
                  colSpan={showActionsColumn ? 6 : 5}
                  className="py-8 text-center text-muted-foreground"
                >
                  {initialQ ? "No users match your search." : "No sub-users yet."}
                </TableCell>
              </TableRow>
            ) : (
              users.map((user) => (
                <TableRow key={user.id}>
                  <TableCell className="font-medium">{user.name}</TableCell>
                  <TableCell>{user.email}</TableCell>
                  <TableCell>{user.group_name ?? <span className="text-muted-foreground">Unassigned</span>}</TableCell>
                  <TableCell>
                    <Badge variant={user.status === "active" ? "secondary" : "outline"}>{user.status}</Badge>
                  </TableCell>
                  <TableCell>
                    <PresenceBadge presence={user.presence ?? "offline"} />
                  </TableCell>
                  {showActionsColumn ? (
                    <TableCell className="text-right">
                      <div className="flex justify-end gap-2">
                        {user.can_impersonate && user.customer_uid && user.permissions?.impersonate ? (
                          <Button
                            size="sm"
                            variant="outline"
                            type="button"
                            onClick={() => void handleImpersonate(user)}
                          >
                            <LogIn />
                            Impersonate
                          </Button>
                        ) : null}
                        {canManageUsers ? (
                          <>
                            {(user.active_session_count ?? 0) > 0 ? (
                              <Button
                                size="sm"
                                variant="outline"
                                type="button"
                                onClick={() => setRevokeTarget(user)}
                              >
                                <LogOut />
                                End sessions
                              </Button>
                            ) : null}
                            <Button size="sm" variant="outline" onClick={() => openEdit(user)}>
                              <Pencil />
                              Edit
                            </Button>
                            <Button size="sm" variant="outline" onClick={() => void handleDelete(user)}>
                              <Trash2 />
                              Delete
                            </Button>
                          </>
                        ) : null}
                      </div>
                    </TableCell>
                  ) : null}
                </TableRow>
              ))
            )}
          </TableBody>
        </Table>
      </div>

      <AdminListPaginationBar
        pagination={pagination}
        isPending={isPending}
        rangeStart={rangeStart}
        rangeEnd={rangeEnd}
        noun="user"
        pageSizeId="users-page-size"
        onPush={pushListParams}
      />

      <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Edit user</DialogTitle>
            <DialogDescription>Update the user&apos;s profile, role, and optional password.</DialogDescription>
          </DialogHeader>

          <div className="grid gap-4">
            <div className="grid gap-2">
              <Label htmlFor="user-email">Email</Label>
              <Input
                id="user-email"
                type="email"
                value={form.email}
                onChange={(event) => setForm((current) => ({ ...current, email: event.target.value }))}
              />
            </div>
            <div className="grid gap-4 sm:grid-cols-2">
              <div className="grid gap-2">
                <Label htmlFor="user-first-name">First name</Label>
                <Input
                  id="user-first-name"
                  value={form.first_name}
                  onChange={(event) => setForm((current) => ({ ...current, first_name: event.target.value }))}
                />
              </div>
              <div className="grid gap-2">
                <Label htmlFor="user-last-name">Last name</Label>
                <Input
                  id="user-last-name"
                  value={form.last_name}
                  onChange={(event) => setForm((current) => ({ ...current, last_name: event.target.value }))}
                />
              </div>
            </div>
            <div className="grid gap-2">
              <Label htmlFor="user-password">New password (optional)</Label>
              <Input
                id="user-password"
                type="password"
                value={form.password}
                onChange={(event) => setForm((current) => ({ ...current, password: event.target.value }))}
              />
            </div>
            {canAssignRoles ? (
              <div className="grid gap-2">
                <Label>Role</Label>
                <Select
                  value={form.group_id || "__none__"}
                  onValueChange={(value) =>
                    setForm((current) => ({
                      ...current,
                      group_id: value === "__none__" ? "" : value,
                    }))
                  }
                >
                  <SelectTrigger>
                    <SelectValue placeholder="Select a role" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="__none__">Unassigned</SelectItem>
                    {groups.map((group) => (
                      <SelectItem key={group.group_id} value={String(group.group_id)}>
                        {group.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
            ) : null}
          </div>

          <DialogFooter>
            <Button variant="outline" onClick={() => setDialogOpen(false)}>
              Cancel
            </Button>
            <Button onClick={() => void handleSave()} disabled={saving}>
              {saving ? "Saving..." : "Save user"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <DeleteConfirmDialog
        open={revokeTarget !== null}
        onOpenChange={(open) => {
          if (!open) setRevokeTarget(null);
        }}
        icon={LogOut}
        title="End all sessions for this user?"
        description={
          revokeTarget
            ? `Force-logout ${revokeTarget.name} on every active device. They will need to sign in again.`
            : null
        }
        confirmLabel="End all sessions"
        confirmingLabel="Ending..."
        onConfirm={handleRevokeSessions}
      />
    </div>
  );
}

function PresenceBadge({
  presence,
}: {
  presence: "online" | "session_active" | "offline";
}) {
  const label =
    presence === "online" ? "Online" : presence === "session_active" ? "Away" : "Offline";
  const variant =
    presence === "online" ? "default" : presence === "session_active" ? "secondary" : "outline";
  return <Badge variant={variant}>{label}</Badge>;
}
