import { AdminPageHeader, adminLayout } from '@/Layouts/AdminLayout';
import { Button, Card, Checkbox, Field, Input, Modal, Pill, Popover, Select, Tooltip } from '@/Components/ui';
import { cn } from '@/lib/cn';
import type { SharedProps } from '@/types/inertia';
import { Head, Link, router, useForm, usePage } from '@inertiajs/react';
import { CheckCircle2, MailWarning, MoreHorizontal, Search, UserPlus } from 'lucide-react';
import {
    useEffect,
    useRef,
    useState,
    type SubmitEvent as ReactSubmitEvent,
    type MouseEvent as ReactMouseEvent,
} from 'react';

type TenantOption = { id: number; name: string };

type UserRow = {
    id: number;
    name: string;
    email: string;
    email_verified: boolean;
    is_admin: boolean;
    disabled: boolean;
    pending: boolean;
    role: string;
    tenant: { id: number; name: string } | null;
    plan_key: string;
    projects_count: number;
    created_at: string | null;
};

type BulkAction = 'delete' | 'make_admin' | 'demote' | 'disable' | 'enable';

const PLAN_TONES: Record<string, 'accent' | 'neutral'> = {
    pro: 'accent',
    scale: 'accent',
    free: 'neutral',
};

type Paginated<T> = {
    data: T[];
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
    links: Array<{ url: string | null; label: string; active: boolean }>;
};

type Props = {
    users: Paginated<UserRow>;
    filters: {
        q: string;
        role: 'all' | 'admin';
        tenant_id: number | null;
    };
    tenants: TenantOption[];
};

const RELATIVE_FMT = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

function relativeTime(iso: string | null): string {
    if (!iso) return '—';
    const diffMs = new Date(iso).getTime() - Date.now();
    const absMin = Math.abs(diffMs) / 60_000;
    if (absMin < 1) return 'just now';
    if (absMin < 60) return RELATIVE_FMT.format(Math.round(diffMs / 60_000), 'minute');
    if (absMin < 60 * 24) return RELATIVE_FMT.format(Math.round(diffMs / 3_600_000), 'hour');
    if (absMin < 60 * 24 * 30) return RELATIVE_FMT.format(Math.round(diffMs / 86_400_000), 'day');
    if (absMin < 60 * 24 * 365) return RELATIVE_FMT.format(Math.round(diffMs / (86_400_000 * 30)), 'month');
    return RELATIVE_FMT.format(Math.round(diffMs / (86_400_000 * 365)), 'year');
}

function AdminUsersIndex({ users, filters, tenants }: Props) {
    const { flash, auth, appMode } = usePage<SharedProps>().props;
    // self_hosted is single-tenant, no billing, no impersonation. Tenant /
    // Plan / Projects-count columns and impersonation are saas-only noise.
    const isSaas = appMode === 'saas';
    const [q, setQ] = useState(filters.q ?? '');
    const [role, setRole] = useState<'all' | 'admin'>(filters.role ?? 'all');
    const [tenantId, setTenantId] = useState<number | ''>(filters.tenant_id ?? '');
    const [deleting, setDeleting] = useState<UserRow | null>(null);
    const [inviting, setInviting] = useState(false);
    const [creating, setCreating] = useState(false);

    // Debounced auto-apply: filters update on input change rather than via a
    // submit button. We compare against the server's `filters` prop so a
    // navigation that lands with different params doesn't fight the local
    // state, and skip the very first effect tick so the initial render
    // doesn't re-fetch the page it just rendered.
    const firstRender = useRef(true);
    useEffect(() => {
        if (firstRender.current) {
            firstRender.current = false;
            return;
        }
        const handle = window.setTimeout(() => {
            const sameQ = q.trim() === (filters.q ?? '');
            const sameRole = role === (filters.role ?? 'all');
            const sameTenant =
                (tenantId === '' ? null : tenantId) === (filters.tenant_id ?? null);
            if (sameQ && sameRole && sameTenant) return;

            router.get(
                '/admin/users',
                {
                    q: q.trim() || undefined,
                    role: role === 'admin' ? 'admin' : undefined,
                    tenant_id: tenantId === '' ? undefined : tenantId,
                },
                {
                    preserveScroll: true,
                    preserveState: true,
                    replace: true,
                },
            );
        }, 220);
        return () => window.clearTimeout(handle);
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [q, role, tenantId]);

    const reset = () => {
        setQ('');
        setRole('all');
        setTenantId('');
    };

    const impersonate = (user: UserRow) => {
        router.post(
            `/admin/users/${user.id}/impersonate`,
            {},
            { preserveScroll: true },
        );
    };

    const toggleAdmin = (user: UserRow) => {
        router.patch(
            `/admin/users/${user.id}`,
            { is_admin: !user.is_admin },
            { preserveScroll: true, preserveState: true },
        );
    };

    const toggleDisabled = (user: UserRow) => {
        // Reuses the bulk endpoint with a single id — same guards (last
        // active admin, self) apply uniformly across single-row + multi-row
        // toggles.
        router.post(
            '/admin/users/bulk',
            { ids: [user.id], action: user.disabled ? 'enable' : 'disable' },
            { preserveScroll: true, preserveState: true },
        );
    };

    // Selection state for the bulk-action bar. Keyed by row id, kept in a
    // Set so we can cheaply diff against the visible page on toggle-all.
    const [selected, setSelected] = useState<Set<number>>(new Set());
    const [pendingBulk, setPendingBulk] = useState<BulkAction | null>(null);
    const [bulkDeleteConfirm, setBulkDeleteConfirm] = useState(false);

    // Reset selection when the visible page changes (filter / pagination).
    const visibleIdsKey = users.data.map((u) => u.id).join(',');
    useEffect(() => {
        setSelected(new Set());
    }, [visibleIdsKey]);

    const toggleOne = (id: number, on: boolean) => {
        setSelected((prev) => {
            const next = new Set(prev);
            if (on) next.add(id);
            else next.delete(id);
            return next;
        });
    };
    const visibleIds = users.data.map((u) => u.id);
    const allVisibleSelected =
        visibleIds.length > 0 && visibleIds.every((id) => selected.has(id));
    const someVisibleSelected =
        !allVisibleSelected && visibleIds.some((id) => selected.has(id));
    const toggleAll = () => {
        setSelected((prev) => {
            if (visibleIds.every((id) => prev.has(id))) {
                const next = new Set(prev);
                visibleIds.forEach((id) => next.delete(id));
                return next;
            }
            const next = new Set(prev);
            visibleIds.forEach((id) => next.add(id));
            return next;
        });
    };

    const runBulk = (action: BulkAction) => {
        if (selected.size === 0) return;
        router.post(
            '/admin/users/bulk',
            { ids: Array.from(selected), action },
            {
                preserveScroll: true,
                onSuccess: () => {
                    setSelected(new Set());
                    setPendingBulk(null);
                },
                onFinish: () => setPendingBulk(null),
            },
        );
    };

    return (
        <>
            <Head title="Admin · Users" />
            <AdminPageHeader
                title="Users"
                description={
                    isSaas
                        ? 'Search, filter, edit, and impersonate end users.'
                        : 'Manage teammates with access to this install.'
                }
            />
                {flash.success && (
                    <div
                        className="mb-4 rounded-lg border border-success/30 bg-success/5 px-4 py-2 text-sm text-success"
                        data-testid="admin-users-flash-success"
                    >
                        {flash.success}
                    </div>
                )}
                {flash.error && (
                    <div
                        className="mb-4 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-2 text-sm text-destructive"
                        data-testid="admin-users-flash-error"
                    >
                        {flash.error}
                    </div>
                )}

                <div
                    className="mb-3 flex items-center justify-between gap-3 text-[11px] text-muted-foreground"
                    data-testid="admin-users-summary"
                >
                    <span>
                        {users.total === 0
                            ? 'No users match these filters.'
                            : `Showing ${users.data.length} of ${users.total} ${users.total === 1 ? 'user' : 'users'}`}
                    </span>
                    {/* Self-hosted only — the admin invites teammates into the
                        single shared pool (Δ20). SaaS has no platform-wide
                        membership: users self-register (creating their own
                        workspace) or are invited into a workspace by its owner,
                        so a platform-operator "invite" would mint a workspace-
                        less, broken account. */}
                    {!isSaas && (
                        <Button
                            type="button"
                            size="sm"
                            onClick={() => setInviting(true)}
                            data-testid="admin-users-invite-open"
                        >
                            <UserPlus className="h-3.5 w-3.5" strokeWidth={2} />
                            Invite user
                        </Button>
                    )}
                    {/* SaaS: operator-provisioned account = registration-
                        equivalent (user + personal workspace + owner pivot +
                        a set-password email). */}
                    {isSaas && (
                        <Button
                            type="button"
                            size="sm"
                            onClick={() => setCreating(true)}
                            data-testid="admin-users-create-open"
                        >
                            <UserPlus className="h-3.5 w-3.5" strokeWidth={2} />
                            Create user
                        </Button>
                    )}
                </div>

                <form
                    onSubmit={(e) => e.preventDefault()}
                    className="mb-4 flex flex-wrap items-end gap-3"
                    data-testid="admin-users-filters"
                >
                    <div className="flex-1 min-w-[220px]">
                        <label
                            htmlFor="users-q"
                            className="mb-1 block text-[11px] font-medium text-muted-foreground"
                        >
                            Search
                        </label>
                        <Input
                            id="users-q"
                            value={q}
                            onChange={(e) => setQ(e.target.value)}
                            placeholder="Name or email"
                            leadingIcon={<Search strokeWidth={2} />}
                            data-testid="admin-users-q"
                        />
                    </div>
                    <div>
                        <label
                            htmlFor="users-role"
                            className="mb-1 block text-[11px] font-medium text-muted-foreground"
                        >
                            Role
                        </label>
                        <Select
                            id="users-role"
                            value={role}
                            onChange={(e) => setRole(e.target.value as 'all' | 'admin')}
                            data-testid="admin-users-role"
                            wrapperClassName="w-40"
                        >
                            <option value="all">All</option>
                            <option value="admin">Admin only</option>
                        </Select>
                    </div>
                    {isSaas && (
                        <div>
                            <label
                                htmlFor="users-tenant"
                                className="mb-1 block text-[11px] font-medium text-muted-foreground"
                            >
                                Tenant
                            </label>
                            <Select
                                id="users-tenant"
                                value={tenantId}
                                onChange={(e) =>
                                    setTenantId(
                                        e.target.value === ''
                                            ? ''
                                            : Number(e.target.value),
                                    )
                                }
                                data-testid="admin-users-tenant"
                                wrapperClassName="w-44"
                            >
                                <option value="">Any</option>
                                {tenants.map((t) => (
                                    <option key={t.id} value={t.id}>
                                        {t.name}
                                    </option>
                                ))}
                            </Select>
                        </div>
                    )}
                    <Button
                        type="button"
                        variant="ghost"
                        size="sm"
                        onClick={reset}
                        data-testid="admin-users-reset"
                        className="h-10"
                    >
                        Reset
                    </Button>
                </form>

                {selected.size > 0 && (
                    <BulkActionBar
                        count={selected.size}
                        pending={pendingBulk}
                        onAction={(a) => {
                            // Delete is irreversible — gate it behind a
                            // confirm dialog. Other actions are reversible
                            // (demote → promote, disable → enable) so they
                            // fire immediately.
                            if (a === 'delete') {
                                setBulkDeleteConfirm(true);
                                return;
                            }
                            setPendingBulk(a);
                            runBulk(a);
                        }}
                        onClear={() => setSelected(new Set())}
                    />
                )}

                <Card padding="none" className="overflow-hidden">
                    <table className="stagger-rows w-full text-sm">
                        <thead className="border-b border-border bg-muted/30">
                            <tr className="text-left text-[11px] font-medium text-muted-foreground">
                                <th className="w-10 px-4 py-2.5">
                                    <Checkbox
                                        aria-label="Select all on this page"
                                        checked={allVisibleSelected}
                                        ref={(el) => {
                                            if (el)
                                                el.indeterminate = someVisibleSelected;
                                        }}
                                        onChange={toggleAll}
                                        wrapperClassName="align-middle"
                                        data-testid="admin-users-select-all"
                                    />
                                </th>
                                <th className="px-4 py-2.5">Name</th>
                                <th className="px-4 py-2.5">Email</th>
                                <th className="px-4 py-2.5">Role</th>
                                {isSaas && <th className="px-4 py-2.5">Tenant</th>}
                                {isSaas && <th className="px-4 py-2.5">Plan</th>}
                                {isSaas && <th className="px-4 py-2.5 text-right">Projects</th>}
                                <th className="px-4 py-2.5">Joined</th>
                                <th className="px-4 py-2.5 w-12" />
                            </tr>
                        </thead>
                        <tbody>
                            {users.data.length === 0 ? (
                                <tr>
                                    <td
                                        colSpan={isSaas ? 9 : 6}
                                        className="px-4 py-12 text-center text-sm text-muted-foreground"
                                        data-testid="admin-users-empty"
                                    >
                                        No users match these filters.
                                    </td>
                                </tr>
                            ) : (
                                users.data.map((u) => (
                                    <tr
                                        key={u.id}
                                        className={cn(
                                            'border-b border-border/60 last:border-0',
                                            selected.has(u.id) && 'bg-accent/5',
                                            u.disabled && 'opacity-60',
                                        )}
                                        data-testid={`admin-user-row-${u.id}`}
                                    >
                                        <td className="px-4 py-3">
                                            <Checkbox
                                                aria-label={`Select ${u.name}`}
                                                checked={selected.has(u.id)}
                                                onChange={(e) =>
                                                    toggleOne(u.id, e.target.checked)
                                                }
                                                wrapperClassName="align-middle"
                                                data-testid={`admin-user-select-${u.id}`}
                                            />
                                        </td>
                                        <td className="px-4 py-3">
                                            <div className="flex items-center gap-2">
                                                <Link
                                                    href={`/admin/users/${u.id}`}
                                                    className="font-medium text-foreground hover:text-accent"
                                                    data-testid={`admin-user-link-${u.id}`}
                                                >
                                                    {u.name}
                                                </Link>
                                                {u.disabled && (
                                                    <Pill tone="neutral" size="sm">
                                                        Disabled
                                                    </Pill>
                                                )}
                                                {u.pending && (
                                                    <Pill
                                                        tone="neutral"
                                                        size="sm"
                                                        data-testid={`admin-user-pending-${u.id}`}
                                                    >
                                                        Invite sent
                                                    </Pill>
                                                )}
                                            </div>
                                        </td>
                                        <td className="px-4 py-3">
                                            <div className="flex items-center gap-1.5">
                                                <span className="font-mono text-[12px] text-foreground/80">
                                                    {u.email}
                                                </span>
                                                {u.email_verified ? (
                                                    <Tooltip label="Email verified">
                                                        <span
                                                            data-testid={`admin-user-verified-${u.id}`}
                                                            className="inline-flex"
                                                        >
                                                            <CheckCircle2
                                                                className="h-3 w-3 shrink-0 text-success"
                                                                strokeWidth={2.5}
                                                                aria-label="Email verified"
                                                            />
                                                        </span>
                                                    </Tooltip>
                                                ) : (
                                                    <Tooltip label="Email not verified">
                                                        <span
                                                            data-testid={`admin-user-unverified-${u.id}`}
                                                            className="inline-flex"
                                                        >
                                                            <MailWarning
                                                                className="h-3 w-3 shrink-0 text-destructive"
                                                                strokeWidth={2}
                                                                aria-label="Email not verified"
                                                            />
                                                        </span>
                                                    </Tooltip>
                                                )}
                                            </div>
                                        </td>
                                        <td className="px-4 py-3">
                                            {u.is_admin ? (
                                                <Pill tone="accent" size="sm">
                                                    Admin
                                                </Pill>
                                            ) : (
                                                <span className="text-[11px] text-muted-foreground">
                                                    User
                                                </span>
                                            )}
                                        </td>
                                        {isSaas && (
                                            <td className="px-4 py-3 text-foreground/80">
                                                {u.tenant ? (
                                                    <Link
                                                        href={`/admin/tenants/${u.tenant.id}`}
                                                        className="hover:text-accent"
                                                    >
                                                        {u.tenant.name}
                                                    </Link>
                                                ) : (
                                                    <span className="text-muted-foreground">—</span>
                                                )}
                                            </td>
                                        )}
                                        {isSaas && (
                                            <td className="px-4 py-3">
                                                <Pill
                                                    tone={PLAN_TONES[u.plan_key] ?? 'neutral'}
                                                    size="sm"
                                                >
                                                    {u.plan_key.charAt(0).toUpperCase() + u.plan_key.slice(1)}
                                                </Pill>
                                            </td>
                                        )}
                                        {isSaas && (
                                            <td className="px-4 py-3 text-right tabular-nums text-foreground/80">
                                                {u.projects_count}
                                            </td>
                                        )}
                                        <td className="px-4 py-3 text-[12px] text-muted-foreground">
                                            {relativeTime(u.created_at)}
                                        </td>
                                        <td className="px-4 py-3">
                                            <RowMenu
                                                user={u}
                                                isSelf={auth.user?.id === u.id}
                                                showImpersonate={isSaas}
                                                onImpersonate={() =>
                                                    impersonate(u)
                                                }
                                                onToggleAdmin={() => toggleAdmin(u)}
                                                onToggleDisabled={() =>
                                                    toggleDisabled(u)
                                                }
                                                onDelete={() => setDeleting(u)}
                                            />
                                        </td>
                                    </tr>
                                ))
                            )}
                        </tbody>
                    </table>
                </Card>

                {users.last_page > 1 && (
                    <Pagination paginator={users} />
                )}

            {inviting && (
                <InviteUserModal onClose={() => setInviting(false)} />
            )}

            {creating && (
                <CreateUserModal onClose={() => setCreating(false)} />
            )}

            {deleting && (
                <DeleteUserModal
                    user={deleting}
                    onClose={() => setDeleting(null)}
                />
            )}

            {bulkDeleteConfirm && (
                <BulkDeleteConfirm
                    count={selected.size}
                    sample={users.data
                        .filter((u) => selected.has(u.id))
                        .slice(0, 5)
                        .map((u) => u.email)}
                    pending={pendingBulk === 'delete'}
                    onCancel={() => setBulkDeleteConfirm(false)}
                    onConfirm={() => {
                        setPendingBulk('delete');
                        setBulkDeleteConfirm(false);
                        runBulk('delete');
                    }}
                />
            )}
        </>
    );
}

AdminUsersIndex.layout = adminLayout;

export default AdminUsersIndex;

function BulkDeleteConfirm({
    count,
    sample,
    pending,
    onCancel,
    onConfirm,
}: {
    count: number;
    sample: string[];
    pending: boolean;
    onCancel: () => void;
    onConfirm: () => void;
}) {
    const remainder = count - sample.length;

    return (
        <Modal
            open
            onClose={onCancel}
            size="md"
            closeOnBackdrop={false}
            closeOnEsc={!pending}
            data-testid="admin-users-bulk-delete-modal"
        >
            <Modal.Header
                eyebrow={`Delete ${count} ${count === 1 ? 'user' : 'users'}`}
                eyebrowClassName="text-destructive"
                title="This can't be undone"
            />
            <Modal.Body fade={false}>
                <p className="text-sm text-muted-foreground">
                    Hard-removes the account and its tenant — projects,
                    screens, uploaded media, and past exports all go
                    with it. <strong>Subscriptions and invoices are
                    kept</strong> for accounting (the email is snapshot
                    onto each). The acting admin and the last remaining
                    admin are skipped.
                </p>

                {sample.length > 0 && (
                    <ul className="max-h-40 overflow-y-auto rounded-md border border-border bg-muted/30 px-3 py-2 font-mono text-[11px] text-foreground/80">
                        {sample.map((email) => (
                            <li key={email} className="truncate">
                                {email}
                            </li>
                        ))}
                        {remainder > 0 && (
                            <li className="text-muted-foreground">
                                + {remainder} more
                            </li>
                        )}
                    </ul>
                )}
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    onClick={onCancel}
                    disabled={pending}
                    data-testid="admin-users-bulk-delete-cancel"
                >
                    Cancel
                </Button>
                <Button
                    type="button"
                    variant="danger"
                    onClick={onConfirm}
                    disabled={pending}
                    data-testid="admin-users-bulk-delete-confirm"
                >
                    {pending ? 'Deleting…' : `Delete ${count}`}
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

function BulkActionBar({
    count,
    pending,
    onAction,
    onClear,
}: {
    count: number;
    pending: BulkAction | null;
    onAction: (a: BulkAction) => void;
    onClear: () => void;
}) {
    const disabled = pending !== null;
    return (
        <div
            className="mb-3 flex flex-wrap items-center gap-3 rounded-lg border border-accent/30 bg-accent/5 px-4 py-2 text-sm"
            data-testid="admin-users-bulk-bar"
        >
            <span className="font-medium text-foreground">
                {count} selected
            </span>
            <span className="ml-auto flex flex-wrap items-center gap-1">
                <BulkBtn
                    label={pending === 'make_admin' ? 'Promoting…' : 'Make admin'}
                    onClick={() => onAction('make_admin')}
                    disabled={disabled}
                    testId="admin-users-bulk-make-admin"
                />
                <BulkBtn
                    label={pending === 'demote' ? 'Demoting…' : 'Demote'}
                    onClick={() => onAction('demote')}
                    disabled={disabled}
                    testId="admin-users-bulk-demote"
                />
                <BulkBtn
                    label={pending === 'disable' ? 'Disabling…' : 'Disable'}
                    onClick={() => onAction('disable')}
                    disabled={disabled}
                    testId="admin-users-bulk-disable"
                />
                <BulkBtn
                    label={pending === 'enable' ? 'Enabling…' : 'Enable'}
                    onClick={() => onAction('enable')}
                    disabled={disabled}
                    testId="admin-users-bulk-enable"
                />
                <BulkBtn
                    label={pending === 'delete' ? 'Deleting…' : 'Delete'}
                    onClick={() => onAction('delete')}
                    disabled={disabled}
                    tone="danger"
                    testId="admin-users-bulk-delete"
                />
                <button
                    type="button"
                    onClick={onClear}
                    className="ml-1 rounded px-2 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground"
                    data-testid="admin-users-bulk-clear"
                >
                    Clear
                </button>
            </span>
        </div>
    );
}

function BulkBtn({
    label,
    onClick,
    disabled,
    tone = 'default',
    testId,
}: {
    label: string;
    onClick: () => void;
    disabled?: boolean;
    tone?: 'default' | 'danger';
    testId?: string;
}) {
    return (
        <button
            type="button"
            onClick={onClick}
            disabled={disabled}
            data-testid={testId}
            className={cn(
                'rounded-md border px-2.5 py-1 text-[11px] font-medium transition-colors disabled:opacity-40',
                tone === 'danger'
                    ? 'border-destructive/40 text-destructive hover:bg-destructive/10'
                    : 'border-border text-foreground hover:bg-muted',
            )}
        >
            {label}
        </button>
    );
}

function RowMenu({
    user,
    isSelf,
    showImpersonate,
    onImpersonate,
    onToggleAdmin,
    onToggleDisabled,
    onDelete,
}: {
    user: UserRow;
    isSelf: boolean;
    showImpersonate: boolean;
    onImpersonate: () => void;
    onToggleAdmin: () => void;
    onToggleDisabled: () => void;
    onDelete: () => void;
}) {
    const stop = (e: ReactMouseEvent) => {
        e.preventDefault();
        e.stopPropagation();
    };
    return (
        <div onClick={stop} onMouseDown={stop} className="flex justify-end">
            <Popover
                align="end"
                trigger={({ open, toggle }) => (
                    <button
                        type="button"
                        onClick={toggle}
                        aria-label="User actions"
                        aria-expanded={open}
                        data-testid={`admin-user-menu-${user.id}`}
                        className={cn(
                            'inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground',
                            open && 'bg-muted text-foreground',
                        )}
                    >
                        <MoreHorizontal className="h-4 w-4" strokeWidth={2} />
                    </button>
                )}
            >
                <div className="flex min-w-[160px] flex-col">
                    <Link
                        href={`/admin/users/${user.id}`}
                        className="rounded-md px-2 py-1.5 text-left text-sm text-foreground hover:bg-muted"
                        data-testid={`admin-user-view-${user.id}`}
                    >
                        View detail
                    </Link>
                    {showImpersonate && !user.is_admin && !isSelf && (
                        <button
                            type="button"
                            onClick={onImpersonate}
                            data-testid={`admin-user-impersonate-${user.id}`}
                            className="rounded-md px-2 py-1.5 text-left text-sm text-foreground hover:bg-muted"
                        >
                            Impersonate
                        </button>
                    )}
                    {!isSelf && (
                        <button
                            type="button"
                            onClick={onToggleAdmin}
                            data-testid={`admin-user-toggle-admin-${user.id}`}
                            className="rounded-md px-2 py-1.5 text-left text-sm text-foreground hover:bg-muted"
                        >
                            {user.is_admin ? 'Demote to user' : 'Make admin'}
                        </button>
                    )}
                    {!isSelf && (
                        <button
                            type="button"
                            onClick={onToggleDisabled}
                            data-testid={`admin-user-toggle-disabled-${user.id}`}
                            className="rounded-md px-2 py-1.5 text-left text-sm text-foreground hover:bg-muted"
                        >
                            {user.disabled ? 'Enable account' : 'Disable account'}
                        </button>
                    )}
                    {!isSelf && (
                        <button
                            type="button"
                            onClick={onDelete}
                            data-testid={`admin-user-delete-${user.id}`}
                            className="rounded-md px-2 py-1.5 text-left text-sm text-destructive hover:bg-destructive/10"
                        >
                            Delete
                        </button>
                    )}
                </div>
            </Popover>
        </div>
    );
}

function Pagination<T>({ paginator }: { paginator: Paginated<T> }) {
    return (
        <nav
            className="mt-4 flex items-center justify-between"
            data-testid="admin-users-pagination"
        >
            <span className="text-[11px] text-muted-foreground">
                Page {paginator.current_page} of {paginator.last_page} ·{' '}
                {paginator.total} total
            </span>
            <div className="flex flex-wrap gap-1">
                {paginator.links.map((link, i) => (
                    <button
                        key={`${link.label}-${i}`}
                        type="button"
                        disabled={!link.url}
                        onClick={() => link.url && router.visit(link.url)}
                        className={cn(
                            'rounded-md border border-border px-2.5 py-1 text-[11px] font-medium transition-colors disabled:opacity-40',
                            link.active
                                ? 'border-accent/40 bg-accent/5 text-accent'
                                : 'text-muted-foreground hover:bg-muted hover:text-foreground',
                        )}
                        // The Laravel paginator embeds HTML arrows in labels;
                        // we render them as text — safe because the labels
                        // are server-controlled, not user-supplied.
                        dangerouslySetInnerHTML={{ __html: link.label }}
                    />
                ))}
            </div>
        </nav>
    );
}

type InviteForm = {
    name: string;
    email: string;
    role: 'editor' | 'viewer';
    is_admin: boolean;
};

function InviteUserModal({ onClose }: { onClose: () => void }) {
    const { data, setData, post, processing, errors, reset } = useForm<InviteForm>({
        name: '',
        email: '',
        role: 'editor',
        is_admin: false,
    });

    const submit = (e: ReactSubmitEvent<HTMLFormElement>) => {
        e.preventDefault();
        post('/admin/users/invite', {
            preserveScroll: true,
            onSuccess: () => {
                reset();
                onClose();
            },
        });
    };

    return (
        <Modal
            open
            onClose={onClose}
            size="md"
            onSubmit={submit}
            closeOnBackdrop={false}
            closeOnEsc={!processing}
            data-testid="admin-users-invite-modal"
        >
            <Modal.Header
                eyebrow="Invite user"
                eyebrowClassName="text-accent"
                title="Add a teammate"
                subtitle="They'll get an email with a link to set a password and join. Accepting the link also confirms their email."
            />
            <Modal.Body fade={false}>
                <Field
                    id="invite-name"
                    label="Name"
                    value={data.name}
                    onChange={(e) => setData('name', e.target.value)}
                    error={errors.name}
                    required
                    autoFocus
                    data-testid="admin-users-invite-name"
                />
                <Field
                    id="invite-email"
                    label="Email"
                    type="email"
                    value={data.email}
                    onChange={(e) => setData('email', e.target.value)}
                    error={errors.email}
                    required
                    data-testid="admin-users-invite-email"
                />
                <div>
                    <label
                        htmlFor="invite-role"
                        className="mb-1 block text-[11px] font-medium text-muted-foreground"
                    >
                        Role
                    </label>
                    <Select
                        id="invite-role"
                        value={data.role}
                        onChange={(e) =>
                            setData('role', e.target.value as 'editor' | 'viewer')
                        }
                        data-testid="admin-users-invite-role"
                    >
                        <option value="editor">Editor — create &amp; edit projects</option>
                        <option value="viewer">Viewer — view &amp; export only</option>
                    </Select>
                    {errors.role && (
                        <p className="mt-1 text-xs text-destructive">
                            {errors.role}
                        </p>
                    )}
                </div>
                <label className="flex items-center gap-2 text-sm text-foreground">
                    <Checkbox
                        checked={data.is_admin}
                        onChange={(e) => setData('is_admin', e.target.checked)}
                        data-testid="admin-users-invite-is-admin"
                    />
                    Grant admin access
                </label>
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    onClick={onClose}
                    disabled={processing}
                    data-testid="admin-users-invite-cancel"
                >
                    Cancel
                </Button>
                <Button
                    type="submit"
                    disabled={processing}
                    data-testid="admin-users-invite-submit"
                >
                    {processing ? 'Sending…' : 'Send invitation'}
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

type CreateForm = {
    name: string;
    email: string;
    is_admin: boolean;
};

/**
 * SaaS operator-provisioned account. Unlike the self-hosted invite (which
 * picks an editor/viewer pool role), this creates a workspace OWNER — so there's
 * no role picker. Posts to /admin/users; the server provisions the personal
 * workspace + owner pivot and emails a set-password link.
 */
function CreateUserModal({ onClose }: { onClose: () => void }) {
    const { data, setData, post, processing, errors, reset } = useForm<CreateForm>({
        name: '',
        email: '',
        is_admin: false,
    });

    const submit = (e: ReactSubmitEvent<HTMLFormElement>) => {
        e.preventDefault();
        post('/admin/users', {
            preserveScroll: true,
            onSuccess: () => {
                reset();
                onClose();
            },
        });
    };

    return (
        <Modal
            open
            onClose={onClose}
            size="md"
            onSubmit={submit}
            closeOnBackdrop={false}
            closeOnEsc={!processing}
            data-testid="admin-users-create-modal"
        >
            <Modal.Header
                eyebrow="Create user"
                eyebrowClassName="text-accent"
                title="Add an account"
                subtitle="Creates the user with their own workspace and emails a link to set a password. The link also confirms their email."
            />
            <Modal.Body fade={false}>
                <Field
                    id="create-name"
                    label="Name"
                    value={data.name}
                    onChange={(e) => setData('name', e.target.value)}
                    error={errors.name}
                    maxLength={120}
                    required
                    autoFocus
                    data-testid="admin-users-create-name"
                />
                <Field
                    id="create-email"
                    label="Email"
                    type="email"
                    value={data.email}
                    onChange={(e) => setData('email', e.target.value)}
                    error={errors.email}
                    maxLength={191}
                    required
                    data-testid="admin-users-create-email"
                />
                <label className="flex items-center gap-2 text-sm text-foreground">
                    <Checkbox
                        checked={data.is_admin}
                        onChange={(e) => setData('is_admin', e.target.checked)}
                        data-testid="admin-users-create-is-admin"
                    />
                    Grant admin access
                </label>
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    onClick={onClose}
                    disabled={processing}
                    data-testid="admin-users-create-cancel"
                >
                    Cancel
                </Button>
                <Button
                    type="submit"
                    disabled={processing}
                    data-testid="admin-users-create-submit"
                >
                    {processing ? 'Creating…' : 'Create user'}
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

function DeleteUserModal({
    user,
    onClose,
}: {
    user: UserRow;
    onClose: () => void;
}) {
    const [submitting, setSubmitting] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const confirm = () => {
        setSubmitting(true);
        setError(null);
        router.delete(`/admin/users/${user.id}`, {
            preserveScroll: true,
            onError: (errs) => {
                setError(errs.user ?? 'Delete failed.');
            },
            onFinish: () => setSubmitting(false),
            onSuccess: () => onClose(),
        });
    };

    return (
        <Modal
            open
            onClose={onClose}
            size="md"
            closeOnBackdrop={false}
            closeOnEsc={!submitting}
            data-testid="admin-user-delete-modal"
        >
            <Modal.Header
                eyebrow="Delete user"
                eyebrowClassName="text-destructive"
                title={user.name}
            />
            <Modal.Body fade={false}>
                <p className="text-sm text-muted-foreground">
                    Hard-removes {user.email} and the tenant they own —
                    projects, screens, uploaded media, and past exports
                    all go with it. <strong>Subscriptions and invoices
                    are kept</strong> for accounting; the email is
                    snapshotted onto each so the row stays attributable.
                    This can't be undone.
                </p>
                {error && (
                    <p
                        className="text-xs text-destructive"
                        data-testid="admin-user-delete-error"
                    >
                        {error}
                    </p>
                )}
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    onClick={onClose}
                    disabled={submitting}
                >
                    Cancel
                </Button>
                <Button
                    type="button"
                    variant="danger"
                    disabled={submitting}
                    onClick={confirm}
                    data-testid="admin-user-delete-confirm"
                >
                    {submitting ? 'Deleting…' : 'Delete'}
                </Button>
            </Modal.Footer>
        </Modal>
    );
}
