"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useState } from "react";
import { create } from "@bufbuild/protobuf";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { IamPolicySpecSchema, ApiResourceRefSchema, } from "@stigmer/protos/ai/stigmer/iam/iampolicy/v1/spec_pb";
import { cn } from "@stigmer/theme";
import { getUserMessage, iamRoleFromString, iamRoleToString, } from "@stigmer/sdk";
import { useResourceAccess } from "./useResourceAccess";
import { usePrincipalsCount } from "./usePrincipalsCount";
import { useWhoAmI } from "./useWhoAmI";
import { useRevokeOrgAccess } from "./useRevokeOrgAccess";
import { useCreateIamPolicy } from "./useCreateIamPolicy";
import { useDeleteIamPolicy } from "./useDeleteIamPolicy";
import { RoleSelector } from "./RoleSelector";
/**
 * Self-contained panel for managing organization members.
 *
 * Displays all principals with access to the organization, their
 * role grants, and provides actions to change roles and remove
 * members. New members are added through the invitation flow.
 * The current user is identified via
 * `identityAccount.whoAmI()` for self-protection (disabling
 * destructive actions on yourself).
 *
 * All visual properties flow through `--stgm-*` design tokens.
 *
 * @example
 * ```tsx
 * <OrgMembersPanel orgId={activeOrg.metadata.id} />
 * ```
 */
export function OrgMembersPanel({ orgId, onRefetchRef, className, }) {
    const resource = orgId ? { kind: "organization", id: orgId } : null;
    const { members, isLoading, error, refetch } = useResourceAccess(resource);
    const { count, refetch: refetchCount } = usePrincipalsCount(orgId || null);
    const { account: currentAccount } = useWhoAmI();
    const currentAccountId = currentAccount?.metadata?.id ?? null;
    const [actionMemberId, setActionMemberId] = useState(null);
    const handleRefetch = useCallback(() => {
        refetch();
        refetchCount();
    }, [refetch, refetchCount]);
    if (onRefetchRef) {
        onRefetchRef(handleRefetch);
    }
    if (isLoading) {
        return (_jsx("div", { className: cn("space-y-2", className), "aria-busy": "true", "aria-label": "Loading members", children: Array.from({ length: 3 }, (_, i) => (_jsx("div", { className: "bg-muted-subtle h-14 animate-pulse rounded-lg" }, i))) }));
    }
    if (error) {
        return (_jsx("p", { className: cn("text-destructive text-xs", className), role: "alert", children: getUserMessage(error) }));
    }
    return (_jsxs("div", { className: cn("space-y-3", className), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-semibold text-foreground", children: "Members" }), count > 0 && (_jsx("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[0.65rem] font-medium text-muted-foreground", children: count }))] }), members.length === 0 ? (_jsx("p", { className: "text-muted-foreground py-4 text-center text-xs", children: "No members found." })) : (_jsx("div", { role: "list", "aria-label": "Organization members", className: "space-y-2", children: members.map((entry) => {
                    const memberId = entry.principal?.id ?? "";
                    return (_jsx(MemberRow, { entry: entry, orgId: orgId, isSelf: memberId === currentAccountId, isActioning: actionMemberId === memberId, onStartAction: () => setActionMemberId(memberId), onEndAction: () => setActionMemberId(null), onMutated: handleRefetch }, memberId));
                }) }))] }));
}
// ---------------------------------------------------------------------------
// MemberRow (internal)
// ---------------------------------------------------------------------------
function MemberRow({ entry, orgId, isSelf, isActioning, onStartAction, onEndAction, onMutated, }) {
    const [actionType, setActionType] = useState(null);
    const principal = entry.principal;
    const memberId = principal?.id ?? "";
    const name = principal?.name || principal?.email || memberId;
    const email = principal?.email ?? "";
    const initial = name.charAt(0).toUpperCase();
    const directRoles = entry.roles.filter((r) => !r.isInherited);
    const startAction = (type) => {
        setActionType(type);
        onStartAction();
    };
    const cancelAction = () => {
        setActionType(null);
        onEndAction();
    };
    if (isActioning && actionType === "remove") {
        return (_jsx(RemoveConfirmation, { memberId: memberId, memberName: name, orgId: orgId, onDone: () => {
                cancelAction();
                onMutated();
            }, onCancel: cancelAction }));
    }
    if (isActioning && actionType === "change-role") {
        return (_jsx(ChangeRoleRow, { entry: entry, orgId: orgId, onDone: () => {
                cancelAction();
                onMutated();
            }, onCancel: cancelAction }));
    }
    return (_jsxs("div", { role: "listitem", className: "flex items-center gap-3 rounded-lg border border-border-muted px-3 py-2.5 hover:border-border transition-colors", children: [_jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground", children: initial }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: name }), isSelf && (_jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[0.6rem] font-medium text-muted-foreground", children: "You" }))] }), email && email !== name && (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: email }))] }), _jsx("div", { className: "hidden sm:flex shrink-0 items-center gap-1.5", children: directRoles.map((grant) => (_jsx(RoleBadge, { grant: grant }, grant.role?.code ?? ""))) }), !isSelf && (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => startAction("change-role"), "aria-label": `Change role for ${name}`, className: "shrink-0 rounded p-1 text-muted-foreground hover:text-foreground hover:bg-accent-hover transition-colors", children: _jsx(EditIcon, {}) }), _jsx("button", { type: "button", onClick: () => startAction("remove"), "aria-label": `Remove ${name}`, className: "shrink-0 rounded p-1 text-muted-foreground hover:text-destructive hover:bg-destructive-subtle transition-colors", children: _jsx(TrashIcon, {}) })] }))] }));
}
// ---------------------------------------------------------------------------
// RoleBadge (internal)
// ---------------------------------------------------------------------------
function RoleBadge({ grant }) {
    const roleName = grant.role?.name || grant.role?.code || "Unknown";
    return (_jsxs("span", { className: cn("inline-flex items-center rounded-md border px-2 py-0.5 text-[0.65rem] font-medium", grant.isInherited
            ? "border-border-muted text-muted-foreground bg-muted-subtle italic"
            : "border-border bg-muted text-foreground"), title: grant.isInherited
            ? `Inherited from ${grant.ownerResource?.kind ?? "parent"}`
            : `Directly assigned`, children: [roleName, grant.isInherited && (_jsx("span", { className: "ml-1 text-[0.55rem] text-muted-foreground", children: "(inherited)" }))] }));
}
// ---------------------------------------------------------------------------
// RemoveConfirmation (internal)
// ---------------------------------------------------------------------------
function RemoveConfirmation({ memberId, memberName, orgId, onDone, onCancel, }) {
    const { revoke, isRevoking, error } = useRevokeOrgAccess();
    const handleConfirm = useCallback(async () => {
        try {
            await revoke(memberId, orgId);
            onDone();
        }
        catch {
            // error state is surfaced via the hook
        }
    }, [memberId, orgId, revoke, onDone]);
    return (_jsxs("div", { role: "listitem", className: "flex items-center justify-between rounded-lg border border-destructive/30 bg-destructive-subtle px-3 py-2.5", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("p", { className: "text-xs text-foreground", children: ["Remove ", _jsx("span", { className: "font-medium", children: memberName }), " from this organization? This revokes all their access."] }), error && (_jsx("p", { className: "mt-0.5 text-[0.65rem] text-destructive", children: getUserMessage(error) }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 ml-3", children: [_jsxs("button", { type: "button", onClick: handleConfirm, disabled: isRevoking, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium", "bg-destructive text-destructive-foreground hover:bg-destructive-hover", "disabled:pointer-events-none disabled:opacity-50"), children: [isRevoking && _jsx(SpinnerIcon, {}), "Remove"] }), _jsx("button", { type: "button", onClick: onCancel, disabled: isRevoking, className: cn("rounded-md px-2.5 py-1 text-xs", "text-muted-foreground hover:text-foreground hover:bg-accent-hover", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] }));
}
// ---------------------------------------------------------------------------
// ChangeRoleRow (internal)
// ---------------------------------------------------------------------------
function ChangeRoleRow({ entry, orgId, onDone, onCancel, }) {
    const { create: createPolicy, isCreating } = useCreateIamPolicy();
    const { remove: deletePolicy, isDeleting } = useDeleteIamPolicy();
    const memberId = entry.principal?.id ?? "";
    const memberName = entry.principal?.name || entry.principal?.email || memberId;
    const directRoles = entry.roles.filter((r) => !r.isInherited);
    const currentRoleCode = directRoles[0]?.role?.code;
    const currentRole = currentRoleCode
        ? iamRoleFromString(currentRoleCode) ?? null
        : null;
    const [selectedRole, setSelectedRole] = useState(null);
    const [error, setError] = useState(null);
    const isWorking = isCreating || isDeleting;
    const canConfirm = selectedRole !== null && selectedRole !== currentRole && !isWorking;
    const handleConfirm = useCallback(async () => {
        if (!selectedRole || !currentRoleCode)
            return;
        setError(null);
        try {
            const deleteSpec = create(IamPolicySpecSchema, {
                principal: create(ApiResourceRefSchema, {
                    kind: "identity_account",
                    id: memberId,
                }),
                resource: create(ApiResourceRefSchema, {
                    kind: "organization",
                    id: orgId,
                }),
                relation: currentRoleCode,
            });
            await deletePolicy(deleteSpec);
            const createSpec = create(IamPolicySpecSchema, {
                principal: create(ApiResourceRefSchema, {
                    kind: "identity_account",
                    id: memberId,
                }),
                resource: create(ApiResourceRefSchema, {
                    kind: "organization",
                    id: orgId,
                }),
                relation: iamRoleToString(selectedRole),
            });
            await createPolicy(createSpec);
            onDone();
        }
        catch (err) {
            setError(err instanceof Error ? err : new Error("Failed to change role"));
        }
    }, [
        selectedRole,
        currentRoleCode,
        memberId,
        orgId,
        deletePolicy,
        createPolicy,
        onDone,
    ]);
    return (_jsxs("div", { role: "listitem", className: "rounded-lg border border-primary/30 bg-primary-subtle px-3 py-2.5 space-y-2", children: [_jsxs("p", { className: "text-xs text-foreground", children: ["Change role for ", _jsx("span", { className: "font-medium", children: memberName })] }), _jsx(RoleSelector, { kind: ApiResourceKind.organization, selected: selectedRole ?? currentRole, onSelect: setSelectedRole, disabled: isWorking }), error && (_jsx("p", { className: "text-[0.65rem] text-destructive", role: "alert", children: getUserMessage(error) })), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs("button", { type: "button", onClick: handleConfirm, disabled: !canConfirm, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium", "bg-primary text-primary-foreground hover:bg-primary-hover", "disabled:pointer-events-none disabled:opacity-40"), children: [isWorking && _jsx(SpinnerIcon, {}), isWorking ? "Changing role…" : "Confirm"] }), _jsx("button", { type: "button", onClick: onCancel, disabled: isWorking, className: cn("rounded-md px-2.5 py-1 text-xs", "text-muted-foreground hover:text-foreground hover:bg-accent-hover", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] }));
}
// ---------------------------------------------------------------------------
// Icons
// ---------------------------------------------------------------------------
function EditIcon() {
    return (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M11.5 1.5l3 3L5 14H2v-3L11.5 1.5z" }) }));
}
function TrashIcon() {
    return (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M2.5 4h11M5.5 4V2.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1V4" }), _jsx("path", { d: "M12.5 4v9a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1V4" }), _jsx("line", { x1: "6.5", y1: "7", x2: "6.5", y2: "11" }), _jsx("line", { x1: "9.5", y1: "7", x2: "9.5", y2: "11" })] }));
}
function SpinnerIcon() {
    return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", className: "animate-spin", "aria-hidden": "true", children: _jsx("path", { d: "M8 2a6 6 0 1 0 6 6" }) }));
}
//# sourceMappingURL=OrgMembersPanel.js.map