// tdd-guard:skip — interactive client component (admin user table + modals); behaviour validated via the admin users flow
'use client'

import { useState, useMemo, useEffect } from'react'
import {
 Search, UserPlus, Pencil, KeyRound, Mail, Trash2, PowerOff, Power, LockOpen, Send, Link2, ClipboardCopy,
} from'lucide-react'
import type { DepartmentMasterData } from'@/lib/repository-types'
import type { UserSession, RoleCode } from'@/lib/auth/permissions-shared'
import { canManageUser } from'@/lib/auth/permissions-shared'
import { createClient } from'@/lib/supabase/client'
import CreateUserModal from'./create-user-modal'
import EditUserModal from'./edit-user-modal'
import DemoUserButton from'./demo-user-button'// TEMP: remove file + this line to delete demo feature

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
interface Role {
 id: string
 code: RoleCode
 label: string
}

interface Department {
 id: string
 department_code: string
 department_name: string
}

export interface UserRow {
 id: string
 auth_user_id?: string | null
 first_name: string
 last_name: string
 display_name: string
 email: string
 avatar_url: string | null
 account_status: string
 must_change_password: boolean
 last_login_at: string | null
 created_at: string
 department_id: string | null
 role_id: string
 // KAR-787 / R3: account not linked to any consultant (Mitarbeiter) record
 is_orphan?: boolean
 // KAR-787 / R47: most recent invitation timestamp (user_audit_log)
 last_invited_at?: string | null
 // KAR-787 / R51/R52: linked consultant (Mitarbeiter), if any
 linked_consultant_id?: string | null
 linked_consultant_name?: string | null
 // Supabase returns joined relations as arrays
 roles: Role | Role[] | null
 department_master_data: Department | Department[] | null
}

function resolveRole(roles: Role | Role[] | null): Role | null {
 if (!roles) return null
 return Array.isArray(roles) ? (roles[0] ?? null) : roles
}

function resolveDept(dept: Department | Department[] | null): Department | null {
 if (!dept) return null
 return Array.isArray(dept) ? (dept[0] ?? null) : dept
}

interface Props {
 initialUsers: UserRow[]
 departments: DepartmentMasterData[]
 // KAR-787 / R53: roles loaded from the DB, threaded into the modals
 roles: { code: RoleCode; id: string; label: string }[]
 session: UserSession
}

// ---------------------------------------------------------------------------
// Badges
// ---------------------------------------------------------------------------
function RoleBadge({ code, label }: { code: RoleCode; label: string }) {
 const cls =
 code ==='masteradmin'?'bg-primary-dark text-white':
 code ==='admin'?'bg-primary text-white':
 code ==='consultant'?'bg-primary text-white':
'bg-muted-foreground text-white'
 return (
 <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${cls}`}>
 {label}
 </span>
 )
}

function StatusBadge({ status }: { status: string }) {
 const cfg: Record<string, { cls: string; label: string }> = {
 active: { cls:'bg-green-100 text-success', label:'Aktiv'},
 invited: { cls:'bg-secondary text-primary', label:'Eingeladen'},
 pending_registration:{ cls:'bg-secondary text-primary', label:'Ausstehend'},
 locked: { cls:'bg-red-100 text-destructive', label:'Gesperrt'},
 inactive: { cls:'bg-muted text-muted-foreground', label:'Inaktiv'},
 deactivated: { cls:'bg-muted text-muted-foreground', label:'Deaktiviert'},
 }
 const { cls, label } = cfg[status] ?? { cls:'bg-muted text-muted-foreground', label: status }
 return (
 <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${cls}`}>
 {label}
 </span>
 )
}

function Avatar({ user }: { user: UserRow }) {
 const initials = `${user.first_name[0] ??''}${user.last_name[0] ??''}`.toUpperCase()
 if (user.avatar_url) {
 return (
 <img
 src={user.avatar_url}
 alt={user.display_name}
 className="w-9 h-9 rounded-full object-cover"
 />
 )
 }
 return (
 <div className="w-9 h-9 rounded-full bg-primary flex items-center justify-center shrink-0">
 <span className="text-xs font-bold text-white">{initials}</span>
 </div>
 )
}

// KAR-769 / D3: the cleartext-password display modal was removed. Invitations
// and password resets now send a single-use recovery link instead — no password
// is ever generated, returned, or shown in the UI.

// ---------------------------------------------------------------------------
// Confirm dialog
// ---------------------------------------------------------------------------
function ConfirmDialog({
 message,
 onConfirm,
 onCancel,
 confirmLabel ='Bestätigen',
 danger = false,
}: {
 message: string
 onConfirm: () => void
 onCancel: () => void
 confirmLabel?: string
 danger?: boolean
}) {
 return (
 <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
 <div className="bg-card text-foreground rounded-xl w-full max-w-sm p-6 space-y-4">
 <p className="text-sm text-foreground">{message}</p>
 <div className="flex gap-3 justify-end">
 <button
 onClick={onCancel}
 className="h-9 px-4 rounded-lg border border-border text-sm text-muted-foreground hover:bg-background transition-colors"
 >
 Abbrechen
 </button>
 <button
 onClick={onConfirm}
 className={`h-9 px-4 rounded-lg text-sm text-white font-medium transition-colors ${
 danger ?'bg-destructive hover:bg-destructive/90':'bg-primary hover:bg-primary/90'
 }`}
 >
 {confirmLabel}
 </button>
 </div>
 </div>
 </div>
 )
}

// ---------------------------------------------------------------------------
// Icon action button with tooltip
// ---------------------------------------------------------------------------
function ActionButton({
 icon: Icon,
 label,
 onClick,
 danger = false,
 disabled = false,
}: {
 icon: React.ElementType
 label: string
 onClick: () => void
 danger?: boolean
 disabled?: boolean
}) {
 return (
 <div className="relative group">
 <button
 onClick={onClick}
 disabled={disabled}
 title={label}
 className={`w-8 h-8 flex items-center justify-center rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${
 danger
 ?'text-destructive hover:bg-red-50'
 :'text-muted-foreground hover:bg-secondary hover:text-primary'
 }`}
 >
 <Icon size={15} />
 </button>
 <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 bg-foreground text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity z-10">
 {label}
 </div>
 </div>
 )
}

// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------
export default function UsersClient({ initialUsers, departments, roles, session }: Props) {
 const [users, setUsers] = useState<UserRow[]>(initialUsers)
 const [search, setSearch] = useState('')
 const [filterRole, setFilterRole] = useState<string>('')
 const [filterDept, setFilterDept] = useState<string>('')
 const [filterStatus, setFilterStatus] = useState<string>('')

 const [emailStats, setEmailStats] = useState<{ pending: number; sent: number; failed: number } | null>(null)
 const supabase = useMemo(() => createClient(), [])

 useEffect(() => {
 async function fetchStats() {
 const [{ count: pending }, { count: sent }, { count: failed }] = await Promise.all([
 supabase.from('email_queue').select('id', { count:'exact', head: true }).eq('status','pending'),
 supabase.from('email_queue').select('id', { count:'exact', head: true }).eq('status','sent'),
 supabase.from('email_queue').select('id', { count:'exact', head: true }).eq('status','failed'),
 ])
 setEmailStats({ pending: pending ?? 0, sent: sent ?? 0, failed: failed ?? 0 })
 }
 void fetchStats()
 }, [])

 const [createOpen, setCreateOpen] = useState(false)
 const [editUser, setEditUser] = useState<UserRow | null>(null)

 // Transient success/info banner (e.g. "Einladung gesendet"). KAR-769 / D3.
 const [notice, setNotice] = useState<string | null>(null)
 const [inviteLinkModal, setInviteLinkModal] = useState<{ email: string; url: string; kind: 'invite' | 'reset' } | null>(null)
 const [linkCopied, setLinkCopied] = useState(false)
 function showNotice(message: string) {
 setNotice(message)
 setTimeout(() => setNotice(null), 5000)
 }

 const [demoUserId, setDemoUserId] = useState<string | null>(null)
 const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null)
 const [processingId, setProcessingId] = useState<string | null>(null)

 const [changeEmailUserId, setChangeEmailUserId] = useState<string | null>(null)
 const [changeEmailValue, setChangeEmailValue] = useState('')
 const [changeEmailSaving, setChangeEmailSaving] = useState(false)
 const [changeEmailError, setChangeEmailError] = useState<string | null>(null)

 // Unique roles from loaded users
 const roleOptions = useMemo(() => {
 const seen = new Map<string, string>()
 users.forEach((u) => {
 const role = resolveRole(u.roles)
 if (role) seen.set(role.code, role.label)
 })
 return Array.from(seen.entries()).map(([code, label]) => ({ code, label }))
 }, [users])

 const filtered = useMemo(() => {
 const q = search.toLowerCase()
 return users.filter((u) => {
 if (q && !u.display_name.toLowerCase().includes(q) && !u.email.toLowerCase().includes(q)) return false
 if (filterRole && resolveRole(u.roles)?.code !== filterRole) return false
 if (filterDept && u.department_id !== filterDept) return false
 if (filterStatus && u.account_status !== filterStatus) return false
 return true
 })
 }, [users, search, filterRole, filterDept, filterStatus])

 function updateUserInList(updated: UserRow) {
 setUsers((prev) => prev.map((u) => (u.id === updated.id ? updated : u)))
 }

 function removeUserFromList(id: string) {
 setUsers((prev) => prev.filter((u) => u.id !== id))
 }

 async function handleToggleStatus(user: UserRow) {
 const newStatus = user.account_status ==='active'?'inactive':'active'
 setProcessingId(user.id)
 const res = await fetch('/api/admin/users', {
 method:'PATCH',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ id: user.id, account_status: newStatus }),
 })
 if (res.ok) {
 const { profile } = await res.json() as { profile: UserRow }
 updateUserInList({ ...user, account_status: profile.account_status })

 const auditAction = newStatus ==='inactive'?'user.deactivated':'user.reactivated'
 await supabase.from('user_audit_log').insert({
 actor_id: session.authUserId,
 target_user_id: user.id,
 action: auditAction,
 details: {},
 })

 if (newStatus ==='inactive') {
 // Fire-and-forget — don't await in UI handler
 void fetch('/api/admin/users/notify', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ template:'account_deactivated', email: user.email }),
 })
 }
 }
 setProcessingId(null)
 }

 async function handleUnlock(user: UserRow) {
 setProcessingId(user.id)
 const res = await fetch('/api/admin/users/unlock', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId: user.id }),
 })
 if (res.ok) {
 updateUserInList({ ...user, account_status:'active'})
 }
 setProcessingId(null)
 }

 async function handleResetPassword(user: UserRow) {
 setProcessingId(user.id)
 // KAR-769 / D3: server issues a single-use recovery link and emails it.
 // No cleartext password is returned; audit is written server-side.
 const res = await fetch('/api/admin/users/reset-password', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId: user.id }),
 })
 if (res.ok) {
 showNotice(`Passwort-Reset-Link an ${user.email} gesendet.`)
 } else {
 showNotice(`Reset-Link konnte nicht gesendet werden (${res.status}).`)
 }
 setProcessingId(null)
 }

 async function handleSendInvite(user: UserRow) {
 setProcessingId(user.id)
 // KAR-769 / D3: explicit, deliberate invitation — generates and emails a
 // single-use login link. Never sent automatically on account creation.
 const res = await fetch('/api/admin/users/invite', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId: user.id }),
 })
 if (res.ok) {
 showNotice(`Einladung an ${user.email} gesendet.`)
 } else {
 showNotice(`Einladung konnte nicht gesendet werden (${res.status}).`)
 }
 setProcessingId(null)
 }

 // KAR-772 interim (kein Mail-Worker): frischen Einladungs-Link erzeugen und dem
 // Admin zum manuellen Verschicken zeigen.
 async function handleCopyInviteLink(user: UserRow) {
 setProcessingId(user.id)
 const res = await fetch('/api/admin/users/invite-link', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId: user.id }),
 })
 if (res.ok) {
 const data = await res.json() as { email: string; link: string }
 setLinkCopied(false)
 setInviteLinkModal({ email: data.email, url: data.link, kind: 'invite' })
 try { await navigator.clipboard.writeText(data.link); setLinkCopied(true) } catch { /* clipboard blocked → manuell kopieren */ }
 } else {
 showNotice(`Einladungs-Link konnte nicht erzeugt werden (${res.status}).`)
 }
 setProcessingId(null)
 }

 // KAR-772 interim (kein Mail-Worker): frischen Passwort-Reset-Link erzeugen und
 // dem Admin zum manuellen Verschicken zeigen — analog zum Einladungs-Link, aber
 // für aktive Nutzer (Self-Service-Reset-Mail wird mangels Worker nicht zugestellt).
 async function handleCopyResetLink(user: UserRow) {
 setProcessingId(user.id)
 const res = await fetch('/api/admin/users/reset-link', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId: user.id }),
 })
 if (res.ok) {
 const data = await res.json() as { email: string; link: string }
 setLinkCopied(false)
 setInviteLinkModal({ email: data.email, url: data.link, kind: 'reset' })
 try { await navigator.clipboard.writeText(data.link); setLinkCopied(true) } catch { /* clipboard blocked → manuell kopieren */ }
 } else {
 showNotice(`Reset-Link konnte nicht erzeugt werden (${res.status}).`)
 }
 setProcessingId(null)
 }

 async function handleChangeEmail(userId: string) {
 setChangeEmailSaving(true)
 setChangeEmailError(null)
 const res = await fetch('/api/admin/users/update-email', {
 method:'POST',
 headers: {'Content-Type':'application/json'},
 body: JSON.stringify({ userId, newEmail: changeEmailValue }),
 })
 if (res.ok) {
 setUsers((prev) => prev.map((u) => u.id === userId ? { ...u, email: changeEmailValue } : u))
 setChangeEmailUserId(null)
 setChangeEmailValue('')
 } else {
 const data = await res.json() as { error: string }
 setChangeEmailError(data.error ??'Fehler')
 }
 setChangeEmailSaving(false)
 }

 async function handleDelete(id: string) {
 setConfirmDeleteId(null)
 setProcessingId(id)
 const res = await fetch(`/api/admin/users?id=${id}`, { method:'DELETE'})
 if (res.ok) removeUserFromList(id)
 setProcessingId(null)
 }

 const isAdmin = session.role ==='admin'|| session.role ==='masteradmin'

 return (
 <div className="space-y-5">
 {/* Email queue stats */}
 {emailStats && (
 <div className="flex items-center gap-4 px-6 py-2 bg-background border border-border rounded-lg text-xs text-muted-foreground">
 <span className="font-medium text-foreground">E-Mail-Warteschlange:</span>
 <span className="flex items-center gap-1">
 <span className="w-1.5 h-1.5 rounded-full bg-status-yellow inline-block"/>
 {emailStats.pending} ausstehend
 </span>
 <span className="flex items-center gap-1">
 <span className="w-1.5 h-1.5 rounded-full bg-success inline-block"/>
 {emailStats.sent} gesendet
 </span>
 {emailStats.failed > 0 && (
 <span className="flex items-center gap-1 text-destructive">
 <span className="w-1.5 h-1.5 rounded-full bg-destructive inline-block"/>
 {emailStats.failed} fehlgeschlagen
 </span>
 )}
 </div>
 )}

 {/* Header */}
 <div className="flex items-center justify-between gap-4 flex-wrap">
 <div>
 <h1 className="text-xl font-bold text-foreground">Benutzerverwaltung</h1>
 <p className="text-sm text-muted-foreground mt-0.5">{users.length} Benutzer gesamt</p>
 </div>
 <div className="flex items-center gap-3 flex-wrap">
 {isAdmin && (
 <DemoUserButton
 demoUserId={demoUserId}
 onDemoCreated={(u) => {
 setUsers((prev) => {
 // Replace existing demo row or prepend
 const without = prev.filter((x) => x.id !== u.id)
 return [u as UserRow, ...without]
 })
 setDemoUserId(u.id)
 }}
 onDemoReset={(id) => {
 setUsers((prev) => prev.filter((x) => x.id !== id))
 setDemoUserId(null)
 }}
 />
 )}
 {isAdmin && (
 <button
 onClick={() => setCreateOpen(true)}
 className="flex items-center gap-2 h-10 px-4 bg-primary text-white rounded-lg text-sm font-semibold hover:bg-primary/90 transition-colors"
 >
 <UserPlus size={16} />
 Benutzer hinzufügen
 </button>
 )}
 </div>
 </div>

 {/* Filters */}
 <div className="flex flex-wrap gap-3">
 <div className="relative flex-1 min-w-48">
 <Search size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"/>
 <input
 type="text"
 value={search}
 onChange={(e) => setSearch(e.target.value)}
 placeholder="Name oder E-Mail suchen…"
 className="w-full h-9 pl-9 pr-3 rounded-lg border border-border bg-card text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
 />
 </div>
 <select
 value={filterRole}
 onChange={(e) => setFilterRole(e.target.value)}
 className="h-9 px-3 rounded-lg border border-border bg-card text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
 >
 <option value="">Alle Rollen</option>
 {roleOptions.map((r) => (
 <option key={r.code} value={r.code}>{r.label}</option>
 ))}
 </select>
 <select
 value={filterDept}
 onChange={(e) => setFilterDept(e.target.value)}
 className="h-9 px-3 rounded-lg border border-border bg-card text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
 >
 <option value="">Alle Abteilungen</option>
 {departments.map((d) => (
 <option key={d.id} value={d.id}>{d.department_code}</option>
 ))}
 </select>
 <select
 value={filterStatus}
 onChange={(e) => setFilterStatus(e.target.value)}
 className="h-9 px-3 rounded-lg border border-border bg-card text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
 >
 <option value="">Alle Status</option>
 <option value="active">Aktiv</option>
 <option value="invited">Eingeladen</option>
 <option value="pending_registration">Ausstehend</option>
 <option value="locked">Gesperrt</option>
 <option value="inactive">Inaktiv</option>
 <option value="deactivated">Deaktiviert</option>
 </select>
 </div>

 {/* Table */}
 <div className="bg-card rounded-xl border border-border overflow-hidden">
 <div className="overflow-x-auto">
 <table className="w-full text-sm">
 <thead>
 <tr className="border-b border-border bg-background">
 <th className="text-left px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider">Benutzer</th>
 <th className="text-left px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider hidden md:table-cell">Abteilung</th>
 <th className="text-left px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider">Rolle</th>
 <th className="text-left px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider">Status</th>
 <th className="text-left px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider hidden lg:table-cell">Letzter Login</th>
 <th className="text-right px-4 py-3 font-semibold text-muted-foreground text-xs uppercase tracking-wider">Aktionen</th>
 </tr>
 </thead>
 <tbody className="divide-y divide-border">
 {filtered.length === 0 && (
 <tr>
 <td colSpan={6} className="px-4 py-10 text-center text-muted-foreground text-sm">
 Keine Benutzer gefunden.
 </td>
 </tr>
 )}
 {filtered.map((user) => {
 const userRole = resolveRole(user.roles)
 const userDept = resolveDept(user.department_master_data)
 const targetRole = (userRole?.code ??'readonly') as RoleCode
 const canManage = isAdmin && canManageUser(session, targetRole)
 const isProcessing = processingId === user.id

 return (
 <tr key={user.id} className="hover:bg-background transition-colors">
 {/* Name + email */}
 <td className="px-4 py-3">
 <div className="flex items-center gap-3">
 <Avatar user={user} />
 <div className="min-w-0">
 <p className="font-medium text-foreground truncate">{user.display_name}</p>
 <p className="text-xs text-muted-foreground truncate">{user.email}</p>
 {user.must_change_password && (
 <p className="text-xs text-status-yellow bg-foreground inline-block px-1 rounded mt-0.5">
 Passwort ausstehend
 </p>
 )}
 {user.is_orphan && (
 <p
 className="text-xs text-amber-700 bg-amber-100 inline-block px-1 rounded mt-0.5"
 title="Dieser Benutzer ist mit keinem Mitarbeiter-Stammdatensatz verknüpft. Verknüpfen oder entfernen."
 >
 Kein Mitarbeiter-Link
 </p>
 )}
 {/* KAR-787 / R52: linked-consultant indicator */}
 {user.linked_consultant_name && (
 <p
 className="text-xs text-muted-foreground inline-flex items-center gap-1 mt-0.5"
 title={`Verknüpft mit Mitarbeiter: ${user.linked_consultant_name}`}
 >
 <Link2 size={11} /> {user.linked_consultant_name}
 </p>
 )}
 </div>
 </div>
 </td>

 {/* Department */}
 <td className="px-4 py-3 hidden md:table-cell">
 {userDept ? (
 <span className="text-foreground">{userDept.department_code}</span>
 ) : (
 <span className="text-muted-foreground">—</span>
 )}
 </td>

 {/* Role badge */}
 <td className="px-4 py-3">
 {userRole ? (
 <RoleBadge code={userRole.code} label={userRole.label} />
 ) : (
 <span className="text-muted-foreground">—</span>
 )}
 </td>

 {/* Status badge */}
 <td className="px-4 py-3">
 <StatusBadge status={user.account_status} />
 </td>

 {/* Last login + last invitation (KAR-787 / R47) */}
 <td className="px-4 py-3 hidden lg:table-cell text-muted-foreground">
 <div>
 {user.last_login_at
 ? new Date(user.last_login_at).toLocaleDateString('de-DE')
 :'—'}
 </div>
 {user.last_invited_at && (
 <div className="text-xs text-muted-foreground" title="Letzte Einladung gesendet">
 ✉ {new Date(user.last_invited_at).toLocaleDateString('de-DE')}
 </div>
 )}
 </td>

 {/* Actions */}
 <td className="px-4 py-3">
 <div className="flex items-center justify-end gap-1">
 {canManage && (
 <>
 <ActionButton
 icon={Pencil}
 label="Bearbeiten"
 onClick={() => setEditUser(user)}
 disabled={isProcessing}
 />
 {user.account_status ==='locked'? (
 <ActionButton
 icon={LockOpen}
 label="Konto entsperren"
 onClick={() => void handleUnlock(user)}
 disabled={isProcessing}
 />
 ) : (
 <ActionButton
 icon={user.account_status ==='active'? PowerOff : Power}
 label={user.account_status ==='active'?'Deaktivieren':'Aktivieren'}
 onClick={() => void handleToggleStatus(user)}
 disabled={isProcessing}
 />
 )}
 {(user.account_status ==='invited'|| user.account_status ==='pending_registration') && (
 <>
 <ActionButton
 icon={Send}
 label="Einladung senden"
 onClick={() => void handleSendInvite(user)}
 disabled={isProcessing}
 />
 <ActionButton
 icon={Link2}
 label="Einladungs-Link kopieren"
 onClick={() => void handleCopyInviteLink(user)}
 disabled={isProcessing}
 />
 </>
 )}
 <ActionButton
 icon={KeyRound}
 label="Passwort zurücksetzen"
 onClick={() => void handleResetPassword(user)}
 disabled={isProcessing}
 />
 {user.account_status === 'active' && (
 <ActionButton
 icon={ClipboardCopy}
 label="Reset-Link kopieren"
 onClick={() => void handleCopyResetLink(user)}
 disabled={isProcessing}
 />
 )}
 <ActionButton
 icon={Mail}
 label="E-Mail ändern"
 onClick={() => {
 setChangeEmailUserId(user.id)
 setChangeEmailValue(user.email)
 setChangeEmailError(null)
 }}
 disabled={isProcessing}
 />
 <ActionButton
 icon={Trash2}
 label="Löschen"
 onClick={() => setConfirmDeleteId(user.id)}
 danger
 disabled={isProcessing}
 />
 </>
 )}
 </div>
 </td>
 </tr>
 )
 })}
 </tbody>
 </table>
 </div>
 </div>

 {/* Change email inline modal */}
 {changeEmailUserId && (
 <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
 <div className="bg-card text-foreground rounded-xl w-full max-w-sm p-6 space-y-4">
 <h3 className="font-bold text-lg text-foreground">E-Mail ändern</h3>
 <input
 type="email"
 value={changeEmailValue}
 onChange={(e) => setChangeEmailValue(e.target.value)}
 className="w-full h-11 px-3 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary"
 placeholder="Neue E-Mail-Adresse"
 autoFocus
 />
 {changeEmailError && <p className="text-sm text-destructive">{changeEmailError}</p>}
 <div className="flex gap-3 justify-end">
 <button
 onClick={() => { setChangeEmailUserId(null); setChangeEmailValue('') }}
 className="h-9 px-4 rounded-lg border border-border text-sm text-muted-foreground hover:bg-background transition-colors"
 >
 Abbrechen
 </button>
 <button
 onClick={() => void handleChangeEmail(changeEmailUserId)}
 disabled={changeEmailSaving || !changeEmailValue}
 className="h-9 px-4 bg-primary text-white rounded-lg text-sm font-semibold hover:bg-primary/90 disabled:opacity-50 transition-colors"
 >
 {changeEmailSaving ?'Speichern…':'Speichern'}
 </button>
 </div>
 </div>
 </div>
 )}

 {/* Confirm delete */}
 {confirmDeleteId && (
 <ConfirmDialog
 message="Möchten Sie diesen Benutzer wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden."
 confirmLabel="Löschen"
 danger
 onConfirm={() => void handleDelete(confirmDeleteId)}
 onCancel={() => setConfirmDeleteId(null)}
 />
 )}

 {/* Transient success/info notice (e.g. invitation sent) */}
 {/* KAR-772 interim: invite link to relay manually */}
 {inviteLinkModal && (
 <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
 <div className="bg-card text-foreground rounded-xl w-full max-w-md p-6 space-y-4">
 <div>
 <p className="text-sm font-medium text-foreground">{inviteLinkModal.kind === 'reset' ?'Passwort-Reset-Link':'Einladungs-Link'} für {inviteLinkModal.email}</p>
 <p className="text-xs text-muted-foreground mt-1">Einmal gültig. Schicke ihn dem Nutzer über deinen eigenen Kanal — darüber setzt er sein Passwort.</p>
 </div>
 <input
 readOnly
 value={inviteLinkModal.url}
 onFocus={(e) => e.currentTarget.select()}
 className="w-full h-10 px-3 rounded-lg border border-border bg-background text-xs font-mono"
 />
 <div className="flex gap-3 justify-end">
 <button
 onClick={() => setInviteLinkModal(null)}
 className="h-9 px-4 rounded-lg border border-border text-sm text-muted-foreground hover:bg-background transition-colors"
 >
 Schließen
 </button>
 <button
 onClick={async () => { try { await navigator.clipboard.writeText(inviteLinkModal.url); setLinkCopied(true) } catch { /* manuell kopieren */ } }}
 className="h-9 px-4 rounded-lg text-sm text-white font-medium bg-primary hover:bg-primary/90 transition-colors"
 >
 {linkCopied ?'Kopiert ✓':'Kopieren'}
 </button>
 </div>
 </div>
 </div>
 )}

 {notice && (
 <div className="fixed bottom-4 left-1/2 -translate-x-1/2 z-50 bg-foreground text-white text-sm px-4 py-2.5 rounded-lg shadow-lg max-w-md">
 {notice}
 </div>
 )}

 {/* Create user modal */}
 {createOpen && (
 <CreateUserModal
 departments={departments}
 roles={roles}
 session={session}
 onClose={() => setCreateOpen(false)}
 onCreate={(newUser) => {
 setUsers((prev) => [newUser as UserRow, ...prev])
 setCreateOpen(false)
 showNotice(`${newUser.display_name} angelegt. Über „Einladung senden" den Login-Link verschicken.`)
 }}
 />
 )}

 {/* Edit user modal */}
 {editUser && (
 <EditUserModal
 user={editUser}
 departments={departments}
 roles={roles}
 session={session}
 onClose={() => setEditUser(null)}
 onSave={(updated) => {
 updateUserInList(updated)
 setEditUser(null)
 }}
 />
 )}
 </div>
 )
}
