import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import {
  ArrowLeft,
  BadgeCheck,
  Building2,
  Copy,
  Eye,
  EyeOff,
  History,
  Key,
  Loader2,
  Mail,
  Plus,
  RefreshCw,
  Save,
  Shield,
  ShieldAlert,
  Trash2,
  Users,
  X,
} from "lucide-react";
import * as React from "react";
import {
  type FieldErrors,
  type UseFormRegister,
  useForm,
} from "react-hook-form";
import { useNavigate, useParams } from "react-router-dom";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "../../components/ui/card";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "../../components/ui/dialog";
import { Input } from "../../components/ui/input";
import { Label } from "../../components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "../../components/ui/select";
import { Switch } from "../../components/ui/switch";
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "../../components/ui/tabs";
import { toast } from "../../components/ui/use-toast";
import type {
  GlobalCustomClaimDefinition,
  PasswordPolicyResponse,
} from "../../lib/adminApi";
import {
  deleteUser,
  fetchAllTenants,
  fetchGlobalCustomClaimDefinitions,
  fetchMe,
  fetchPasswordPolicy,
  fetchTenant,
  fetchUser,
  fetchUserRpHistory,
  type TenantSummary,
  type UserAppointment,
  type UserUpdateRequest,
  updateUser,
} from "../../lib/adminApi";
import { t } from "../../lib/i18n";
import {
  canManageUserInTenantScope,
  normalizeAdminRole,
} from "../../lib/roles";
import { generateSecurePassword } from "../../lib/utils";
import {
  buildAuthenticatedOrgChartTenantPickerUrl,
  filterNonHanmacFamilyTenants,
  getTenantGradeOptions,
  isHanmacFamilyTenant,
  isHanmacFamilyUser,
  type OrgChartTenantSelection,
  parseOrgChartTenantSelection,
} from "./orgChartPicker";
import type { UserSchemaField } from "./userSchemaFields";
import {
  normalizeUserStatusValue,
  userStatusLabel,
  userStatusValues,
} from "./userStatus";
import { resolvePersonalTenant } from "./utils/personalTenant";

type UserFormValues = Omit<UserUpdateRequest, "metadata"> & {
  email: string;
  metadata: Record<string, unknown> & {
    employee_id?: string;
    sub_email?: string | string[];
  };
};
type UserCategory = "hanmac" | "external" | "personal";

type PasswordResetMode = "generated" | "manual";
type PickerTarget = { kind: "appointment"; index: number };
type AppointmentDraft = UserAppointment & {
  draftId: string;
};
type GlobalCustomClaimType =
  | "text"
  | "number"
  | "boolean"
  | "array"
  | "object"
  | "date"
  | "datetime";
type CustomClaimPermission = "admin_only" | "user_and_admin";
type GlobalCustomClaimRow = {
  id: string;
  key: string;
  label: string;
  value: string;
  valueType: GlobalCustomClaimType;
  readPermission: CustomClaimPermission;
  writePermission: CustomClaimPermission;
  description?: string;
};

const PASSWORD_RESET_MIN_LENGTH = 12;

function isMetadataRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function normalizeCustomClaimPermission(
  value: unknown,
  fallback: CustomClaimPermission,
): CustomClaimPermission {
  return value === "admin_only" || value === "user_and_admin"
    ? value
    : fallback;
}

function cleanMetadataValue(value: unknown): unknown {
  if (Array.isArray(value)) {
    return value
      .filter((item): item is string => typeof item === "string")
      .map((item) => item.trim())
      .filter(Boolean);
  }
  if (isMetadataRecord(value)) {
    return Object.fromEntries(
      Object.entries(value).filter(
        ([_, fieldValue]) =>
          fieldValue !== undefined && fieldValue !== null && fieldValue !== "",
      ),
    );
  }
  return value;
}

function normalizeEmployeeIDMetadataValue(value: unknown) {
  if (typeof value === "string" || typeof value === "number") {
    return String(value).trim();
  }
  if (!isMetadataRecord(value)) {
    return "";
  }
  const entries = Object.entries(value)
    .map(([key, fieldValue]) => ({
      index: Number(key),
      value: typeof fieldValue === "string" ? fieldValue : "",
    }))
    .filter((entry) => Number.isInteger(entry.index) && entry.value.length > 0)
    .sort((a, b) => a.index - b.index);
  if (entries.length === 0) {
    return "";
  }
  return entries
    .map((entry) => entry.value)
    .join("")
    .trim();
}

function normalizeSubEmails(value: unknown): string[] {
  if (Array.isArray(value)) {
    return value
      .filter((item): item is string => typeof item === "string")
      .map((item) => item.trim())
      .filter((item) => item.includes("@"));
  }
  if (typeof value === "string" && value.trim() !== "") {
    return value
      .split(/[;,\n\r\t]/)
      .map((email) => email.trim())
      .filter((email) => email.includes("@"));
  }
  return [];
}

function createDraftId() {
  return globalThis.crypto?.randomUUID?.() ?? `appointment-${Date.now()}`;
}

function createGlobalCustomClaimRows(
  metadata: Record<string, unknown>,
  definitions: GlobalCustomClaimDefinition[],
): GlobalCustomClaimRow[] {
  const rawClaims = isMetadataRecord(metadata.global_custom_claims)
    ? metadata.global_custom_claims
    : {};
  const rawPermissions = isMetadataRecord(
    metadata.global_custom_claim_permissions,
  )
    ? metadata.global_custom_claim_permissions
    : {};

  return definitions.map((definition, index) => {
    const value = rawClaims[definition.key];
    const rawPermission = rawPermissions[definition.key];
    const permission: Record<string, unknown> = isMetadataRecord(rawPermission)
      ? rawPermission
      : {};
    return {
      id: `${definition.key}-${index}`,
      key: definition.key,
      label: definition.label,
      description: definition.description,
      value:
        typeof value === "string"
          ? value
          : value == null
            ? ""
            : JSON.stringify(value),
      valueType: definition.valueType,
      readPermission: normalizeCustomClaimPermission(
        permission.readPermission,
        definition.readPermission,
      ),
      writePermission: normalizeCustomClaimPermission(
        permission.writePermission,
        definition.writePermission,
      ),
    };
  });
}

function globalCustomClaimInputType(valueType: GlobalCustomClaimType) {
  if (valueType === "date") {
    return "date";
  }
  if (valueType === "datetime") {
    return "datetime-local";
  }
  if (valueType === "number") {
    return "number";
  }
  return "text";
}

function globalCustomClaimRowsToMetadata(rows: GlobalCustomClaimRow[]) {
  const claims: Record<string, unknown> = {};
  const types: Record<string, GlobalCustomClaimType> = {};
  const permissions: Record<
    string,
    {
      readPermission: CustomClaimPermission;
      writePermission: CustomClaimPermission;
    }
  > = {};

  for (const row of rows) {
    const key = row.key.trim();
    if (!key) {
      continue;
    }
    claims[key] = row.value.trim();
    types[key] = row.valueType;
    permissions[key] = {
      readPermission: row.readPermission,
      writePermission: row.writePermission,
    };
  }

  return { claims, types, permissions };
}

async function resolveTenantSelection(
  selection: OrgChartTenantSelection,
  tenants: TenantSummary[],
) {
  const cached = tenants.find((tenant) => tenant.id === selection.id);
  if (cached) {
    return {
      id: cached.id,
      name: cached.name,
      slug: cached.slug,
    };
  }

  const tenant = await fetchTenant(selection.id);
  return {
    id: tenant.id,
    name: tenant.name,
    slug: tenant.slug,
  };
}

function getTenantVisibility(tenant?: TenantSummary) {
  const value = tenant?.config?.visibility;
  return typeof value === "string" ? value.trim().toLowerCase() : "public";
}

function isPrivateTenant(tenant?: TenantSummary) {
  return getTenantVisibility(tenant) === "private";
}

function appointmentTenantsFromMetadata(
  metadata: Record<string, unknown> | undefined,
  tenants: TenantSummary[],
) {
  const rawAppointments = metadata?.additionalAppointments;
  if (!Array.isArray(rawAppointments)) {
    return [];
  }

  return rawAppointments
    .map((raw) => {
      if (!raw || typeof raw !== "object") {
        return null;
      }
      const appointment = raw as Record<string, unknown>;
      const tenantId =
        typeof appointment.tenantId === "string" ? appointment.tenantId : "";
      const tenantSlug =
        typeof appointment.tenantSlug === "string"
          ? appointment.tenantSlug
          : typeof appointment.slug === "string"
            ? appointment.slug
            : "";
      return tenants.find(
        (tenant) =>
          (tenantId && tenant.id === tenantId) ||
          (tenantSlug && tenant.slug === tenantSlug),
      );
    })
    .filter((tenant): tenant is TenantSummary => Boolean(tenant))
    .filter((tenant) => !isPrivateTenant(tenant));
}

function createEmptyAppointment(): AppointmentDraft {
  return {
    draftId: createDraftId(),
    tenantId: "",
    tenantName: "",
    tenantSlug: "",
    isPrimary: false,
    isOwner: false,
    isAdmin: false,
    isManager: false,
    grade: "",
    jobTitle: "",
    position: "",
  };
}

function validateManualPassword(
  password: string,
  policy?: PasswordPolicyResponse,
) {
  if (password.trim().length === 0) {
    return t(
      "msg.admin.users.detail.password_manual_required",
      "비밀번호를 입력해 주세요.",
    );
  }

  const minLength = policy?.minLength ?? PASSWORD_RESET_MIN_LENGTH;
  if (password.length < minLength) {
    return t(
      "msg.userfront.reset.error.min_length",
      "비밀번호는 최소 {{count}}자 이상이어야 합니다.",
      { count: String(minLength) },
    );
  }

  const hasLower = /[a-z]/.test(password);
  const hasUpper = /[A-Z]/.test(password);
  const hasNumber = /[0-9]/.test(password);
  const hasSymbol = /[\W_]/.test(password);
  let typeCount = 0;
  if (hasLower) typeCount++;
  if (hasUpper) typeCount++;
  if (hasNumber) typeCount++;
  if (hasSymbol) typeCount++;

  const minTypes = policy?.minCharacterTypes ?? 0;
  if (minTypes > 0 && typeCount < minTypes) {
    return t(
      "msg.userfront.reset.error.min_types",
      "비밀번호는 영문 대/소문자/숫자/특수문자 중 {{count}}가지 이상 포함해야 합니다.",
      { count: String(minTypes) },
    );
  }

  if ((policy?.lowercase ?? true) && !hasLower) {
    return t(
      "msg.userfront.reset.error.lowercase",
      "최소 1개 이상의 소문자를 포함해야 합니다.",
    );
  }
  if ((policy?.uppercase ?? false) && !hasUpper) {
    return t(
      "msg.userfront.reset.error.uppercase",
      "최소 1개 이상의 대문자를 포함해야 합니다.",
    );
  }
  if ((policy?.number ?? true) && !hasNumber) {
    return t(
      "msg.userfront.reset.error.number",
      "최소 1개 이상의 숫자를 포함해야 합니다.",
    );
  }
  if ((policy?.nonAlphanumeric ?? true) && !hasSymbol) {
    return t(
      "msg.userfront.reset.error.symbol",
      "최소 1개 이상의 특수문자를 포함해야 합니다.",
    );
  }

  return null;
}

function TenantMetadataFields({
  tenant,
  schema,
  register,
  errors,
}: {
  tenant: { id: string; name: string; slug: string };
  schema: UserSchemaField[];
  register: UseFormRegister<UserFormValues>;
  errors: FieldErrors<UserFormValues>;
}) {
  return (
    <div className="rounded-xl border border-border bg-card overflow-hidden shadow-sm">
      <div className="bg-muted/30 px-5 py-3 border-b border-border flex items-center justify-between">
        <div className="flex items-center gap-2">
          <Building2 size={16} className="text-primary" />
          <span className="text-sm font-bold uppercase tracking-tight">
            {tenant.name}
          </span>
        </div>
        <span className="text-[10px] font-mono opacity-50 bg-background px-2 py-0.5 rounded border">
          {tenant.slug}
        </span>
      </div>
      <div className="p-6 grid gap-6 md:grid-cols-2">
        {schema.length === 0 ? (
          <p className="text-sm text-muted-foreground md:col-span-2">
            {t(
              "msg.admin.users.detail.tenant_schema_empty",
              "이 테넌트에 설정된 프로필 필드가 없습니다.",
            )}
          </p>
        ) : (
          schema.map((field) => (
            <div key={field.key} className="space-y-2">
              <Label
                htmlFor={`metadata.${tenant.id}.${field.key}`}
                className="text-xs font-semibold text-muted-foreground flex items-center gap-1"
              >
                {field.label}
                {field.required && <span className="text-destructive">*</span>}
                {field.adminOnly && (
                  <span className="ml-2 text-[9px] bg-blue-500/10 text-blue-500 px-1.5 py-0.5 rounded uppercase font-bold">
                    Admin Only
                  </span>
                )}
                {field.isLoginId && (
                  <span className="ml-2 text-[9px] bg-green-500/10 text-green-600 px-1.5 py-0.5 rounded uppercase font-bold">
                    {t("ui.admin.users.detail.form.is_login_id", "로그인 ID")}
                  </span>
                )}
              </Label>
              <Input
                id={`metadata.${tenant.id}.${field.key}`}
                type={
                  field.type === "number"
                    ? "number"
                    : field.type === "date"
                      ? "date"
                      : field.type === "boolean"
                        ? "checkbox"
                        : "text"
                }
                className={
                  field.type === "boolean" ? "w-5 h-5" : "h-10 text-sm"
                }
                {...register(`metadata.${tenant.id}.${field.key}` as const, {
                  required: field.required
                    ? t(
                        "msg.admin.users.detail.form.field_required",
                        "필수입니다.",
                      )
                    : false,
                  pattern: field.validation
                    ? {
                        value: new RegExp(field.validation),
                        message: t(
                          "msg.admin.users.detail.form.invalid_format",
                          "형식이 올바르지 않습니다.",
                        ),
                      }
                    : undefined,
                })}
              />
              {(
                errors.metadata as unknown as Record<
                  string,
                  Record<string, { message?: string }>
                >
              )?.[tenant.id]?.[field.key] && (
                <p className="text-[10px] text-destructive font-medium">
                  {
                    (
                      errors.metadata as unknown as Record<
                        string,
                        Record<string, { message?: string }>
                      >
                    )?.[tenant.id]?.[field.key]?.message
                  }
                </p>
              )}
            </div>
          ))
        )}
      </div>
    </div>
  );
}

function UserDetailPage() {
  const params = useParams<{ id: string }>();
  const userId = params.id ?? "";
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const [_error, _setError] = React.useState<string | null>(null);
  const [_successMsg, _setSuccessMsg] = React.useState<string | null>(null);
  const [isPasswordResetOpen, setIsPasswordResetOpen] = React.useState(false);
  const [generatedPassword, setGeneratedPassword] = React.useState<
    string | null
  >(null);
  const [passwordResetMode, setPasswordResetMode] =
    React.useState<PasswordResetMode>("generated");
  const [manualPassword, setManualPassword] = React.useState("");
  const [manualPasswordConfirm, setManualPasswordConfirm] = React.useState("");
  const [isManualPasswordVisible, setIsManualPasswordVisible] =
    React.useState(false);
  const [passwordResetError, setPasswordResetError] = React.useState<
    string | null
  >(null);
  const [userCategory, setUserCategory] =
    React.useState<UserCategory>("external");
  const [additionalAppointments, setAdditionalAppointments] = React.useState<
    AppointmentDraft[]
  >([]);
  const [globalCustomClaimRows, setGlobalCustomClaimRows] = React.useState<
    GlobalCustomClaimRow[]
  >([]);
  const [pickerTarget, setPickerTarget] = React.useState<PickerTarget | null>(
    null,
  );
  const [isResolvingTenant, setIsResolvingTenant] = React.useState(false);

  const [activeTab, setActiveTab] = React.useState("info");

  const { data: profile } = useQuery({
    queryKey: ["me"],
    queryFn: fetchMe,
  });

  const {
    data: user,
    isLoading,
    isError,
  } = useQuery({
    queryKey: ["user", userId],
    queryFn: () => fetchUser(userId),
    enabled: userId.length > 0 && userId !== "new",
  });

  const { data: tenantsData } = useQuery({
    queryKey: ["tenants", "all"],
    queryFn: () => fetchAllTenants(),
  });
  const tenants = React.useMemo(
    () => tenantsData?.items ?? [],
    [tenantsData?.items],
  );

  const rpHistoryQuery = useQuery({
    queryKey: ["user-rp-history", userId],
    queryFn: () => fetchUserRpHistory(userId),
    enabled: !!userId && userId !== "new",
  });

  const { data: passwordPolicy } = useQuery({
    queryKey: ["password-policy"],
    queryFn: fetchPasswordPolicy,
  });
  const { data: globalCustomClaimDefinitionsData } = useQuery({
    queryKey: ["global-custom-claim-definitions"],
    queryFn: fetchGlobalCustomClaimDefinitions,
  });
  const globalCustomClaimDefinitions = React.useMemo(
    () => globalCustomClaimDefinitionsData?.items ?? [],
    [globalCustomClaimDefinitionsData?.items],
  );

  const {
    register,
    handleSubmit,
    reset,
    watch,
    setValue,
    formState: { errors },
  } = useForm<UserFormValues>({
    defaultValues: {
      name: "",
      phone: "",
      role: "user",
      status: "active",
      tenantSlug: "",
      department: "",
      grade: "",
      position: "",
      jobTitle: "",
      metadata: {},
    },
  });

  const profileRole = normalizeAdminRole(profile?.role);
  const isAdmin = profileRole === "super_admin";
  const isSelf = Boolean(profile?.id && user?.id && profile.id === user.id);
  const canManageCurrentUser = canManageUserInTenantScope({ profile, user });
  const watchedStatus = watch("status");

  const [newSubEmail, setNewSubEmail] = React.useState("");
  const currentSubEmails = (watch("metadata.sub_email") as string[]) || [];

  const handleAddSubEmail = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter" || e.key === "," || e.key === " ") {
      e.preventDefault();
      const value = newSubEmail.trim().replace(/,/g, "");
      if (value?.includes("@") && !currentSubEmails.includes(value)) {
        setValue("metadata.sub_email", [...currentSubEmails, value], {
          shouldDirty: true,
        });
        setNewSubEmail("");
      }
    }
  };

  const handleRemoveSubEmail = (emailToRemove: string) => {
    setValue(
      "metadata.sub_email",
      currentSubEmails.filter((e) => e !== emailToRemove),
      { shouldDirty: true },
    );
  };

  const resetMutation = useMutation({
    mutationFn: (newPass: string) => updateUser(userId, { password: newPass }),
    onSuccess: (_, newPass) => {
      setGeneratedPassword(newPass);
      setPasswordResetError(null);
      toast.success(
        t(
          "msg.admin.users.detail.password_generated",
          "사용자 비밀번호가 성공적으로 재설정되었습니다.",
        ),
      );
    },
    onError: (err: AxiosError<{ error?: string }>) => {
      const message =
        err.response?.data?.error ||
        t("msg.admin.users.detail.update_error", "수정에 실패했습니다.");
      setPasswordResetError(message);
      toast.error(message);
    },
  });

  const handleOpenPasswordReset = () => {
    if (isSelf) return;
    setIsPasswordResetOpen(true);
    setGeneratedPassword(null);
    setPasswordResetMode("generated");
    setManualPassword("");
    setManualPasswordConfirm("");
    setIsManualPasswordVisible(false);
    setPasswordResetError(null);
  };

  const handleClosePasswordReset = () => {
    setIsPasswordResetOpen(false);
    setGeneratedPassword(null);
    setPasswordResetError(null);
  };

  const handleExecutePasswordReset = () => {
    if (isSelf) return;

    let newPass = manualPassword;

    if (passwordResetMode === "manual") {
      const vErr = validateManualPassword(manualPassword, passwordPolicy);
      if (vErr) {
        setPasswordResetError(vErr);
        return;
      }
      if (manualPassword !== manualPasswordConfirm) {
        setPasswordResetError(
          t(
            "msg.userfront.reset.error.mismatch",
            "비밀번호가 일치하지 않습니다.",
          ),
        );
        return;
      }
    } else {
      newPass = generateSecurePassword();
    }

    resetMutation.mutate(newPass);
  };

  const hanmacFamilyTenantId = React.useMemo(() => {
    const envTenantId = import.meta.env.VITE_HANMAC_FAMILY_TENANT_ID;
    if (typeof envTenantId === "string" && envTenantId.trim()) {
      return envTenantId.trim();
    }
    return tenants.find((tenant) => tenant.slug === "hanmac-family")?.id ?? "";
  }, [tenants]);
  const personalTenant = React.useMemo(
    () => resolvePersonalTenant(tenants),
    [tenants],
  );

  const pickerUrl = buildAuthenticatedOrgChartTenantPickerUrl(
    import.meta.env.ORGFRONT_URL,
    {
      tenantId: userCategory === "hanmac" ? hanmacFamilyTenantId : undefined,
    },
  );

  const applyTenantSelection = React.useCallback(
    async (selection: OrgChartTenantSelection, target: PickerTarget) => {
      setIsResolvingTenant(true);
      try {
        const tenant = await resolveTenantSelection(selection, tenants);
        setAdditionalAppointments((current) =>
          current.map((appointment, index) =>
            index === target.index
              ? {
                  ...appointment,
                  tenantId: tenant.id,
                  tenantName: tenant.name,
                  tenantSlug: tenant.slug,
                }
              : appointment,
          ),
        );
        setPickerTarget(null);
      } catch (_) {
        toast.error(
          t(
            "msg.admin.users.detail.tenant_resolve_failed",
            "선택한 테넌트 정보를 불러오지 못했습니다.",
          ),
        );
      } finally {
        setIsResolvingTenant(false);
      }
    },
    [tenants],
  );

  React.useEffect(() => {
    if (!pickerTarget) return;

    const onMessage = (event: MessageEvent) => {
      const selection = parseOrgChartTenantSelection(event.data);
      if (!selection) return;
      void applyTenantSelection(selection, pickerTarget);
    };

    window.addEventListener("message", onMessage);
    return () => window.removeEventListener("message", onMessage);
  }, [applyTenantSelection, pickerTarget]);

  const addAppointment = () => {
    setAdditionalAppointments((current) => [
      ...current,
      createEmptyAppointment(),
    ]);
  };

  const updateAppointment = (
    index: number,
    patch: Partial<UserAppointment>,
  ) => {
    setAdditionalAppointments((current) =>
      current.map((appointment, currentIndex) => {
        if (currentIndex === index) {
          return { ...appointment, ...patch };
        }
        if (patch.isPrimary === true) {
          return { ...appointment, isPrimary: false };
        }
        return appointment;
      }),
    );
  };

  const removeAppointment = (index: number) => {
    setAdditionalAppointments((current) =>
      current.filter((_, currentIndex) => currentIndex !== index),
    );
  };

  const _setPrimaryAppointment = (targetIndex: number) => {
    setAdditionalAppointments((current) =>
      current.map((appointment, index) => ({
        ...appointment,
        isPrimary: index === targetIndex,
      })),
    );
  };

  const handleUserCategoryChange = (value: string) => {
    const nextCategory = value as UserCategory;
    setUserCategory(nextCategory);
    if (nextCategory !== "hanmac") {
      setAdditionalAppointments([]);
    }
  };

  const ensurePersonalTenant = async () => {
    return personalTenant;
  };

  React.useEffect(() => {
    if (user) {
      const metadata = (user.metadata ?? {}) as Record<string, unknown>;
      const rawAppointments = metadata.additionalAppointments;
      const primaryFromMetadata =
        typeof metadata.primaryTenantId === "string"
          ? {
              id: metadata.primaryTenantId,
              name:
                typeof metadata.primaryTenantName === "string"
                  ? metadata.primaryTenantName
                  : user.tenant?.name || user.tenantSlug || "",
              slug: user.tenantSlug,
            }
          : null;
      const fallbackAppointment =
        primaryFromMetadata ??
        (user.tenant
          ? {
              id: user.tenant.id,
              name: user.tenant.name,
              slug: user.tenant.slug,
            }
          : null);

      reset({
        email: user.email || "",
        name: user.name,
        phone: user.phone || "",
        role: user.role,
        status: normalizeUserStatusValue(user.status),
        tenantSlug:
          user.tenantSlug ||
          user.joinedTenants?.find(
            (t) => t.type === "COMPANY" || t.type === "COMPANY_GROUP",
          )?.slug ||
          "",
        department: user.department || "",
        grade: user.grade || "",
        position: user.position || "",
        jobTitle: user.jobTitle || "",
        metadata: {
          ...((user.metadata as unknown as Record<
            string,
            Record<string, string | number | boolean>
          >) || {}),
          employee_id: normalizeEmployeeIDMetadataValue(
            user.metadata?.employee_id,
          ),
          sub_email: Array.isArray(user.metadata?.sub_email)
            ? user.metadata.sub_email
            : typeof user.metadata?.sub_email === "string"
              ? user.metadata.sub_email
                  .split(/[;,\n\r\t]/)
                  .map((e) => e.trim())
                  .filter((e) => e.includes("@"))
              : [],
        } as UserFormValues["metadata"],
      });
      const isUserHanmacFamily = isHanmacFamilyUser(
        user,
        tenants,
        hanmacFamilyTenantId,
      );
      const isPersonalUser =
        user.tenantSlug === personalTenant.slug ||
        user.tenant?.id === personalTenant.id ||
        user.tenant?.slug === personalTenant.slug ||
        metadata.personalTenantId === personalTenant.id;
      const resolvedUserCategory = isPersonalUser
        ? "personal"
        : isUserHanmacFamily
          ? "hanmac"
          : "external";
      setUserCategory(resolvedUserCategory);
      setGlobalCustomClaimRows(
        createGlobalCustomClaimRows(metadata, globalCustomClaimDefinitions),
      );
      const familyFallbackTenants = [
        ...(user.joinedTenants ?? []),
        ...(user.tenant ? [user.tenant] : []),
      ].filter(
        (tenant, index, allTenants) =>
          allTenants.findIndex((item) => item.id === tenant.id) === index &&
          isHanmacFamilyTenant(tenant, tenants, hanmacFamilyTenantId),
      );
      setAdditionalAppointments(
        Array.isArray(rawAppointments)
          ? (rawAppointments as UserAppointment[]).map((appointment) => ({
              ...appointment,
              isPrimary:
                appointment.isPrimary === true ||
                appointment.tenantId === primaryFromMetadata?.id,
              isOwner: appointment.isOwner === true,
              isAdmin: appointment.isAdmin === true,
              isManager: appointment.isManager === true,
              draftId: createDraftId(),
            }))
          : isUserHanmacFamily
            ? familyFallbackTenants.length > 0
              ? familyFallbackTenants.map((tenant) => ({
                  draftId: createDraftId(),
                  tenantId: tenant.id,
                  tenantName: tenant.name,
                  tenantSlug: tenant.slug,
                  isPrimary: tenant.id === fallbackAppointment?.id,
                  isOwner:
                    metadata.primaryTenantIsOwner === true &&
                    tenant.id === fallbackAppointment?.id,
                  isAdmin: false,
                  isManager: false,
                  grade: user.grade,
                  jobTitle: user.jobTitle,
                  position: user.position,
                }))
              : fallbackAppointment
                ? [
                    {
                      draftId: createDraftId(),
                      tenantId: fallbackAppointment.id,
                      tenantName: fallbackAppointment.name,
                      tenantSlug: fallbackAppointment.slug,
                      isPrimary: true,
                      isOwner: metadata.primaryTenantIsOwner === true,
                      isAdmin: false,
                      isManager: false,
                      grade: user.grade,
                      jobTitle: user.jobTitle,
                      position: user.position,
                    },
                  ]
                : []
            : [],
      );
    }
  }, [
    globalCustomClaimDefinitions,
    hanmacFamilyTenantId,
    personalTenant,
    tenants,
    user,
    reset,
  ]);

  const mutation = useMutation({
    mutationFn: (data: UserUpdateRequest) => updateUser(userId, data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["users"] });
      queryClient.invalidateQueries({ queryKey: ["user", userId] });
      toast.success(t("msg.info.saved_success", "저장되었습니다."));
    },
    onError: (err: AxiosError<{ error?: string }>) => {
      toast.error(
        err.response?.data?.error ||
          t("err.common.unknown", "오류가 발생했습니다."),
      );
    },
  });

  const deleteMutation = useMutation({
    mutationFn: () => deleteUser(userId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["users"] });
      toast.success(
        t("msg.admin.users.detail.delete_success", "사용자가 삭제되었습니다."),
      );
      navigate("/users");
    },
  });

  const onSubmit = async (data: UserFormValues) => {
    const cleanMetadata = Object.fromEntries(
      Object.entries(data.metadata ?? {}).flatMap(([key, value]) => {
        const cleanedValue = cleanMetadataValue(value);
        if (
          cleanedValue === undefined ||
          cleanedValue === null ||
          cleanedValue === ""
        ) {
          return [];
        }
        return [[key, cleanedValue]];
      }),
    );

    const {
      hanmacFamily: _hanmacFamily,
      userType: _userType,
      sub_email: rawSubEmail,
      ...safeMetadata
    } = cleanMetadata;
    const subEmail = normalizeSubEmails(rawSubEmail);

    const metadata: Record<string, unknown> = {
      ...safeMetadata,
      ...(subEmail.length > 0 ? { sub_email: subEmail } : { sub_email: [] }),
    };
    const employeeID = String(data.metadata?.employee_id ?? "").trim();
    if (employeeID) {
      metadata.employee_id = employeeID;
    } else {
      delete metadata.employee_id;
    }

    const payload: UserUpdateRequest = {
      ...data,
      metadata,
    };
    if (profileRole !== "super_admin") {
      delete payload.email;
    } else {
      payload.email = data.email.trim();
    }
    payload.role = undefined;

    if (userCategory === "personal") {
      try {
        const tenant = await ensurePersonalTenant();
        payload.tenantSlug = tenant.slug;
        payload.department = undefined;
        payload.grade = undefined;
        payload.position = undefined;
        payload.jobTitle = undefined;
        payload.metadata = {
          ...metadata,
          personalTenantId: tenant.id,
        };
      } catch (_) {
        toast.error("Personal 테넌트를 준비하지 못했습니다.");
        return;
      }
    }

    if (userCategory === "hanmac") {
      const appointments = additionalAppointments
        .filter((appointment) => appointment.tenantId)
        .map((appointment) => ({
          tenantId: appointment.tenantId,
          tenantSlug: appointment.tenantSlug,
          tenantName: appointment.tenantName,
          isPrimary: appointment.isPrimary === true,
          ...(appointment.isOwner === true ? { isOwner: true } : {}),
          ...(appointment.isAdmin === true ? { isAdmin: true } : {}),
          ...(appointment.isManager === true ? { isManager: true } : {}),
          grade: appointment.grade,
          jobTitle: appointment.jobTitle,
          position: appointment.position,
        }));

      const primary = appointments.find((a) => a.isPrimary);
      if (primary) {
        payload.tenantSlug = primary.tenantSlug;
        payload.primaryTenantId = primary.tenantId;
        payload.primaryTenantName = primary.tenantName;
        metadata.primaryTenantId = primary.tenantId;
        metadata.primaryTenantSlug = primary.tenantSlug;
        metadata.primaryTenantName = primary.tenantName;
      } else {
        payload.tenantSlug = undefined;
      }

      payload.department = undefined;
      payload.grade = undefined;
      payload.position = undefined;
      payload.jobTitle = undefined;
      payload.additionalAppointments = appointments;
      payload.metadata = {
        ...metadata,
        additionalAppointments: appointments,
        primaryTenantId: primary?.tenantId,
        primaryTenantName: primary?.tenantName,
        primaryTenantSlug: primary?.tenantSlug,
      };
      payload.tenantSlug = primary?.tenantSlug;
      payload.primaryTenantId = primary?.tenantId;
      payload.primaryTenantName = primary?.tenantName;
    }

    mutation.mutate(payload);
  };

  const handleDelete = () => {
    if (
      window.confirm(
        t("msg.admin.users.detail.delete_confirm", "삭제하시겠습니까?"),
      )
    ) {
      deleteMutation.mutate();
    }
  };

  const updateGlobalCustomClaimRow = (
    id: string,
    patch: Partial<GlobalCustomClaimRow>,
  ) => {
    setGlobalCustomClaimRows((current) =>
      current.map((row) => (row.id === id ? { ...row, ...patch } : row)),
    );
  };

  const saveGlobalCustomClaims = () => {
    const { claims, types, permissions } = globalCustomClaimRowsToMetadata(
      globalCustomClaimRows,
    );
    mutation.mutate({
      metadata: {
        ...((user?.metadata as Record<string, unknown> | undefined) ?? {}),
        global_custom_claims: claims,
        global_custom_claim_types: types,
        global_custom_claim_permissions: permissions,
      },
    });
  };

  const userAffiliatedTenants = React.useMemo(() => {
    const joined = user?.joinedTenants || [];
    const primary = user?.tenant;
    const appointmentTenants = appointmentTenantsFromMetadata(
      user?.metadata as Record<string, unknown> | undefined,
      tenants,
    );
    const all = joined.filter((tenant) => {
      const fullTenant = tenants.find((item) => item.id === tenant.id);
      return !isPrivateTenant(fullTenant ?? tenant);
    });
    if (
      primary &&
      !isPrivateTenant(
        tenants.find((tenant) => tenant.id === primary.id) ?? primary,
      ) &&
      !all.some((t) => t.id === primary.id)
    ) {
      all.unshift(primary);
    }
    for (const tenant of appointmentTenants) {
      if (!all.some((item) => item.id === tenant.id)) {
        all.push(tenant);
      }
    }
    return all;
  }, [tenants, user?.joinedTenants, user?.metadata, user?.tenant]);
  const selectableRepresentativeTenants = React.useMemo(
    () =>
      filterNonHanmacFamilyTenants(userAffiliatedTenants, hanmacFamilyTenantId),
    [userAffiliatedTenants, hanmacFamilyTenantId],
  );

  if (isLoading) {
    return (
      <div className="flex h-64 items-center justify-center">
        <Loader2 className="h-8 w-8 animate-spin text-primary" />
      </div>
    );
  }

  if (isError || !user) {
    return (
      <div className="rounded-md bg-destructive/15 p-6 text-center mt-6">
        <p className="text-destructive font-medium">
          {t("msg.admin.users.detail.not_found", "사용자를 찾을 수 없습니다.")}
        </p>
        <Button
          variant="outline"
          className="mt-4"
          onClick={() => navigate("/users")}
        >
          {t("ui.admin.users.detail.go_list", "목록으로 이동")}
        </Button>
      </div>
    );
  }

  if (!isAdmin && !isSelf && !canManageCurrentUser) {
    return (
      <div className="flex h-[50vh] flex-col items-center justify-center space-y-4">
        <ShieldAlert size={48} className="text-destructive" />
        <h3 className="text-lg font-bold">
          {t("msg.admin.common.forbidden", "이 작업을 수행할 권한이 없습니다.")}
        </h3>
        <Button onClick={() => navigate("/")}>
          {t("ui.common.go_home", "홈으로 이동")}
        </Button>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header with back button and actions */}
      <div className="flex items-center justify-between">
        <Button
          variant="ghost"
          onClick={() => navigate("/users")}
          size="sm"
          className="hover:bg-muted"
        >
          <ArrowLeft className="mr-2 h-4 w-4" />
          {t("ui.admin.users.detail.back", "목록으로")}
        </Button>
        {isAdmin && (
          <Button variant="destructive" onClick={handleDelete} size="sm">
            <Trash2 className="mr-2 h-4 w-4" />
            {t("ui.admin.users.detail.delete", "사용자 삭제")}
          </Button>
        )}
      </div>

      {/* User Quick Summary Header */}
      <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 bg-[var(--color-panel)] border border-border rounded-2xl p-8 shadow-sm">
        <div className="flex items-center gap-6">
          <div className="h-20 w-20 rounded-2xl bg-primary/10 flex items-center justify-center text-primary shadow-inner">
            <Users size={40} />
          </div>
          <div className="space-y-2">
            <div className="flex items-center gap-3">
              <h1 className="text-3xl font-extrabold tracking-tight">
                {user.name}
              </h1>
              <Badge
                variant="outline"
                className="h-6 px-3 bg-blue-500/10 text-blue-600 border-blue-200 font-bold"
              >
                <Building2 size={12} className="mr-1.5" />
                {user.tenant?.name ||
                  user.tenantSlug ||
                  user.joinedTenants?.find(
                    (t) => t.type === "COMPANY" || t.type === "COMPANY_GROUP",
                  )?.name ||
                  t("ui.admin.users.detail.form.tenant_global", "시스템 전역")}
              </Badge>
              <Badge
                variant={user.status === "active" ? "default" : "secondary"}
                className="h-6 px-3"
              >
                {t(`ui.common.status.${user.status}`, user.status)}
              </Badge>
            </div>
            <div className="flex flex-wrap items-center gap-4 text-muted-foreground text-sm">
              <div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
                <Mail size={14} className="text-primary/70" />
                {user.email}
              </div>
              {normalizeSubEmails(user.metadata?.sub_email).length > 0 && (
                <div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
                  <Mail size={14} className="text-primary/40" />
                  <span className="text-[10px] font-bold">
                    +{normalizeSubEmails(user.metadata?.sub_email).length}
                  </span>
                </div>
              )}
              {user.phone && (
                <div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
                  <Shield size={14} className="text-primary/70" />
                  {user.phone}
                </div>
              )}
            </div>
          </div>
        </div>
        <div className="flex flex-col items-start md:items-end text-[10px] text-muted-foreground gap-1.5 uppercase tracking-widest font-bold opacity-70">
          <p>
            {t("ui.admin.users.detail.created_at", "가입일")}: {user.createdAt}
          </p>
          <p>
            {t("ui.admin.users.detail.updated_at", "최근 수정")}:{" "}
            {user.updatedAt}
          </p>
        </div>
      </div>

      <Tabs
        value={activeTab}
        onValueChange={setActiveTab}
        className="w-full space-y-6"
      >
        <TabsList className="bg-muted/50 p-1.5 rounded-xl inline-flex w-full md:w-auto">
          <TabsTrigger
            value="info"
            className="px-6 py-2 rounded-lg data-[state=active]:shadow-sm"
          >
            <Users size={16} className="mr-2" />
            {t("ui.admin.users.detail.tabs.info", "기본 정보")}
          </TabsTrigger>
          <TabsTrigger
            value="tenants"
            className="px-6 py-2 rounded-lg data-[state=active]:shadow-sm"
          >
            <Building2 size={16} className="mr-2" />
            {t("ui.admin.users.detail.tabs.tenants", "테넌트 프로필")}
          </TabsTrigger>
          <TabsTrigger
            value="customClaims"
            className="px-6 py-2 rounded-lg data-[state=active]:shadow-sm"
            data-testid="global-custom-claim-tab"
          >
            <Key size={16} className="mr-2" />
            {t(
              "ui.admin.users.detail.tabs.custom_claims",
              "전역 Custom Claims",
            )}
          </TabsTrigger>
          <TabsTrigger
            value="security"
            className="px-6 py-2 rounded-lg data-[state=active]:shadow-sm"
          >
            <Shield size={16} className="mr-2" />
            {t("ui.admin.users.detail.tabs.security", "보안 & 활동")}
          </TabsTrigger>
        </TabsList>

        <form onSubmit={handleSubmit(onSubmit)}>
          <TabsContent
            value="info"
            className="space-y-6 mt-0 animate-in fade-in slide-in-from-bottom-2"
          >
            <Card className="border-none shadow-sm bg-[var(--color-panel)] rounded-2xl overflow-hidden">
              <CardHeader className="pb-4">
                <CardTitle className="text-lg flex items-center gap-2">
                  <BadgeCheck size={18} className="text-primary" />
                  {t("ui.admin.users.detail.edit_title", "프로필 정보")}
                </CardTitle>
                <CardDescription>
                  {t(
                    "msg.admin.users.detail.edit_subtitle",
                    "{{email}} 계정의 정보를 수정합니다.",
                    { email: user.email },
                  )}
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-8 p-8">
                <div className="grid gap-8 md:grid-cols-2">
                  <div className="space-y-2">
                    <Label
                      htmlFor="email"
                      className="text-xs font-bold uppercase text-muted-foreground"
                    >
                      {t("ui.admin.users.detail.form.email", "이메일")}
                    </Label>
                    <Input
                      id="email"
                      type="email"
                      disabled={profileRole !== "super_admin"}
                      {...register("email", {
                        required: t(
                          "msg.admin.users.detail.email_required",
                          "이메일을 입력하세요.",
                        ),
                      })}
                      className={
                        profileRole === "super_admin"
                          ? "h-11 shadow-sm"
                          : "bg-muted/50 border-none font-medium h-11"
                      }
                    />
                  </div>
                  <div className="space-y-2">
                    <Label
                      htmlFor="name"
                      className="text-xs font-bold uppercase text-muted-foreground"
                    >
                      {t("ui.admin.users.detail.form.name", "이름")}
                    </Label>
                    <Input
                      id="name"
                      {...register("name", {
                        required: t(
                          "msg.admin.users.detail.form.name_required",
                          "이름은 필수입니다.",
                        ),
                      })}
                      className="h-11 shadow-sm"
                    />
                  </div>
                </div>

                <div className="grid gap-8 md:grid-cols-2 pt-6 border-t border-dashed">
                  <div className="space-y-2">
                    <Label
                      htmlFor="phone"
                      className="text-xs font-bold uppercase text-muted-foreground"
                    >
                      {t("ui.admin.users.detail.form.phone", "연락처")}
                    </Label>
                    <Input
                      id="phone"
                      {...register("phone")}
                      className="h-11 shadow-sm"
                    />
                  </div>
                  <div className="space-y-2">
                    <Label
                      htmlFor="metadata_employee_id"
                      className="text-xs font-bold uppercase text-muted-foreground"
                    >
                      사번
                    </Label>
                    <Input
                      id="metadata_employee_id"
                      maxLength={20}
                      {...register("metadata.employee_id", {
                        setValueAs: (value) =>
                          typeof value === "string" ? value.trim() : value,
                        maxLength: {
                          value: 20,
                          message:
                            "Worksmobile 사번은 20자 이하로 입력해야 합니다.",
                        },
                      })}
                      className="h-11 shadow-sm"
                    />
                    {errors.metadata?.employee_id && (
                      <p className="text-xs text-destructive">
                        {String(errors.metadata.employee_id.message)}
                      </p>
                    )}
                    <p className="text-[10px] text-muted-foreground mt-1">
                      Worksmobile employeeNumber로 전송됩니다. 1~20자만
                      허용됩니다.
                    </p>
                  </div>
                </div>

                <div className="grid gap-8 md:grid-cols-2 pt-6 border-t border-dashed">
                  <div className="space-y-2 col-span-full">
                    <Label
                      htmlFor="sub_email_input"
                      className="text-xs font-bold uppercase text-muted-foreground flex items-center gap-2"
                    >
                      <Mail size={14} />
                      {t("ui.admin.users.detail.form.sub_email", "보조 이메일")}
                    </Label>
                    <div className="flex flex-col gap-2">
                      <div className="flex flex-wrap gap-2 mb-1">
                        {currentSubEmails.map((email) => (
                          <Badge
                            key={email}
                            variant="secondary"
                            className="flex items-center gap-1 px-2.5 py-1 text-xs font-medium"
                          >
                            {email}
                            <button
                              type="button"
                              onClick={() => handleRemoveSubEmail(email)}
                              className="text-muted-foreground hover:text-foreground ml-1 rounded-full p-0.5 hover:bg-muted transition-colors"
                            >
                              <X size={12} />
                            </button>
                          </Badge>
                        ))}
                      </div>
                      <div className="relative">
                        <Input
                          id="sub_email_input"
                          value={newSubEmail}
                          onChange={(e) => setNewSubEmail(e.target.value)}
                          onKeyDown={handleAddSubEmail}
                          className="h-11 shadow-sm pr-20"
                          placeholder={t(
                            "ui.admin.users.detail.form.sub_email_placeholder",
                            "추가할 이메일 입력 후 Enter",
                          )}
                        />
                        <Button
                          type="button"
                          variant="ghost"
                          size="sm"
                          className="absolute right-1 top-1 h-9 text-xs font-bold"
                          data-testid="add-sub-email-btn"
                          onClick={() => {
                            const value = newSubEmail.trim().replace(/,/g, "");
                            if (
                              value?.includes("@") &&
                              !currentSubEmails.includes(value)
                            ) {
                              setValue(
                                "metadata.sub_email",
                                [...currentSubEmails, value],
                                { shouldDirty: true },
                              );
                              setNewSubEmail("");
                            }
                          }}
                        >
                          {t("ui.common.add", "추가")}
                        </Button>
                      </div>
                      <p className="text-[10px] text-muted-foreground mt-1">
                        {t(
                          "msg.admin.users.detail.sub_email_help",
                          "* 보조 이메일로도 로그인이 가능하며 계정 찾기 등에 활용될 수 있습니다.",
                        )}
                      </p>
                    </div>
                  </div>
                </div>

                <div className="grid gap-8 md:grid-cols-2 pt-6 border-t border-dashed">
                  <div className="space-y-2">
                    <Label
                      htmlFor="status"
                      className="text-xs font-bold uppercase text-muted-foreground"
                    >
                      {t("ui.admin.users.detail.form.status", "상태")}
                    </Label>
                    <Select
                      value={normalizeUserStatusValue(watchedStatus || "")}
                      onValueChange={(value) =>
                        setValue("status", normalizeUserStatusValue(value), {
                          shouldDirty: true,
                        })
                      }
                    >
                      <SelectTrigger id="status" className="h-11 shadow-sm">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        {userStatusValues.map((status) => (
                          <SelectItem key={status} value={status}>
                            {userStatusLabel(status)}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                </div>

                <Tabs
                  value={userCategory}
                  onValueChange={handleUserCategoryChange}
                  className="space-y-4 pt-6 border-t border-dashed"
                >
                  <TabsList className="flex h-auto w-full justify-start rounded-none border-b bg-transparent p-0 text-foreground">
                    <TabsTrigger
                      value="hanmac"
                      className="-mb-px rounded-b-none rounded-t-md border border-transparent border-b-border bg-muted/40 px-4 py-2 text-muted-foreground shadow-none data-[state=active]:border-border data-[state=active]:border-b-background data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-none"
                    >
                      한맥가족 구성원
                    </TabsTrigger>
                    <TabsTrigger
                      value="external"
                      className="-mb-px rounded-b-none rounded-t-md border border-transparent border-b-border bg-muted/40 px-4 py-2 text-muted-foreground shadow-none data-[state=active]:border-border data-[state=active]:border-b-background data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-none"
                    >
                      외부 기업 회원
                    </TabsTrigger>
                    <TabsTrigger
                      value="personal"
                      className="-mb-px rounded-b-none rounded-t-md border border-transparent border-b-border bg-muted/40 px-4 py-2 text-muted-foreground shadow-none data-[state=active]:border-border data-[state=active]:border-b-background data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-none"
                    >
                      개인 회원
                    </TabsTrigger>
                  </TabsList>
                </Tabs>

                {userCategory === "external" && (
                  <div className="grid gap-8 md:grid-cols-2">
                    <div className="space-y-2">
                      <Label
                        htmlFor="tenantSlug"
                        className="text-xs font-bold uppercase text-muted-foreground"
                      >
                        {t(
                          "ui.admin.users.detail.form.tenant_slug",
                          "대표 소속 (Tenant Slug)",
                        )}
                      </Label>
                      <div className="relative">
                        <select
                          id="tenantSlug"
                          className="flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:opacity-50"
                          {...register("tenantSlug")}
                          disabled={
                            profileRole !== "super_admin" &&
                            selectableRepresentativeTenants.length <= 1
                          }
                        >
                          {selectableRepresentativeTenants.map((t) => (
                            <option key={t.id} value={t.slug}>
                              {t.name} ({t.slug})
                            </option>
                          ))}
                        </select>
                        <BadgeCheck
                          size={14}
                          className="absolute right-3 top-[15px] text-primary pointer-events-none"
                        />
                      </div>
                      <p className="text-[10px] text-muted-foreground mt-1">
                        *{" "}
                        {t(
                          "msg.admin.users.detail.tenant_slug_help",
                          "사용자의 주된 정체성을 결정하는 대표 조직을 지정합니다.",
                        )}
                      </p>
                    </div>
                  </div>
                )}

                {userCategory === "hanmac" && (
                  <div className="space-y-4 rounded-md border p-4">
                    <div className="space-y-4">
                      <div className="space-y-3">
                        <div className="flex items-center justify-between gap-3">
                          <div>
                            <p className="text-sm font-medium">
                              {t(
                                "ui.admin.users.detail.form.additional_appointments",
                                "소속별 직급/직책/직무",
                              )}
                            </p>
                            <p className="text-xs text-muted-foreground">
                              {t(
                                "msg.admin.users.detail.form.additional_appointments_help",
                                "테넌트별 조직장 여부, 직급, 직책, 직무를 입력합니다.",
                              )}
                            </p>
                          </div>
                          <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            onClick={addAppointment}
                            data-testid="add-appointment-btn"
                          >
                            <Plus className="mr-2 h-4 w-4" />
                            {t("ui.common.add", "추가")}
                          </Button>
                        </div>

                        {additionalAppointments.map((appointment, index) => (
                          <div
                            key={appointment.draftId}
                            data-testid={`detail-appointment-row-${index}`}
                            className="grid gap-3 rounded-md border p-3 lg:grid-cols-[minmax(280px,1.2fr)_minmax(280px,1fr)_auto] lg:items-end"
                          >
                            <div
                              className="space-y-2"
                              data-testid={`detail-appointment-tenant-owner-line-${index}`}
                            >
                              <Label>
                                {t(
                                  "ui.admin.users.detail.form.appointment_tenant",
                                  "소속 테넌트",
                                )}
                              </Label>
                              <div
                                className="flex items-center justify-between gap-3"
                                data-testid={`detail-appointment-tenant-owner-controls-${index}`}
                              >
                                <div className="flex min-w-0 flex-1 items-center gap-3">
                                  <Button
                                    type="button"
                                    variant="outline"
                                    className="min-w-0 max-w-full"
                                    onClick={() =>
                                      setPickerTarget({
                                        kind: "appointment",
                                        index,
                                      })
                                    }
                                    disabled={isResolvingTenant}
                                    data-testid={`detail-appointment-tenant-picker-${index}`}
                                  >
                                    <Building2 className="mr-2 h-4 w-4 shrink-0" />
                                    <span className="truncate">
                                      {appointment.tenantName ||
                                        t(
                                          "ui.admin.users.detail.form.pick_tenant",
                                          "테넌트 선택",
                                        )}
                                    </span>
                                  </Button>
                                  {appointment.tenantSlug && (
                                    <span className="truncate text-xs text-muted-foreground">
                                      {appointment.tenantSlug}
                                    </span>
                                  )}
                                </div>
                                <div className="flex shrink-0 items-center gap-3 whitespace-nowrap">
                                  <label className="flex items-center gap-2 text-sm">
                                    <Switch
                                      checked={appointment.isPrimary === true}
                                      onCheckedChange={(checked) =>
                                        updateAppointment(index, {
                                          isPrimary: checked === true,
                                        })
                                      }
                                      disabled={appointment.isPrimary === true}
                                      aria-label={t(
                                        "ui.admin.users.detail.form.appointment_owner",
                                        "대표 조직",
                                      )}
                                    />
                                    {t(
                                      "ui.admin.users.detail.form.appointment_owner",
                                      "대표 조직",
                                    )}
                                  </label>
                                  <label className="flex items-center gap-2 text-sm">
                                    <Switch
                                      checked={appointment.isManager === true}
                                      onCheckedChange={(checked) =>
                                        updateAppointment(index, {
                                          isManager: checked === true,
                                        })
                                      }
                                      aria-label={t(
                                        "ui.admin.users.detail.form.appointment_manager",
                                        "조직장",
                                      )}
                                    />
                                    {t(
                                      "ui.admin.users.detail.form.appointment_manager",
                                      "조직장",
                                    )}
                                  </label>
                                </div>
                              </div>
                            </div>

                            <div
                              className="grid gap-3 sm:grid-cols-3"
                              data-testid={`detail-appointment-position-line-${index}`}
                            >
                              <div className="space-y-2">
                                <Label
                                  htmlFor={`detail-appointment-grade-${index}`}
                                >
                                  {t(
                                    "ui.admin.users.detail.form.grade",
                                    "직급",
                                  )}
                                </Label>
                                <select
                                  id={`detail-appointment-grade-${index}`}
                                  className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
                                  value={appointment.grade ?? ""}
                                  onChange={(event) =>
                                    updateAppointment(index, {
                                      grade: event.target.value || "",
                                    })
                                  }
                                >
                                  <option value="">없음</option>
                                  {getTenantGradeOptions(
                                    appointment,
                                    tenants,
                                  ).map((grade) => (
                                    <option key={grade} value={grade}>
                                      {grade}
                                    </option>
                                  ))}
                                </select>
                              </div>
                              <div className="space-y-2">
                                <Label
                                  htmlFor={`detail-appointment-job-title-${index}`}
                                >
                                  {t(
                                    "ui.admin.users.detail.form.job_title",
                                    "직무",
                                  )}
                                </Label>
                                <Input
                                  id={`detail-appointment-job-title-${index}`}
                                  value={appointment.jobTitle ?? ""}
                                  onChange={(event) =>
                                    updateAppointment(index, {
                                      jobTitle: event.target.value,
                                    })
                                  }
                                />
                              </div>

                              <div className="space-y-2">
                                <Label
                                  htmlFor={`detail-appointment-position-${index}`}
                                >
                                  {t(
                                    "ui.admin.users.detail.form.position",
                                    "직책",
                                  )}
                                </Label>
                                <Input
                                  id={`detail-appointment-position-${index}`}
                                  value={appointment.position ?? ""}
                                  onChange={(event) =>
                                    updateAppointment(index, {
                                      position: event.target.value,
                                    })
                                  }
                                />
                              </div>
                            </div>
                            <Button
                              type="button"
                              variant="ghost"
                              size="sm"
                              onClick={() => removeAppointment(index)}
                            >
                              <Trash2 className="h-4 w-4" />
                              <span className="sr-only">
                                {t("ui.common.delete", "삭제")}
                              </span>
                            </Button>
                          </div>
                        ))}
                      </div>
                    </div>
                  </div>
                )}

                {userCategory === "personal" && (
                  <div className="rounded-md border bg-muted/30 p-4 text-sm">
                    {personalTenant
                      ? `Personal (${personalTenant.slug})`
                      : "Personal 테넌트로 생성됩니다."}
                  </div>
                )}

                {userCategory === "external" && (
                  <div className="grid gap-6 md:grid-cols-3 pt-8 border-t">
                    <div className="space-y-2">
                      <Label
                        htmlFor="department"
                        className="text-xs font-bold uppercase text-muted-foreground"
                      >
                        {t(
                          "ui.admin.users.detail.form.department",
                          "부서/학과",
                        )}
                      </Label>
                      <Input
                        id="department"
                        {...register("department")}
                        className="h-11 shadow-sm"
                      />
                    </div>
                    <div className="space-y-2">
                      <Label
                        htmlFor="grade"
                        className="text-xs font-bold uppercase text-muted-foreground"
                      >
                        {t("ui.admin.users.detail.form.grade", "직급")}
                      </Label>
                      <Input
                        id="grade"
                        {...register("grade")}
                        className="h-11 shadow-sm"
                      />
                    </div>
                    <div className="space-y-2">
                      <Label
                        htmlFor="position"
                        className="text-xs font-bold uppercase text-muted-foreground"
                      >
                        {t("ui.admin.users.detail.form.position", "직책")}
                      </Label>
                      <Input
                        id="position"
                        {...register("position")}
                        className="h-11 shadow-sm"
                      />
                    </div>
                    <div className="space-y-2">
                      <Label
                        htmlFor="jobTitle"
                        className="text-xs font-bold uppercase text-muted-foreground"
                      >
                        {t("ui.admin.users.detail.form.job_title", "직무")}
                      </Label>
                      <Input
                        id="jobTitle"
                        {...register("jobTitle")}
                        className="h-11 shadow-sm"
                      />
                    </div>
                  </div>
                )}
              </CardContent>
            </Card>

            <div className="flex justify-end pt-4">
              <Button
                type="submit"
                disabled={mutation.isPending}
                className="px-12 h-12 rounded-xl shadow-lg transition-all hover:scale-105"
              >
                {mutation.isPending ? (
                  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
                ) : (
                  <Save className="mr-2 h-5 w-5" />
                )}
                <span className="text-base font-bold">
                  {t("ui.admin.users.detail.save", "저장하기")}
                </span>
              </Button>
            </div>
          </TabsContent>

          <TabsContent
            value="tenants"
            className="space-y-6 mt-0 animate-in fade-in slide-in-from-bottom-2"
          >
            <Card className="border-none shadow-sm bg-[var(--color-panel)] rounded-2xl">
              <CardHeader className="pb-4">
                <CardTitle className="text-lg flex items-center gap-2">
                  <Building2 size={18} className="text-primary" />
                  {t(
                    "ui.admin.users.detail.custom_fields.multi_title",
                    "테넌트별 상세 프로필",
                  )}
                </CardTitle>
                <CardDescription>
                  {t(
                    "msg.admin.users.detail.tenants_desc",
                    "각 테넌트별로 정의된 커스텀 스키마 정보를 관리합니다.",
                  )}
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-8 p-8">
                {userAffiliatedTenants.length === 0 ? (
                  <div className="py-16 text-center text-muted-foreground border-2 border-dashed rounded-2xl bg-muted/5">
                    <Building2 size={48} className="mx-auto mb-4 opacity-20" />
                    <p className="font-medium">
                      {t(
                        "msg.admin.users.detail.no_tenants",
                        "소속된 테넌트 정보가 없습니다.",
                      )}
                    </p>
                  </div>
                ) : (
                  <div className="grid gap-8">
                    {userAffiliatedTenants.map((t) => {
                      const tDetail = tenants.find(
                        (tenant) => tenant.id === t.id,
                      );
                      const schema = (tDetail?.config?.userSchema ||
                        []) as UserSchemaField[];
                      return (
                        <TenantMetadataFields
                          key={t.id}
                          tenant={t}
                          schema={schema}
                          register={register}
                          errors={errors}
                        />
                      );
                    })}
                  </div>
                )}
              </CardContent>
            </Card>

            <div className="flex justify-end pt-4">
              <Button
                type="submit"
                disabled={mutation.isPending}
                className="px-12 h-12 rounded-xl shadow-lg transition-all hover:scale-105"
              >
                {mutation.isPending ? (
                  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
                ) : (
                  <Save className="mr-2 h-5 w-5" />
                )}
                <span className="text-base font-bold">
                  {t(
                    "ui.admin.users.detail.save_tenants",
                    "모든 테넌트 프로필 저장",
                  )}
                </span>
              </Button>
            </div>
          </TabsContent>

          <TabsContent
            value="customClaims"
            className="space-y-6 mt-0 animate-in fade-in slide-in-from-bottom-2"
          >
            <Card className="border-none shadow-sm bg-[var(--color-panel)] rounded-2xl">
              <CardHeader className="pb-4">
                <div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
                  <div>
                    <CardTitle className="text-lg flex items-center gap-2">
                      <Key size={18} className="text-primary" />
                      {t(
                        "ui.admin.users.detail.custom_claims.title",
                        "사용자별 Custom Claim 값",
                      )}
                    </CardTitle>
                    <CardDescription>
                      {t(
                        "msg.admin.users.detail.custom_claims.description",
                        "전역으로 정의된 custom claim의 이 사용자 값을 관리합니다. 읽기/쓰기 표시는 사용자가 본인 claim 값을 조회하거나 직접 수정할 수 있는지에 대한 권한이며, claim 정의 추가와 타입 변경은 전역 설정 화면에서만 가능합니다.",
                      )}
                    </CardDescription>
                  </div>
                  <Button
                    type="button"
                    variant="outline"
                    className="gap-2"
                    onClick={() => navigate("/users/custom-claims")}
                  >
                    <Key className="h-4 w-4" />
                    {t(
                      "ui.admin.users.global_custom_claims.manage_definitions",
                      "전역 정의 관리",
                    )}
                  </Button>
                </div>
              </CardHeader>
              <CardContent className="space-y-4 p-8">
                {globalCustomClaimRows.length === 0 ? (
                  <div className="rounded-2xl border-2 border-dashed bg-muted/5 py-12 text-center text-sm text-muted-foreground">
                    {t(
                      "msg.admin.users.detail.custom_claims.empty",
                      "전역으로 정의된 custom claim이 없습니다.",
                    )}
                  </div>
                ) : (
                  <div className="space-y-3">
                    {globalCustomClaimRows.map((claim) => (
                      <div
                        key={claim.id}
                        className="grid gap-3 lg:grid-cols-[minmax(180px,0.8fr)_130px_150px_160px_minmax(220px,1fr)]"
                      >
                        <div className="flex h-10 items-center rounded-md border bg-muted/30 px-3 font-mono text-xs">
                          {claim.key}
                        </div>
                        <Badge
                          variant="muted"
                          className="h-10 justify-center rounded-md px-3 font-mono text-xs"
                        >
                          {claim.valueType}
                        </Badge>
                        <Badge
                          variant="muted"
                          className="h-10 justify-center rounded-md px-3 text-xs"
                        >
                          {claim.readPermission === "user_and_admin"
                            ? t(
                                "ui.common.custom_claim_permission.user_and_admin",
                                "사용자 및 관리자 가능",
                              )
                            : t(
                                "ui.common.custom_claim_permission.admin_only",
                                "관리자만 가능",
                              )}
                        </Badge>
                        <Badge
                          variant="muted"
                          className="h-10 justify-center rounded-md px-3 text-xs"
                        >
                          {claim.writePermission === "user_and_admin"
                            ? t(
                                "ui.common.custom_claim_permission.user_and_admin",
                                "사용자 및 관리자 가능",
                              )
                            : t(
                                "ui.common.custom_claim_permission.admin_only",
                                "관리자만 가능",
                              )}
                        </Badge>
                        <Input
                          type={globalCustomClaimInputType(claim.valueType)}
                          value={claim.value}
                          onChange={(event) =>
                            updateGlobalCustomClaimRow(claim.id, {
                              value: event.target.value,
                            })
                          }
                          className="font-mono text-xs"
                          data-testid={`global-custom-claim-value-${claim.key || claim.id}`}
                          placeholder="claim value"
                        />
                      </div>
                    ))}
                  </div>
                )}
              </CardContent>
            </Card>

            <div className="flex justify-end pt-4">
              <Button
                type="button"
                disabled={mutation.isPending}
                onClick={saveGlobalCustomClaims}
                className="px-12 h-12 rounded-xl shadow-lg transition-all hover:scale-105"
              >
                {mutation.isPending ? (
                  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
                ) : (
                  <Save className="mr-2 h-5 w-5" />
                )}
                <span className="text-base font-bold">
                  {t(
                    "ui.admin.users.detail.custom_claims.save",
                    "사용자 Claim 값 저장",
                  )}
                </span>
              </Button>
            </div>
          </TabsContent>
        </form>

        <TabsContent
          value="security"
          className="space-y-6 mt-0 animate-in fade-in slide-in-from-bottom-2"
        >
          <div className="grid gap-6 lg:grid-cols-2">
            <Card className="border-none shadow-sm bg-[var(--color-panel)] rounded-2xl h-fit">
              <CardHeader>
                <CardTitle className="text-lg flex items-center gap-2">
                  <Key size={18} className="text-primary" />
                  {t("ui.admin.users.detail.password_title", "비밀번호 관리")}
                </CardTitle>
                <CardDescription>
                  {t(
                    "msg.admin.users.detail.security_desc",
                    "비밀번호 초기화 및 보안 설정을 관리합니다.",
                  )}
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-6">
                <div className="flex items-center justify-between rounded-2xl border bg-muted/20 px-6 py-6 transition-all hover:bg-muted/30">
                  <div className="space-y-1">
                    <p className="font-bold text-sm">
                      {t(
                        "ui.admin.users.detail.reset_password_label",
                        "비밀번호 초기화",
                      )}
                    </p>
                    <p className="text-xs text-muted-foreground">
                      {t(
                        "msg.admin.users.detail.reset_password_help",
                        "안전한 새 비밀번호로 교체합니다.",
                      )}
                    </p>
                  </div>
                  <Button
                    variant="outline"
                    onClick={handleOpenPasswordReset}
                    disabled={isSelf}
                    className="h-10 rounded-xl px-5 border-primary/20 hover:border-primary/50 hover:bg-primary/5"
                  >
                    <RefreshCw className="mr-2 h-4 w-4" />
                    {t("ui.admin.users.detail.reset_password", "초기화 도구")}
                  </Button>
                </div>

                {isSelf && (
                  <div className="rounded-xl bg-blue-500/5 border border-blue-500/10 px-5 py-4 text-sm text-blue-600 flex items-center gap-3">
                    <Shield size={18} className="shrink-0" />
                    <p className="leading-relaxed">
                      {t(
                        "msg.admin.users.detail.self_password_reset_blocked",
                        "보안을 위해 본인 계정은 사용자 포털에서만 변경 가능합니다.",
                      )}
                    </p>
                  </div>
                )}

                {isPasswordResetOpen && !generatedPassword && !isSelf && (
                  <div className="mt-4 p-6 border rounded-2xl bg-card shadow-sm animate-in zoom-in-95 duration-200">
                    <Tabs
                      value={passwordResetMode}
                      onValueChange={(v) =>
                        setPasswordResetMode(v as PasswordResetMode)
                      }
                    >
                      <TabsList className="grid w-full grid-cols-2 mb-6 p-1 bg-gray-100 dark:bg-gray-800 rounded-lg">
                        <TabsTrigger
                          value="auto"
                          className="rounded-md data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:shadow-sm font-bold py-2 text-gray-500"
                        >
                          {t("ui.admin.users.detail.reset_auto", "자동 생성")}
                        </TabsTrigger>
                        <TabsTrigger
                          value="manual"
                          className="rounded-md data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:shadow-sm font-bold py-2 text-gray-500"
                        >
                          {t("ui.admin.users.detail.reset_manual", "직접 입력")}
                        </TabsTrigger>
                      </TabsList>

                      <TabsContent value="auto" className="space-y-4">
                        <div className="bg-muted/50 p-4 rounded-xl text-xs text-muted-foreground leading-relaxed">
                          {t(
                            "msg.admin.users.detail.reset_auto_desc",
                            "해킹이 어려운 복잡한 임시 비밀번호를 시스템이 즉시 생성합니다.",
                          )}
                        </div>
                        <Button
                          type="button"
                          onClick={() =>
                            setManualPassword(generateSecurePassword())
                          }
                          variant="secondary"
                          className="w-full h-11 rounded-xl font-bold"
                        >
                          {t(
                            "ui.admin.users.detail.generate_button",
                            "랜덤 비밀번호 생성",
                          )}
                        </Button>
                      </TabsContent>

                      <TabsContent value="manual" className="space-y-5 pt-2">
                        <div className="space-y-2">
                          <Label className="text-xs font-bold uppercase">
                            {t(
                              "ui.admin.users.detail.manual_password",
                              "새 비밀번호",
                            )}
                          </Label>
                          <div className="relative">
                            <Input
                              type={
                                isManualPasswordVisible ? "text" : "password"
                              }
                              value={manualPassword}
                              onChange={(e) =>
                                setManualPassword(e.target.value)
                              }
                              className="h-11 rounded-xl shadow-sm pr-12"
                            />
                            <Button
                              type="button"
                              variant="ghost"
                              size="icon"
                              className="absolute right-0 top-0 h-full px-3 hover:bg-transparent text-muted-foreground"
                              onClick={() =>
                                setIsManualPasswordVisible(
                                  !isManualPasswordVisible,
                                )
                              }
                            >
                              {isManualPasswordVisible ? (
                                <EyeOff size={18} />
                              ) : (
                                <Eye size={18} />
                              )}
                            </Button>
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label className="text-xs font-bold uppercase">
                            {t(
                              "ui.admin.users.detail.manual_confirm",
                              "비밀번호 확인",
                            )}
                          </Label>
                          <Input
                            type="password"
                            value={manualPasswordConfirm}
                            onChange={(e) =>
                              setManualPasswordConfirm(e.target.value)
                            }
                            className="h-11 rounded-xl shadow-sm"
                          />
                        </div>
                      </TabsContent>

                      {passwordResetError && (
                        <div className="flex items-center gap-2 text-xs text-destructive mt-4 p-3 bg-destructive/5 rounded-lg border border-destructive/10">
                          <Shield size={14} />
                          <span className="font-medium">
                            {passwordResetError}
                          </span>
                        </div>
                      )}

                      <div className="flex justify-end gap-3 mt-8">
                        <Button
                          variant="ghost"
                          type="button"
                          onClick={handleClosePasswordReset}
                          className="h-11 rounded-xl px-6 font-bold"
                        >
                          {t("ui.common.cancel", "취소")}
                        </Button>
                        <Button
                          type="button"
                          onClick={handleExecutePasswordReset}
                          disabled={resetMutation.isPending}
                          className="h-11 rounded-xl px-8 font-bold shadow-md"
                        >
                          {resetMutation.isPending && (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          )}
                          {t(
                            "ui.admin.users.detail.reset_execute",
                            "재설정 완료",
                          )}
                        </Button>
                      </div>
                    </Tabs>
                  </div>
                )}

                {generatedPassword && (
                  <div className="mt-4 p-8 bg-green-500/10 border border-green-500/20 rounded-2xl space-y-6 animate-in zoom-in-95">
                    <div className="flex items-center gap-3 text-green-700 font-extrabold text-lg">
                      <BadgeCheck size={28} className="text-green-600" />
                      {t(
                        "ui.admin.users.detail.password_done",
                        "성공적으로 초기화됨",
                      )}
                    </div>
                    <div className="p-5 bg-white border border-green-200 rounded-2xl flex items-center justify-between shadow-sm">
                      <code className="text-2xl font-mono tracking-widest text-primary selection:bg-primary selection:text-white">
                        {generatedPassword}
                      </code>
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={() => {
                          navigator.clipboard.writeText(generatedPassword);
                          toast.success(
                            t("msg.common.copied", "복사되었습니다."),
                          );
                        }}
                        className="h-10 px-4 rounded-xl hover:bg-green-50 font-bold"
                      >
                        <Copy size={16} className="mr-2" />
                        {t("ui.common.copy", "복사")}
                      </Button>
                    </div>
                    <Button
                      className="w-full h-12 rounded-xl font-bold bg-green-600 hover:bg-green-700 shadow-md"
                      type="button"
                      onClick={handleClosePasswordReset}
                    >
                      {t("ui.common.close", "안전하게 도구 닫기")}
                    </Button>
                  </div>
                )}
              </CardContent>
            </Card>

            <Card className="border-none shadow-sm bg-[var(--color-panel)] rounded-2xl">
              <CardHeader>
                <CardTitle className="text-lg flex items-center gap-2">
                  <History size={18} className="text-primary" />
                  {t("ui.admin.users.detail.history_title", "서비스 이용 내역")}
                </CardTitle>
                <CardDescription>
                  {t(
                    "msg.admin.users.detail.history_desc",
                    "최근 로그인한 연동 서비스(RP) 목록입니다.",
                  )}
                </CardDescription>
              </CardHeader>
              <CardContent className="p-8 pt-2">
                {rpHistoryQuery.isLoading ? (
                  <div className="py-12 text-center text-muted-foreground animate-pulse">
                    {t("msg.common.loading", "불러오는 중...")}
                  </div>
                ) : !rpHistoryQuery.data || rpHistoryQuery.data.length === 0 ? (
                  <div className="py-16 text-center text-muted-foreground border-2 border-dashed rounded-2xl bg-muted/5">
                    <History size={40} className="mx-auto mb-4 opacity-10" />
                    <p className="text-sm font-medium">
                      {t(
                        "msg.admin.users.detail.no_history",
                        "아직 이용한 서비스가 없습니다.",
                      )}
                    </p>
                  </div>
                ) : (
                  <div className="grid gap-4">
                    {rpHistoryQuery.data.map((item) => (
                      <div
                        key={item.client_id || item.client_id}
                        className="flex items-center justify-between p-5 rounded-2xl border bg-card hover:border-primary/40 hover:shadow-md transition-all group"
                      >
                        <div className="flex flex-col gap-1.5">
                          <span className="font-extrabold text-base group-hover:text-primary transition-colors">
                            {item.client_name || item.client_id}
                          </span>
                          <span className="text-[11px] text-muted-foreground font-mono bg-muted px-2 py-0.5 rounded w-fit">
                            {item.client_id}
                          </span>
                        </div>
                        <div className="text-right">
                          <Badge
                            variant="outline"
                            className="text-[10px] font-bold px-2 py-1 rounded-md border-primary/20"
                          >
                            {item.lastLoginAt}
                          </Badge>
                        </div>
                      </div>
                    ))}
                  </div>
                )}
              </CardContent>
            </Card>
          </div>
        </TabsContent>
      </Tabs>
      <Dialog
        open={pickerTarget !== null}
        onOpenChange={(open) => {
          if (!open) setPickerTarget(null);
        }}
      >
        <DialogContent className="max-w-[460px] p-4">
          <DialogHeader>
            <DialogTitle>
              {t("ui.admin.users.detail.form.pick_tenant", "테넌트 선택")}
            </DialogTitle>
            <DialogDescription>
              {t(
                "msg.admin.users.detail.form.picker_description",
                "org-chart에서 테넌트를 선택하면 사용자 소속에 반영됩니다.",
              )}
            </DialogDescription>
          </DialogHeader>
          <iframe
            title={t("ui.admin.users.detail.form.pick_tenant", "테넌트 선택")}
            src={pickerUrl}
            className="h-[600px] w-full rounded-md border"
          />
        </DialogContent>
      </Dialog>
    </div>
  );
}

export default UserDetailPage;
