'use client'

import { useState, useCallback, useEffect, useMemo } from 'react'
import Link from 'next/link'
import { useSearchParams, usePathname } from 'next/navigation'
import { trpc } from '@/lib/trpc/client'
import { Badge } from '@/components/ui/badge'
import { CountryDisplay } from '@/components/shared/country-display'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Checkbox } from '@/components/ui/checkbox'
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Skeleton } from '@/components/ui/skeleton'
import { UserAvatar } from '@/components/shared/user-avatar'
import { UserActions, UserMobileActions } from '@/components/admin/user-actions'
import { Pagination } from '@/components/shared/pagination'
import { SortableHeader } from '@/components/shared/sortable-header'
import { Plus, Users, Search, Mail, Loader2, X, Send } from 'lucide-react'
import { toast } from 'sonner'
import { formatRelativeTime } from '@/lib/utils'
import { AnimatePresence, motion } from 'motion/react'
type RoleValue = 'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER'

type TabKey = 'all' | 'jury' | 'mentors' | 'observers' | 'admins' | 'applicants'

const TAB_ROLES: Record<TabKey, RoleValue[] | undefined> = {
  all: undefined,
  jury: ['JURY_MEMBER'],
  mentors: ['MENTOR'],
  observers: ['OBSERVER'],
  admins: ['SUPER_ADMIN', 'PROGRAM_ADMIN'],
  applicants: undefined, // handled separately
}

const statusColors: Record<string, 'default' | 'success' | 'secondary' | 'destructive'> = {
  NONE: 'secondary',
  ACTIVE: 'success',
  INVITED: 'secondary',
  SUSPENDED: 'destructive',
}

const statusLabels: Record<string, string> = {
  NONE: 'Not Invited',
  INVITED: 'Invited',
  ACTIVE: 'Active',
  SUSPENDED: 'Suspended',
}

const roleColors: Record<string, 'default' | 'outline' | 'secondary'> = {
  JURY_MEMBER: 'default',
  MENTOR: 'secondary',
  OBSERVER: 'outline',
  PROGRAM_ADMIN: 'default',
  SUPER_ADMIN: 'destructive' as 'default',
}

function InlineSendInvite({ userId, userEmail }: { userId: string; userEmail: string }) {
  const utils = trpc.useUtils()
  const sendInvitation = trpc.user.sendInvitation.useMutation({
    onSuccess: () => {
      toast.success(`Invitation sent to ${userEmail}`)
      utils.user.list.invalidate()
    },
    onError: (error) => {
      toast.error(error.message || 'Failed to send invitation')
    },
  })

  return (
    <Button
      variant="outline"
      size="sm"
      className="h-6 text-xs gap-1 px-2"
      onClick={(e) => {
        e.stopPropagation()
        sendInvitation.mutate({ userId })
      }}
      disabled={sendInvitation.isPending}
    >
      {sendInvitation.isPending ? (
        <Loader2 className="h-3 w-3 animate-spin" />
      ) : (
        <Mail className="h-3 w-3" />
      )}
      Send Invite
    </Button>
  )
}

export function MembersContent() {
  const searchParams = useSearchParams()

  const pathname = usePathname()

  const tab = (searchParams.get('tab') as TabKey) || 'all'
  const search = searchParams.get('search') || ''
  const page = parseInt(searchParams.get('page') || '1', 10)

  const [searchInput, setSearchInput] = useState(search)

  // Debounced search
  useEffect(() => {
    const timer = setTimeout(() => {
      updateParams({ search: searchInput || null, page: '1' })
    }, 300)
    return () => clearTimeout(timer)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchInput])

  const updateParams = useCallback(
    (updates: Record<string, string | null>) => {
      const params = new URLSearchParams(searchParams.toString())
      Object.entries(updates).forEach(([key, value]) => {
        if (value === null || value === '') {
          params.delete(key)
        } else {
          params.set(key, value)
        }
      })
      window.history.replaceState(null, '', `${pathname}?${params.toString()}`)
    },
    [searchParams, pathname]
  )

  const roles = TAB_ROLES[tab]

  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
  const [sortBy, setSortBy] = useState<string | undefined>(undefined)
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc')

  const handleSort = (column: string) => {
    if (sortBy === column) {
      setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))
    } else {
      setSortBy(column)
      setSortDir('asc')
    }
    updateParams({ page: '1' })
  }

  const { data: currentUser } = trpc.user.me.useQuery()
  const currentUserRole = currentUser?.role as RoleValue | undefined

  const { data, isLoading } = trpc.user.list.useQuery({
    roles: roles,
    search: search || undefined,
    page,
    perPage: 20,
    sortBy: sortBy as 'name' | 'email' | 'role' | 'status' | 'lastLoginAt' | 'createdAt' | undefined,
    sortDir: sortBy ? sortDir : undefined,
  })

  const invitableIdsQuery = trpc.user.listInvitableIds.useQuery(
    {
      roles: roles,
      search: search || undefined,
    },
    { enabled: false }
  )

  const utils = trpc.useUtils()

  const bulkInvite = trpc.user.bulkSendInvitations.useMutation({
    onSuccess: (result) => {
      const { sent, errors } = result as { sent: number; skipped: number; errors: string[] }
      if (errors && errors.length > 0) {
        toast.warning(`Sent ${sent} invitation${sent !== 1 ? 's' : ''}, ${errors.length} failed`)
      } else {
        toast.success(`Invitations sent to ${sent} member${sent !== 1 ? 's' : ''}`)
      }
      setSelectedIds(new Set())
      utils.user.list.invalidate()
    },
    onError: (error) => {
      toast.error(error.message || 'Failed to send invitations')
    },
  })

  const bulkUpdateRoles = trpc.user.bulkUpdateRoles.useMutation({
    onSuccess: (r) => {
      const parts: string[] = []
      if (r.updated > 0) parts.push(`Updated ${r.updated} user${r.updated === 1 ? '' : 's'}`)
      if (r.alreadyHadRole > 0) parts.push(`${r.alreadyHadRole} already had role`)
      toast.success(parts.join(' · ') || 'No changes')
      setSelectedIds(new Set())
      utils.user.list.invalidate()
    },
    onError: (error) => {
      toast.error(error.message || 'Failed to update roles')
    },
  })

  const selectableUsers = useMemo(
    () => data?.users ?? [],
    [data?.users]
  )

  const allSelectableSelected =
    selectableUsers.length > 0 && selectableUsers.every((u) => selectedIds.has(u.id))

  const someSelectableSelected =
    selectableUsers.some((u) => selectedIds.has(u.id)) && !allSelectableSelected

  const toggleUser = useCallback((userId: string) => {
    setSelectedIds((prev) => {
      const next = new Set(prev)
      if (next.has(userId)) {
        next.delete(userId)
      } else {
        next.add(userId)
      }
      return next
    })
  }, [])

  const toggleAll = useCallback(() => {
    if (allSelectableSelected) {
      // Deselect all on this page
      setSelectedIds((prev) => {
        const next = new Set(prev)
        for (const u of selectableUsers) {
          next.delete(u.id)
        }
        return next
      })
    } else {
      // Select all selectable on this page
      setSelectedIds((prev) => {
        const next = new Set(prev)
        for (const u of selectableUsers) {
          next.add(u.id)
        }
        return next
      })
    }
  }, [allSelectableSelected, selectableUsers])

  const selectAllMatching = useCallback(async () => {
    const result = await invitableIdsQuery.refetch()
    const ids = result.data?.userIds ?? []
    if (ids.length === 0) {
      toast.info('No invitable members match the current filter')
      return
    }
    setSelectedIds(new Set(ids))
    toast.success(`Selected ${ids.length} matching members`)
  }, [invitableIdsQuery])

  const clearSelection = useCallback(() => {
    setSelectedIds(new Set())
  }, [])

  const handleTabChange = (value: string) => {
    updateParams({ tab: value === 'all' ? null : value, page: '1' })
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex items-center justify-between flex-wrap gap-4">
        <div className="min-w-0">
          <h1 className="text-2xl font-semibold tracking-tight">Members</h1>
          <p className="text-muted-foreground">
            Manage jury members, mentors, observers, and admins
          </p>
        </div>
        <Button asChild className="w-full sm:w-auto shrink-0">
          <Link href="/admin/members/invite">
            <Plus className="mr-2 h-4 w-4" />
            Add Member
          </Link>
        </Button>
      </div>

      {/* Tabs */}
      <Tabs value={tab} onValueChange={handleTabChange}>
        <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          <TabsList>
            <TabsTrigger value="all">All</TabsTrigger>
            <TabsTrigger value="jury">Jury</TabsTrigger>
            <TabsTrigger value="mentors">Mentors</TabsTrigger>
            <TabsTrigger value="observers">Observers</TabsTrigger>
            <TabsTrigger value="admins">Admins</TabsTrigger>
            <TabsTrigger value="applicants">Applicants</TabsTrigger>
          </TabsList>

          {/* Search */}
          <div className="relative w-full sm:w-64">
            <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              placeholder="Search by name or email..."
              value={searchInput}
              onChange={(e) => setSearchInput(e.target.value)}
              className="pl-9"
            />
          </div>
        </div>
      </Tabs>

      {/* Applicants tab */}
      {tab === 'applicants' && <ApplicantsTabContent search={search} searchInput={searchInput} setSearchInput={setSearchInput} />}

      {/* Content (non-applicant tabs) */}
      {tab !== 'applicants' && isLoading ? (
        <MembersSkeleton />
      ) : data && data.users.length > 0 ? (
        <>
          {/* Top Pagination */}
          {data.totalPages > 1 && (
            <Pagination
              page={page}
              totalPages={data.totalPages}
              total={data.total}
              perPage={data.perPage}
              onPageChange={(newPage) => updateParams({ page: String(newPage) })}
            />
          )}

          {/* Bulk selection controls */}
          <Card>
            <CardContent className="py-3 flex flex-wrap items-center justify-between gap-2">
              <p className="text-sm text-muted-foreground">
                {selectedIds.size > 0
                  ? `${selectedIds.size} selected. Selection persists across pages and filters.`
                  : 'Selection persists across pages and filters.'}
              </p>
              <div className="flex items-center gap-2">
                {selectedIds.size > 0 && (
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() =>
                      bulkUpdateRoles.mutate({
                        userIds: Array.from(selectedIds),
                        addRole: 'MENTOR',
                      })
                    }
                    disabled={bulkUpdateRoles.isPending}
                  >
                    {bulkUpdateRoles.isPending ? (
                      <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                    ) : null}
                    Add MENTOR role
                  </Button>
                )}
                <Button
                  variant="outline"
                  size="sm"
                  onClick={selectAllMatching}
                  disabled={invitableIdsQuery.isFetching}
                >
                  {invitableIdsQuery.isFetching ? (
                    <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                  ) : null}
                  Select all matching
                </Button>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={clearSelection}
                  disabled={selectedIds.size === 0}
                >
                  Clear selection
                </Button>
              </div>
            </CardContent>
          </Card>

          {/* Desktop table */}
          <Card className="hidden md:block">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead className="w-10">
                    {selectableUsers.length > 0 && (
                      <Checkbox
                        checked={allSelectableSelected ? true : someSelectableSelected ? 'indeterminate' : false}
                        onCheckedChange={toggleAll}
                        aria-label="Select all members"
                      />
                    )}
                  </TableHead>
                  <SortableHeader label="Member" column="name" currentSort={sortBy} currentDir={sortDir} onSort={handleSort} />
                  <SortableHeader label="Role" column="role" currentSort={sortBy} currentDir={sortDir} onSort={handleSort} />
                  <TableHead>Expertise</TableHead>
                  <TableHead>Assignments</TableHead>
                  <SortableHeader label="Status" column="status" currentSort={sortBy} currentDir={sortDir} onSort={handleSort} />
                  <SortableHeader label="Last Login" column="lastLoginAt" currentSort={sortBy} currentDir={sortDir} onSort={handleSort} />
                  <TableHead className="text-right">Actions</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {data.users.map((user) => (
                  <TableRow key={user.id}>
                    <TableCell>
                      <Checkbox
                        checked={selectedIds.has(user.id)}
                        onCheckedChange={() => toggleUser(user.id)}
                        aria-label={`Select ${user.name || user.email}`}
                      />
                    </TableCell>
                    <TableCell>
                      <Link href={`/admin/members/${user.id}`} className="flex items-center gap-3 hover:opacity-80">
                        <UserAvatar
                          user={user}
                          avatarUrl={(user as Record<string, unknown>).avatarUrl as string | undefined}
                          size="sm"
                        />
                        <div>
                          <p className="font-medium hover:underline">{user.name || 'Unnamed'}</p>
                          <p className="text-sm text-muted-foreground">
                            {user.email}
                          </p>
                        </div>
                      </Link>
                    </TableCell>
                    <TableCell>
                      <div className="flex flex-wrap gap-1">
                        {((user as unknown as { roles?: string[] }).roles?.length
                          ? (user as unknown as { roles: string[] }).roles
                          : [user.role]
                        ).map((r) => (
                          <Badge key={r} variant={roleColors[r] || 'secondary'}>
                            {r.replace(/_/g, ' ')}
                          </Badge>
                        ))}
                      </div>
                    </TableCell>
                    <TableCell>
                      {user.expertiseTags && user.expertiseTags.length > 0 ? (
                        <div className="flex flex-wrap gap-1">
                          {user.expertiseTags.slice(0, 2).map((tag) => (
                            <Badge key={tag} variant="outline" className="text-xs">
                              {tag}
                            </Badge>
                          ))}
                          {user.expertiseTags.length > 2 && (
                            <Badge variant="outline" className="text-xs">
                              +{user.expertiseTags.length - 2}
                            </Badge>
                          )}
                        </div>
                      ) : (
                        <span className="text-sm text-muted-foreground">-</span>
                      )}
                    </TableCell>
                    <TableCell>
                      <div>
                        {user.role === 'APPLICANT' ? (
                          (() => {
                            const info = (user as unknown as { applicantRoundInfo?: { projectName: string; roundName: string; state: string } | null }).applicantRoundInfo
                            if (!info) return <span className="text-sm text-muted-foreground">-</span>
                            const stateColor = info.state === 'REJECTED' ? 'destructive' as const
                              : info.state === 'WITHDRAWN' ? 'secondary' as const
                              : info.state === 'PASSED' ? 'success' as const
                              : 'outline' as const
                            const stateLabel = info.state === 'REJECTED' ? 'Rejected'
                              : info.state === 'WITHDRAWN' ? 'Withdrawn'
                              : info.state === 'PASSED' ? 'Passed'
                              : info.roundName
                            return (
                              <div className="flex flex-col gap-0.5">
                                <span className="text-sm font-medium truncate max-w-[200px]">{info.projectName}</span>
                                <Badge variant={stateColor} className="w-fit text-[10px] px-1.5 py-0">
                                  {stateLabel}
                                </Badge>
                              </div>
                            )
                          })()
                        ) : user.role === 'MENTOR' ? (
                          <p>{(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.mentorAssignments} mentored</p>
                        ) : (
                          <p>{(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.assignments} assigned</p>
                        )}
                      </div>
                    </TableCell>
                    <TableCell>
                      <div className="flex items-center gap-2">
                        <Badge variant={statusColors[user.status] || 'secondary'}>
                          {statusLabels[user.status] || user.status}
                        </Badge>
                        {user.status === 'NONE' && (
                          <InlineSendInvite userId={user.id} userEmail={user.email} />
                        )}
                      </div>
                    </TableCell>
                    <TableCell>
                      {user.lastLoginAt ? (
                        <span title={new Date(user.lastLoginAt).toLocaleString()}>
                          {formatRelativeTime(user.lastLoginAt)}
                        </span>
                      ) : (
                        <span className="text-muted-foreground">Never</span>
                      )}
                    </TableCell>
                    <TableCell className="text-right">
                      <UserActions
                        userId={user.id}
                        userEmail={user.email}
                        userStatus={user.status}
                        userRole={user.role as RoleValue}
                        userRoles={(user as unknown as { roles?: RoleValue[] }).roles}
                        currentUserRole={currentUserRole}
                      />
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </Card>

          {/* Mobile cards */}
          <div className="space-y-4 md:hidden">
            {data.users.map((user) => (
              <Card key={user.id}>
                <CardHeader className="pb-3">
                  <div className="flex items-start justify-between">
                    <div className="flex items-center gap-3">
                      <Checkbox
                        checked={selectedIds.has(user.id)}
                        onCheckedChange={() => toggleUser(user.id)}
                        aria-label={`Select ${user.name || user.email}`}
                        className="mt-1"
                      />
                      <UserAvatar
                        user={user}
                        avatarUrl={(user as Record<string, unknown>).avatarUrl as string | undefined}
                        size="md"
                      />
                      <Link href={`/admin/members/${user.id}`}>
                        <CardTitle className="text-base hover:underline">
                          {user.name || 'Unnamed'}
                        </CardTitle>
                        <CardDescription className="text-xs">
                          {user.email}
                        </CardDescription>
                      </Link>
                    </div>
                    <div className="flex flex-col items-end gap-1.5">
                      <Badge variant={statusColors[user.status] || 'secondary'}>
                        {statusLabels[user.status] || user.status}
                      </Badge>
                      {user.status === 'NONE' && (
                        <InlineSendInvite userId={user.id} userEmail={user.email} />
                      )}
                    </div>
                  </div>
                </CardHeader>
                <CardContent className="space-y-3">
                  <div className="flex items-center justify-between text-sm">
                    <span className="text-muted-foreground">Role</span>
                    <div className="flex flex-wrap gap-1 justify-end">
                      {((user as unknown as { roles?: string[] }).roles?.length
                        ? (user as unknown as { roles: string[] }).roles
                        : [user.role]
                      ).map((r) => (
                        <Badge key={r} variant={roleColors[r] || 'secondary'}>
                          {r.replace(/_/g, ' ')}
                        </Badge>
                      ))}
                    </div>
                  </div>
                  <div className="flex items-center justify-between text-sm">
                    <span className="text-muted-foreground">
                      {user.role === 'APPLICANT' ? 'Project' : 'Assignments'}
                    </span>
                    <span>
                      {user.role === 'APPLICANT' ? (
                        (() => {
                          const info = (user as unknown as { applicantRoundInfo?: { projectName: string; roundName: string; state: string } | null }).applicantRoundInfo
                          if (!info) return <span className="text-muted-foreground">-</span>
                          const stateColor = info.state === 'REJECTED' ? 'destructive' as const
                            : info.state === 'WITHDRAWN' ? 'secondary' as const
                            : info.state === 'PASSED' ? 'success' as const
                            : 'outline' as const
                          const stateLabel = info.state === 'REJECTED' ? 'Rejected'
                            : info.state === 'WITHDRAWN' ? 'Withdrawn'
                            : info.state === 'PASSED' ? 'Passed'
                            : info.roundName
                          return (
                            <span className="flex flex-col items-end gap-0.5">
                              <span className="truncate max-w-[160px]">{info.projectName}</span>
                              <Badge variant={stateColor} className="text-[10px] px-1.5 py-0">
                                {stateLabel}
                              </Badge>
                            </span>
                          )
                        })()
                      ) : user.role === 'MENTOR'
                        ? `${(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.mentorAssignments} mentored`
                        : `${(user as unknown as { _count: { mentorAssignments: number; assignments: number } })._count.assignments} assigned`}
                    </span>
                  </div>
                  <div className="flex items-center justify-between text-sm">
                    <span className="text-muted-foreground">Last Login</span>
                    <span>
                      {user.lastLoginAt ? (
                        formatRelativeTime(user.lastLoginAt)
                      ) : (
                        <span className="text-muted-foreground">Never</span>
                      )}
                    </span>
                  </div>
                  {user.expertiseTags && user.expertiseTags.length > 0 && (
                    <div className="flex flex-wrap gap-1">
                      {user.expertiseTags.map((tag) => (
                        <Badge key={tag} variant="outline" className="text-xs">
                          {tag}
                        </Badge>
                      ))}
                    </div>
                  )}
                  <UserMobileActions
                    userId={user.id}
                    userEmail={user.email}
                    userStatus={user.status}
                    userRole={user.role as RoleValue}
                    userRoles={(user as unknown as { roles?: RoleValue[] }).roles}
                    currentUserRole={currentUserRole}
                  />
                </CardContent>
              </Card>
            ))}
          </div>

          {/* Pagination */}
          <Pagination
            page={page}
            totalPages={data.totalPages}
            total={data.total}
            perPage={data.perPage}
            onPageChange={(newPage) => updateParams({ page: String(newPage) })}
          />
        </>
      ) : tab !== 'applicants' ? (
        <Card>
          <CardContent className="flex flex-col items-center justify-center py-12 text-center">
            <Users className="h-12 w-12 text-muted-foreground/50" />
            <p className="mt-2 font-medium">No members found</p>
            <p className="text-sm text-muted-foreground">
              {search
                ? 'Try adjusting your search'
                : 'Invite members to get started'}
            </p>
            <Button asChild className="mt-4">
              <Link href="/admin/members/invite">
                <Plus className="mr-2 h-4 w-4" />
                Invite Member
              </Link>
            </Button>
          </CardContent>
        </Card>
      ) : null}

      {/* Floating bulk invite toolbar */}
      <AnimatePresence>
        {selectedIds.size > 0 && (
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: 20 }}
            transition={{ duration: 0.2 }}
            className="fixed bottom-4 left-1/2 -translate-x-1/2 z-50"
          >
            <Card className="shadow-lg border-2">
              <CardContent className="flex items-center gap-3 px-4 py-3">
                <span className="text-sm font-medium whitespace-nowrap">
                  {selectedIds.size} selected
                </span>
                <Button
                  size="sm"
                  onClick={() => bulkInvite.mutate({ userIds: Array.from(selectedIds) })}
                  disabled={bulkInvite.isPending}
                  className="gap-1.5"
                >
                  {bulkInvite.isPending ? (
                    <Loader2 className="h-4 w-4 animate-spin" />
                  ) : (
                    <Send className="h-4 w-4" />
                  )}
                  Invite Selected
                </Button>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={clearSelection}
                  disabled={bulkInvite.isPending}
                  className="gap-1.5"
                >
                  <X className="h-4 w-4" />
                  Clear
                </Button>
              </CardContent>
            </Card>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  )
}

function ApplicantsTabContent({ search, searchInput, setSearchInput }: { search: string; searchInput: string; setSearchInput: (v: string) => void }) {
  const [page, setPage] = useState(1)
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
  const utils = trpc.useUtils()

  const { data, isLoading } = trpc.user.getApplicants.useQuery({
    search: search || undefined,
    page,
    perPage: 20,
  })

  const bulkInvite = trpc.user.bulkInviteApplicants.useMutation({
    onSuccess: (result) => {
      const msg = `Sent ${result.sent} invite${result.sent !== 1 ? 's' : ''}`
      if (result.failed.length > 0) {
        toast.warning(`${msg}, ${result.failed.length} failed`)
      } else {
        toast.success(msg)
      }
      setSelectedIds(new Set())
      utils.user.getApplicants.invalidate()
    },
    onError: (error) => toast.error(error.message),
  })

  const toggleUser = useCallback((id: string) => {
    setSelectedIds((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }, [])

  const users = data?.users ?? []
  const allSelected = users.length > 0 && users.every((u) => selectedIds.has(u.id))
  const someSelected = users.some((u) => selectedIds.has(u.id)) && !allSelected

  const toggleAll = useCallback(() => {
    if (allSelected) {
      setSelectedIds((prev) => {
        const next = new Set(prev)
        users.forEach((u) => next.delete(u.id))
        return next
      })
    } else {
      setSelectedIds((prev) => {
        const next = new Set(prev)
        users.forEach((u) => next.add(u.id))
        return next
      })
    }
  }, [allSelected, users])

  if (isLoading) return <MembersSkeleton />

  if (!data || data.users.length === 0) {
    return (
      <Card>
        <CardContent className="flex flex-col items-center justify-center py-12 text-center">
          <Users className="h-12 w-12 text-muted-foreground/50" />
          <p className="mt-2 font-medium">No applicants found</p>
          <p className="text-sm text-muted-foreground">
            {search ? 'Try adjusting your search' : 'Applicant users will appear here after CSV import or project submission'}
          </p>
        </CardContent>
      </Card>
    )
  }

  return (
    <>
      {/* Top Pagination */}
      {data.totalPages > 1 && (
        <Pagination
          page={page}
          totalPages={data.totalPages}
          total={data.total}
          perPage={data.perPage}
          onPageChange={setPage}
        />
      )}

      {/* Desktop table */}
      <Card className="hidden md:block">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead className="w-10">
                <Checkbox
                  checked={allSelected ? true : someSelected ? 'indeterminate' : false}
                  onCheckedChange={toggleAll}
                  aria-label="Select all"
                />
              </TableHead>
              <TableHead>Applicant</TableHead>
              <TableHead>Project</TableHead>
              <TableHead>Nationality</TableHead>
              <TableHead>Institution</TableHead>
              <TableHead>Status</TableHead>
              <TableHead>Last Login</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {data.users.map((user) => (
              <TableRow key={user.id}>
                <TableCell>
                  <Checkbox
                    checked={selectedIds.has(user.id)}
                    onCheckedChange={() => toggleUser(user.id)}
                  />
                </TableCell>
                <TableCell>
                  <Link href={`/admin/members/${user.id}`} className="block">
                    <p className="font-medium hover:underline">{user.name || 'Unnamed'}</p>
                    <p className="text-sm text-muted-foreground">{user.email}</p>
                  </Link>
                </TableCell>
                <TableCell>
                  {user.projectName ? (
                    <span className="text-sm">{user.projectName}</span>
                  ) : (
                    <span className="text-sm text-muted-foreground">-</span>
                  )}
                </TableCell>
                <TableCell>
                  <span className="text-sm">{user.nationality ? <CountryDisplay country={user.nationality} /> : <span className="text-muted-foreground">-</span>}</span>
                </TableCell>
                <TableCell>
                  <span className="text-sm">{user.institution || <span className="text-muted-foreground">-</span>}</span>
                </TableCell>
                <TableCell>
                  <div className="flex items-center gap-2">
                    <Badge variant={statusColors[user.status] || 'secondary'}>
                      {statusLabels[user.status] || user.status}
                    </Badge>
                    {user.status === 'NONE' && (
                      <InlineSendInvite userId={user.id} userEmail={user.email} />
                    )}
                  </div>
                </TableCell>
                <TableCell>
                  {user.lastLoginAt ? (
                    <span title={new Date(user.lastLoginAt).toLocaleString()}>
                      {formatRelativeTime(user.lastLoginAt)}
                    </span>
                  ) : (
                    <span className="text-muted-foreground">Never</span>
                  )}
                </TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </Card>

      {/* Mobile cards */}
      <div className="space-y-4 md:hidden">
        {data.users.map((user) => (
          <Card key={user.id}>
            <CardContent className="p-4">
              <div className="flex items-start gap-3">
                <Checkbox
                  checked={selectedIds.has(user.id)}
                  onCheckedChange={() => toggleUser(user.id)}
                  className="mt-1"
                />
                <div className="flex-1 min-w-0">
                  <p className="font-medium">{user.name || 'Unnamed'}</p>
                  <p className="text-sm text-muted-foreground truncate">{user.email}</p>
                  {user.projectName && (
                    <p className="text-sm text-muted-foreground mt-1">{user.projectName}</p>
                  )}
                </div>
                <Badge variant={statusColors[user.status] || 'secondary'}>
                  {statusLabels[user.status] || user.status}
                </Badge>
              </div>
            </CardContent>
          </Card>
        ))}
      </div>

      {data.totalPages > 1 && (
        <Pagination
          page={page}
          totalPages={data.totalPages}
          total={data.total}
          perPage={data.perPage}
          onPageChange={setPage}
        />
      )}

      {/* Floating bulk invite bar */}
      <AnimatePresence>
        {selectedIds.size > 0 && (
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: 20 }}
            transition={{ duration: 0.2 }}
            className="fixed bottom-4 left-1/2 -translate-x-1/2 z-50"
          >
            <Card className="shadow-lg border-2">
              <CardContent className="flex items-center gap-3 px-4 py-3">
                <span className="text-sm font-medium whitespace-nowrap">
                  {selectedIds.size} selected
                </span>
                <Button
                  size="sm"
                  onClick={() => bulkInvite.mutate({ userIds: Array.from(selectedIds) })}
                  disabled={bulkInvite.isPending}
                  className="gap-1.5"
                >
                  {bulkInvite.isPending ? (
                    <Loader2 className="h-4 w-4 animate-spin" />
                  ) : (
                    <Send className="h-4 w-4" />
                  )}
                  Send Invites
                </Button>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => setSelectedIds(new Set())}
                  disabled={bulkInvite.isPending}
                  className="gap-1.5"
                >
                  <X className="h-4 w-4" />
                  Clear
                </Button>
              </CardContent>
            </Card>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  )
}

function MembersSkeleton() {
  return (
    <Card>
      <CardContent className="p-6">
        <div className="space-y-4">
          {[...Array(5)].map((_, i) => (
            <div key={i} className="flex items-center gap-4">
              <Skeleton className="h-10 w-10 rounded-full" />
              <div className="flex-1 space-y-2">
                <Skeleton className="h-5 w-32" />
                <Skeleton className="h-4 w-48" />
              </div>
              <Skeleton className="h-9 w-9" />
            </div>
          ))}
        </div>
      </CardContent>
    </Card>
  )
}
