// src/components/team-management.tsx
"use client";

import {
  HourglassIcon,
  MailIcon,
  TriangleAlertIcon,
  UserPlusIcon,
} from "lucide-react";
import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import { MemberSitesDialog } from "@/components/team-member-sites-dialog";
import {
  AlertDialog,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { authClient } from "@/lib/auth-client";
import { cn } from "@/lib/utils";

type OrgRole = "owner" | "admin" | "member";

type Member = {
  id: string;
  role: string;
  userId: string;
  user: {
    id: string;
    name: string;
    email: string;
    image?: string | null;
  };
};

type Invitation = {
  id: string;
  email: string;
  role?: string | null;
  status: string;
};

const ROLE_LABELS: Record<string, string> = {
  owner: "Propriétaire",
  admin: "Administrateur",
  member: "Membre",
};

const INVITE_STATUS_LABELS: Record<string, string> = {
  pending: "En attente",
  accepted: "Acceptée",
  canceled: "Annulée",
  rejected: "Refusée",
};

// Roles assignable from the UI. We deliberately exclude "owner": ownership
// transfer is sensitive and left to a dedicated flow / server-side enforcement.
const ASSIGNABLE_ROLES: OrgRole[] = ["member", "admin"];

function roleLabel(role?: string | null) {
  if (!role) return "—";
  return ROLE_LABELS[role] ?? role;
}

function initials(name: string, email: string) {
  const source = name.trim() || email;
  const parts = source.split(/\s+/).filter(Boolean);
  if (parts.length >= 2) {
    return (parts[0][0] + parts[1][0]).toUpperCase();
  }
  return source.slice(0, 2).toUpperCase();
}

/**
 * Pure display math for the seats gauge. `limit === -1` means unlimited
 * (no bar, no cap); `full` drives the destructive bar + invite lock.
 */
export function seatGaugeState(used: number, limit: number) {
  const unlimited = limit === -1;
  const full = !unlimited && used >= limit;
  const percent = unlimited
    ? 0
    : limit <= 0
      ? used > 0
        ? 100
        : 0
      : Math.min(100, (used / limit) * 100);
  return { unlimited, full, percent };
}

function SeatsGauge({ used, limit }: { used: number; limit: number }) {
  const { unlimited, full, percent } = seatGaugeState(used, limit);

  return (
    <div className="flex flex-col gap-1.5">
      <div className="flex items-center justify-between gap-4">
        <span className="text-muted-foreground text-sm">
          {unlimited
            ? `Sièges : ${used} (illimité)`
            : `Sièges : ${used} / ${limit}`}
        </span>
        {full ? (
          <Button asChild size="sm" variant="outline" className="shrink-0">
            <Link href="/settings/billing">Augmenter le plan</Link>
          </Button>
        ) : null}
      </div>
      {unlimited ? null : (
        <div
          className="h-1.5 w-full overflow-hidden rounded-full bg-muted"
          role="progressbar"
          aria-label="Sièges utilisés"
          aria-valuemin={0}
          aria-valuemax={limit}
          aria-valuenow={Math.min(used, limit)}
        >
          <div
            className={cn(
              "h-full rounded-full bg-primary transition-[width]",
              full && "bg-destructive",
            )}
            style={{ width: `${percent}%` }}
          />
        </div>
      )}
    </div>
  );
}

function RoleBadge({ role }: { role: string }) {
  const isOwner = role === "owner";
  return (
    <Badge
      variant="secondary"
      className={cn(
        "gap-1.5 font-medium",
        isOwner && "border-primary/30 bg-primary/10 text-primary",
      )}
    >
      <span
        className={cn(
          "size-1.5 rounded-full",
          isOwner ? "bg-primary" : "bg-muted-foreground",
        )}
      />
      {roleLabel(role)}
    </Badge>
  );
}

/**
 * Seat-flow S1/S3 banner: invitees whose acceptance was seat-blocked are
 * waiting for the plan to grow. Only shown while seats are actually full —
 * once a seat frees up (upgrade), the invitees were emailed and the banner
 * disappears on its own.
 */
function SeatBlockedBanner({ emails }: { emails: string[] }) {
  const list = emails.join(", ");
  const text =
    emails.length === 1
      ? `${list} attend une place — augmente ton plan pour l'accueillir.`
      : `${list} attendent une place — augmente ton plan pour les accueillir.`;
  return (
    <div
      role="status"
      className="flex items-start gap-2.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 py-3 text-amber-900 text-sm dark:text-amber-200"
    >
      <HourglassIcon className="mt-0.5 size-4 shrink-0" />
      <span>{text}</span>
    </div>
  );
}

export function TeamManagement({
  currentUserId,
  currentRole,
  orgId,
  orgName,
  seatsUsed,
  seatsLimit,
  seatBlockedEmails,
  orgSites,
}: {
  currentUserId: string;
  currentRole: OrgRole | string;
  orgId: string;
  orgName: string;
  seatsUsed: number;
  seatsLimit: number;
  seatBlockedEmails: string[];
  orgSites: { id: string; name: string }[];
}) {
  const canManage = currentRole === "owner" || currentRole === "admin";
  const isOwner = currentRole === "owner";
  const { full: seatsFull } = seatGaugeState(seatsUsed, seatsLimit);

  const [members, setMembers] = useState<Member[] | null>(null);
  const [invitations, setInvitations] = useState<Invitation[] | null>(null);
  const [loadError, setLoadError] = useState<string | null>(null);

  const load = useCallback(async () => {
    setLoadError(null);
    const [membersRes, invitesRes] = await Promise.all([
      authClient.organization.listMembers({
        query: { organizationId: orgId },
      }),
      authClient.organization.listInvitations({
        query: { organizationId: orgId },
      }),
    ]);

    if (membersRes.error || invitesRes.error) {
      setLoadError(
        membersRes.error?.message ??
          invitesRes.error?.message ??
          "Impossible de charger l'équipe.",
      );
      return;
    }

    setMembers((membersRes.data?.members ?? []) as Member[]);
    // Only surface invitations that are still actionable.
    setInvitations(
      ((invitesRes.data ?? []) as Invitation[]).filter(
        (inv) => inv.status === "pending",
      ),
    );
  }, [orgId]);

  useEffect(() => {
    void load();
  }, [load]);

  return (
    <>
      <div className="mb-1 flex flex-col gap-1.5">
        <h1 className="font-heading font-semibold text-2xl tracking-tight">
          Équipe
        </h1>
        <p className="text-muted-foreground text-sm">
          Gère les membres, les invitations et les paramètres de ton
          organisation.
        </p>
      </div>

      {loadError ? (
        <p className="text-destructive text-sm" role="alert">
          {loadError}
        </p>
      ) : null}

      {seatBlockedEmails.length > 0 && seatsFull ? (
        <SeatBlockedBanner emails={seatBlockedEmails} />
      ) : null}

      <MembersCard
        members={members}
        currentUserId={currentUserId}
        canManage={canManage}
        orgId={orgId}
        orgSites={orgSites}
        seatsUsed={seatsUsed}
        seatsLimit={seatsLimit}
        onInvited={load}
        onChanged={load}
      />

      <InvitationsCard
        invitations={invitations}
        canManage={canManage}
        onChanged={load}
      />

      <OrganizationCard
        orgId={orgId}
        orgName={orgName}
        canManage={canManage}
        isOwner={isOwner}
        members={members}
        currentUserId={currentUserId}
        onChanged={load}
      />
    </>
  );
}

function MembersCard({
  members,
  currentUserId,
  canManage,
  orgId,
  orgSites,
  seatsUsed,
  seatsLimit,
  onInvited,
  onChanged,
}: {
  members: Member[] | null;
  currentUserId: string;
  canManage: boolean;
  orgId: string;
  orgSites: { id: string; name: string }[];
  seatsUsed: number;
  seatsLimit: number;
  onInvited: () => Promise<void>;
  onChanged: () => Promise<void>;
}) {
  const { full: seatsFull } = seatGaugeState(seatsUsed, seatsLimit);

  return (
    <Card>
      <CardHeader>
        <div className="flex items-start justify-between gap-4">
          <div className="flex flex-col gap-1.5">
            <CardTitle>Membres</CardTitle>
            <CardDescription>
              Les personnes qui ont accès à cette organisation.
            </CardDescription>
          </div>
          {canManage ? (
            seatsFull ? (
              <div className="flex shrink-0 flex-col items-end gap-1">
                <Button size="sm" disabled>
                  <UserPlusIcon className="size-4" />
                  Inviter un membre
                </Button>
                <span className="text-muted-foreground text-xs">
                  Plus de siège disponible sur ton forfait.
                </span>
              </div>
            ) : (
              <InviteDialog onInvited={onInvited} />
            )
          ) : null}
        </div>
        <SeatsGauge used={seatsUsed} limit={seatsLimit} />
      </CardHeader>
      <CardContent>
        {members === null ? (
          <div className="flex flex-col gap-3">
            <Skeleton className="h-12 w-full" />
            <Skeleton className="h-12 w-full" />
          </div>
        ) : members.length === 0 ? (
          <p className="text-muted-foreground text-sm">
            Aucun membre pour le moment.
          </p>
        ) : (
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead>Membre</TableHead>
                <TableHead>Rôle</TableHead>
                {canManage ? <TableHead className="text-right" /> : null}
              </TableRow>
            </TableHeader>
            <TableBody>
              {members.map((member) => (
                <MemberRow
                  key={member.id}
                  member={member}
                  currentUserId={currentUserId}
                  canManage={canManage}
                  orgId={orgId}
                  orgSites={orgSites}
                  onChanged={onChanged}
                />
              ))}
            </TableBody>
          </Table>
        )}
      </CardContent>
    </Card>
  );
}

function MemberRow({
  member,
  currentUserId,
  canManage,
  orgId,
  orgSites,
  onChanged,
}: {
  member: Member;
  currentUserId: string;
  canManage: boolean;
  orgId: string;
  orgSites: { id: string; name: string }[];
  onChanged: () => Promise<void>;
}) {
  const isSelf = member.userId === currentUserId;
  const isOwnerMember = member.role === "owner";
  // Owner/admin can edit roles, but never their own role and never an owner's
  // role from this UI (ownership transfer is out of scope). The server enforces
  // the hard guarantees (last-owner protection); this is just UX guarding.
  const canEditRole = canManage && !isSelf && !isOwnerMember;
  // Removal: managers only, not yourself (use "leave" for that), not an owner.
  const canRemove = canManage && !isSelf && !isOwnerMember;

  const [roleSaving, setRoleSaving] = useState(false);

  async function changeRole(role: string) {
    if (role === member.role) return;
    setRoleSaving(true);
    const res = await authClient.organization.updateMemberRole({
      memberId: member.id,
      role: role as OrgRole,
      organizationId: orgId,
    });
    setRoleSaving(false);
    if (res.error) {
      toast.error(res.error.message ?? "Impossible de changer le rôle.");
      return;
    }
    toast.success("Rôle mis à jour.");
    await onChanged();
  }

  return (
    <TableRow>
      <TableCell>
        <div className="flex items-center gap-3">
          <Avatar className="size-9">
            {member.user.image ? (
              <AvatarImage src={member.user.image} alt={member.user.name} />
            ) : null}
            <AvatarFallback>
              {initials(member.user.name, member.user.email)}
            </AvatarFallback>
          </Avatar>
          <div className="flex min-w-0 flex-col">
            <span className="flex items-center gap-1.5 font-medium text-sm">
              {member.user.name || member.user.email}
              {isSelf ? (
                <span className="font-normal text-muted-foreground text-xs">
                  (toi)
                </span>
              ) : null}
            </span>
            <span className="truncate text-muted-foreground text-xs">
              {member.user.email}
            </span>
          </div>
        </div>
      </TableCell>
      <TableCell>
        {canEditRole ? (
          <Select
            value={member.role}
            onValueChange={changeRole}
            disabled={roleSaving}
          >
            <SelectTrigger className="w-[180px]" aria-label="Rôle du membre">
              <SelectValue />
            </SelectTrigger>
            <SelectContent position="popper">
              {ASSIGNABLE_ROLES.map((r) => (
                <SelectItem key={r} value={r}>
                  {ROLE_LABELS[r]}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        ) : (
          <RoleBadge role={member.role} />
        )}
      </TableCell>
      {canManage ? (
        <TableCell className="text-right">
          <div className="flex items-center justify-end gap-2">
            {/* Members get per-site sharing; owners/admins see all sites. */}
            {member.role !== "owner" && member.role !== "admin" ? (
              <MemberSitesDialog
                userId={member.userId}
                memberName={member.user.name || member.user.email}
                sites={orgSites}
              />
            ) : null}
            {canRemove ? (
              <RemoveMemberDialog
                member={member}
                orgId={orgId}
                onChanged={onChanged}
              />
            ) : null}
          </div>
        </TableCell>
      ) : null}
    </TableRow>
  );
}

function RemoveMemberDialog({
  member,
  orgId,
  onChanged,
}: {
  member: Member;
  orgId: string;
  onChanged: () => Promise<void>;
}) {
  const [open, setOpen] = useState(false);
  const [pending, setPending] = useState(false);

  async function remove() {
    setPending(true);
    const res = await authClient.organization.removeMember({
      memberIdOrEmail: member.id,
      organizationId: orgId,
    });
    setPending(false);
    if (res.error) {
      toast.error(res.error.message ?? "Impossible de retirer ce membre.");
      return;
    }
    toast.success("Membre retiré.");
    setOpen(false);
    await onChanged();
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger asChild>
        <Button
          variant="ghost"
          size="sm"
          className="text-muted-foreground hover:text-destructive"
        >
          Retirer
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Retirer ce membre&nbsp;?</AlertDialogTitle>
          <AlertDialogDescription>
            {member.user.name || member.user.email} perdra l'accès à cette
            organisation. Cette action est immédiate.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel disabled={pending}>Annuler</AlertDialogCancel>
          <Button
            variant="destructive"
            onClick={(e) => {
              e.preventDefault();
              void remove();
            }}
            disabled={pending}
          >
            {pending ? "…" : "Retirer"}
          </Button>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

function InvitationsCard({
  invitations,
  canManage,
  onChanged,
}: {
  invitations: Invitation[] | null;
  canManage: boolean;
  onChanged: () => Promise<void>;
}) {
  const [cancelingId, setCancelingId] = useState<string | null>(null);

  async function cancel(invitationId: string) {
    setCancelingId(invitationId);
    const res = await authClient.organization.cancelInvitation({
      invitationId,
    });
    setCancelingId(null);
    if (res.error) {
      toast.error(res.error.message ?? "Impossible d'annuler l'invitation.");
      return;
    }
    toast.success("Invitation annulée.");
    await onChanged();
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Invitations en attente</CardTitle>
        <CardDescription>
          Les invitations envoyées qui n'ont pas encore été acceptées.
        </CardDescription>
      </CardHeader>
      <CardContent>
        {invitations === null ? (
          <div className="flex flex-col gap-3">
            <Skeleton className="h-10 w-full" />
          </div>
        ) : invitations.length === 0 ? (
          <div className="flex flex-col items-center gap-2.5 px-5 py-8 text-center">
            <div className="grid size-11 place-items-center rounded-xl border bg-muted/40 text-muted-foreground">
              <MailIcon className="size-[22px]" />
            </div>
            <p className="font-medium text-sm">Aucune invitation en attente</p>
            <p className="max-w-80 text-muted-foreground text-sm">
              Invite un membre pour qu'il apparaisse ici jusqu'à ce qu'il
              rejoigne l'organisation.
            </p>
          </div>
        ) : (
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead>Email</TableHead>
                <TableHead>Rôle</TableHead>
                <TableHead>Statut</TableHead>
                {canManage ? <TableHead className="text-right" /> : null}
              </TableRow>
            </TableHeader>
            <TableBody>
              {invitations.map((invitation) => (
                <TableRow key={invitation.id}>
                  <TableCell className="font-medium">
                    {invitation.email}
                  </TableCell>
                  <TableCell>{roleLabel(invitation.role)}</TableCell>
                  <TableCell>
                    <Badge variant="outline">
                      {INVITE_STATUS_LABELS[invitation.status] ??
                        invitation.status}
                    </Badge>
                  </TableCell>
                  {canManage ? (
                    <TableCell className="text-right">
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={() => cancel(invitation.id)}
                        disabled={cancelingId === invitation.id}
                      >
                        {cancelingId === invitation.id ? "…" : "Annuler"}
                      </Button>
                    </TableCell>
                  ) : null}
                </TableRow>
              ))}
            </TableBody>
          </Table>
        )}
      </CardContent>
    </Card>
  );
}

function InviteDialog({ onInvited }: { onInvited: () => Promise<void> }) {
  const [open, setOpen] = useState(false);
  const [email, setEmail] = useState("");
  const [role, setRole] = useState<"member" | "admin">("member");
  const [pending, setPending] = useState(false);

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    setPending(true);
    const res = await authClient.organization.inviteMember({
      email,
      role,
    });
    setPending(false);
    if (res.error) {
      toast.error(res.error.message ?? "Impossible d'envoyer l'invitation.");
      return;
    }
    toast.success("Invitation envoyée.");
    setEmail("");
    setRole("member");
    setOpen(false);
    await onInvited();
  }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <Button size="sm" className="shrink-0">
          <UserPlusIcon className="size-4" />
          Inviter un membre
        </Button>
      </DialogTrigger>
      <DialogContent>
        <form onSubmit={submit}>
          <DialogHeader>
            <DialogTitle>Inviter un membre</DialogTitle>
            <DialogDescription>
              Envoie une invitation par email à rejoindre l'organisation.
            </DialogDescription>
          </DialogHeader>
          <FieldGroup className="py-4">
            <Field>
              <FieldLabel htmlFor="invite-email">Email</FieldLabel>
              <Input
                id="invite-email"
                type="email"
                placeholder="m@exemple.com"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                required
              />
            </Field>
            <Field>
              <FieldLabel htmlFor="invite-role">Rôle</FieldLabel>
              <Select
                value={role}
                onValueChange={(v) => setRole(v as "member" | "admin")}
              >
                <SelectTrigger id="invite-role">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent position="popper">
                  <SelectItem value="member">Membre</SelectItem>
                  <SelectItem value="admin">Administrateur</SelectItem>
                </SelectContent>
              </Select>
            </Field>
          </FieldGroup>
          <DialogFooter>
            <DialogClose asChild>
              <Button type="button" variant="outline">
                Annuler
              </Button>
            </DialogClose>
            <Button type="submit" disabled={pending || !email}>
              {pending ? "…" : "Envoyer l'invitation"}
            </Button>
          </DialogFooter>
        </form>
      </DialogContent>
    </Dialog>
  );
}

function OrganizationCard({
  orgId,
  orgName,
  canManage,
  isOwner,
  members,
  currentUserId,
  onChanged,
}: {
  orgId: string;
  orgName: string;
  canManage: boolean;
  isOwner: boolean;
  members: Member[] | null;
  currentUserId: string;
  onChanged: () => Promise<void>;
}) {
  // Members other than the owner (i.e. everyone who must be removed before the
  // org can be deleted). Computed from the loaded list; null while loading.
  const otherMembers =
    members?.filter((m) => m.role !== "owner" && m.userId !== currentUserId) ??
    [];
  const hasOtherMembers = otherMembers.length > 0;

  return (
    <>
      <Card>
        <CardHeader>
          <CardTitle>Organisation</CardTitle>
          <CardDescription>
            Les paramètres généraux de ton organisation.
          </CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-6">
          {canManage ? (
            <EditOrgNameForm orgId={orgId} orgName={orgName} />
          ) : (
            <div className="flex flex-col gap-1">
              <span className="font-medium text-sm">Nom</span>
              <span className="text-muted-foreground text-sm">{orgName}</span>
            </div>
          )}

          {/* An owner cannot leave the organization — the leave action is only
              offered to non-owners. */}
          {isOwner ? null : (
            <div className="flex flex-col gap-3 border-t pt-6 sm:flex-row sm:items-center sm:justify-between">
              <div className="flex flex-col gap-1">
                <span className="font-medium text-sm">
                  Quitter l'organisation
                </span>
                <span className="text-muted-foreground text-sm">
                  Tu perdras l'accès à cette organisation.
                </span>
              </div>
              <LeaveOrgDialog orgId={orgId} />
            </div>
          )}
        </CardContent>
      </Card>

      {/* Danger zone — owners only. Removing all other members is a precondition
          to deleting the org, surfaced inline when needed. */}
      {isOwner ? (
        <Card className="border-destructive/40 bg-destructive/[0.03]">
          <CardHeader>
            <CardTitle className="flex items-center gap-2 text-destructive">
              <TriangleAlertIcon className="size-[17px]" />
              Zone de danger
            </CardTitle>
          </CardHeader>
          <CardFooter className="justify-between gap-4 border-destructive/30 border-t">
            <div className="flex flex-col gap-0.5">
              <span className="font-medium text-foreground text-sm">
                Supprimer l'organisation
              </span>
              <span className="text-muted-foreground text-sm">
                {hasOtherMembers
                  ? "Retire d'abord tous les autres membres avant de supprimer l'organisation."
                  : "Supprime définitivement l'organisation et toutes ses données."}
              </span>
            </div>
            <div className="flex shrink-0 flex-col items-stretch gap-2 sm:items-end">
              {hasOtherMembers ? (
                <RemoveAllMembersDialog
                  members={otherMembers}
                  orgId={orgId}
                  onChanged={onChanged}
                />
              ) : null}
              <DeleteOrgDialog
                orgId={orgId}
                orgName={orgName}
                disabled={hasOtherMembers}
              />
            </div>
          </CardFooter>
        </Card>
      ) : null}
    </>
  );
}

function RemoveAllMembersDialog({
  members,
  orgId,
  onChanged,
}: {
  members: Member[];
  orgId: string;
  onChanged: () => Promise<void>;
}) {
  const [open, setOpen] = useState(false);
  const [pending, setPending] = useState(false);

  async function removeAll() {
    setPending(true);
    for (const member of members) {
      const res = await authClient.organization.removeMember({
        memberIdOrEmail: member.id,
        organizationId: orgId,
      });
      if (res.error) {
        setPending(false);
        toast.error(
          res.error.message ?? "Impossible de retirer tous les membres.",
        );
        // Refresh so the list reflects whatever was already removed.
        await onChanged();
        return;
      }
    }
    setPending(false);
    setOpen(false);
    toast.success("Tous les autres membres ont été retirés.");
    await onChanged();
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger asChild>
        <Button variant="outline">Retirer tous les membres</Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>
            Retirer tous les autres membres&nbsp;?
          </AlertDialogTitle>
          <AlertDialogDescription>
            Les {members.length} autres membres perdront immédiatement l'accès à
            cette organisation. Cette action est nécessaire avant de pouvoir
            supprimer l'organisation.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel disabled={pending}>Annuler</AlertDialogCancel>
          <Button
            variant="destructive"
            onClick={(e) => {
              e.preventDefault();
              void removeAll();
            }}
            disabled={pending}
          >
            {pending ? "…" : "Retirer tous les membres"}
          </Button>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

function EditOrgNameForm({
  orgId,
  orgName,
}: {
  orgId: string;
  orgName: string;
}) {
  const [name, setName] = useState(orgName);
  const [pending, setPending] = useState(false);

  const dirty = name.trim() !== orgName && name.trim().length > 0;

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    setPending(true);
    const res = await authClient.organization.update({
      data: { name: name.trim() },
      organizationId: orgId,
    });
    setPending(false);
    if (res.error) {
      toast.error(res.error.message ?? "Impossible de mettre à jour le nom.");
      return;
    }
    toast.success("Organisation enregistrée.");
  }

  return (
    <form onSubmit={submit}>
      <Field>
        <FieldLabel htmlFor="org-name">Nom de l'organisation</FieldLabel>
        <div className="flex items-end gap-2.5">
          <Input
            id="org-name"
            className="flex-1"
            value={name}
            onChange={(e) => setName(e.target.value)}
            required
          />
          <Button type="submit" disabled={pending || !dirty}>
            {pending ? "…" : "Enregistrer"}
          </Button>
        </div>
      </Field>
    </form>
  );
}

function LeaveOrgDialog({ orgId }: { orgId: string }) {
  const [open, setOpen] = useState(false);
  const [pending, setPending] = useState(false);

  async function leave() {
    setPending(true);
    const res = await authClient.organization.leave({
      organizationId: orgId,
    });
    if (res.error) {
      setPending(false);
      toast.error(res.error.message ?? "Impossible de quitter l'organisation.");
      return;
    }
    // Active org is gone for this user — bounce into the app, which will
    // re-resolve the session / active org.
    window.location.href = "/keywords/reservoir";
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger asChild>
        <Button variant="outline">Quitter</Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Quitter l'organisation&nbsp;?</AlertDialogTitle>
          <AlertDialogDescription>
            Tu perdras immédiatement l'accès à cette organisation. Tu devras
            être réinvité pour la rejoindre à nouveau.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel disabled={pending}>Annuler</AlertDialogCancel>
          <Button
            variant="destructive"
            onClick={(e) => {
              e.preventDefault();
              void leave();
            }}
            disabled={pending}
          >
            {pending ? "…" : "Quitter"}
          </Button>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

function DeleteOrgDialog({
  orgId,
  orgName,
  disabled = false,
}: {
  orgId: string;
  orgName: string;
  disabled?: boolean;
}) {
  const [open, setOpen] = useState(false);
  const [confirm, setConfirm] = useState("");
  const [pending, setPending] = useState(false);

  const canDelete = confirm.trim() === orgName.trim();

  async function remove() {
    if (!canDelete) return;
    setPending(true);
    const res = await authClient.organization.delete({
      organizationId: orgId,
    });
    if (res.error) {
      setPending(false);
      toast.error(
        res.error.message ?? "Impossible de supprimer l'organisation.",
      );
      return;
    }
    // Org (and our membership) is gone — leave the app shell.
    window.location.href = "/keywords/reservoir";
  }

  return (
    <AlertDialog
      open={open}
      onOpenChange={(o) => {
        setOpen(o);
        if (!o) {
          setConfirm("");
        }
      }}
    >
      <AlertDialogTrigger asChild>
        <Button variant="destructive" disabled={disabled}>
          Supprimer l'organisation
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>
            Supprimer définitivement l'organisation&nbsp;?
          </AlertDialogTitle>
          <AlertDialogDescription>
            Cette action est irréversible. Tous les membres, invitations et
            données associées seront supprimés. Pour confirmer, saisis le nom de
            l'organisation&nbsp;: <strong>{orgName}</strong>.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <Field>
          <FieldLabel htmlFor="delete-org-confirm">
            Nom de l'organisation
          </FieldLabel>
          <Input
            id="delete-org-confirm"
            value={confirm}
            onChange={(e) => setConfirm(e.target.value)}
            placeholder={orgName}
            autoComplete="off"
          />
        </Field>
        <AlertDialogFooter>
          <AlertDialogCancel disabled={pending}>Annuler</AlertDialogCancel>
          <Button
            variant="destructive"
            onClick={(e) => {
              e.preventDefault();
              void remove();
            }}
            disabled={pending || !canDelete}
          >
            {pending ? "…" : "Supprimer définitivement"}
          </Button>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}
