"use client";

import React, { useState, useCallback, useEffect, useMemo, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Plus,
  Edit,
  Trash2,
  Users,
  X,
  Search,
  AlertCircle,
  Shield,
  ChevronRight,
  ChevronDown,
  ChevronLeft,
  ChevronsLeft,
  ChevronsRight,
} from "lucide-react";
import { roleAPI, RoleGroup } from "@/lib/api/role";
import { useAuth } from "@/hooks/useAuth";
import { RoleFormModal } from "@/components/admin/RoleFormModal";
import { RoleDeleteModal } from "@/components/admin/RoleDeleteModal";
import { ScrollToTop } from "@/components/common/ScrollToTop";
import { useMenu } from "@/contexts/MenuContext";
import { cn } from "@/lib/utils";

type UserItem = {
  userId: string;
  userName?: string;
  deptName?: string;
};

type MenuNode = {
  objid: string;
  menuName: string;
  parentObjid?: string | null;
  sortOrder?: number;
  children: MenuNode[];
  level: number;
};

type PermMap = {
  createYn: "Y" | "N";
  readYn: "Y" | "N";
  updateYn: "Y" | "N";
  deleteYn: "Y" | "N";
};

const EMPTY_PERM: PermMap = { createYn: "N", readYn: "N", updateYn: "N", deleteYn: "N" };

/**
 * 권한 관리 (통합 페이지)
 *
 * 레이아웃:
 *  [권한목록] | [권한있는 직원] | [추가/삭제] | [권한없는 직원]
 *  ──────────────────────────────────────────────────────────
 *  [메뉴 트리구조] | 등록/수정 | 삭제 | 조회
 *
 * 단일 회사 운영 가정 — 회사 필터/CrossTenant 처리는 제거.
 * 체크박스 토글 시 즉시 서버 반영(auto-save).
 */
export default function UserAuthPage() {
  const { user: currentUser } = useAuth();
  const { refreshMenus } = useMenu();

  const isAdmin =
    currentUser?.userType === "SUPER_ADMIN" ||
    currentUser?.userType === "COMPANY_ADMIN" ||
    currentUser?.userType === "ADMIN";

  // 권한 그룹 목록
  const [roleGroups, setRoleGroups] = useState<RoleGroup[]>([]);
  const [selectedRole, setSelectedRole] = useState<RoleGroup | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isLoadingWorkspace, setIsLoadingWorkspace] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [searchText, setSearchText] = useState("");

  // 모달
  const [formModal, setFormModal] = useState<{
    isOpen: boolean;
    editingRole: RoleGroup | null;
  }>({ isOpen: false, editingRole: null });
  const [deleteModal, setDeleteModal] = useState<{
    isOpen: boolean;
    role: RoleGroup | null;
  }>({ isOpen: false, role: null });

  // 워크스페이스 데이터
  const [members, setMembers] = useState<UserItem[]>([]);
  const [nonMembers, setNonMembers] = useState<UserItem[]>([]);
  const [menus, setMenus] = useState<any[]>([]);
  const [permissions, setPermissions] = useState<Record<string, PermMap>>({});

  // 멤버 이동 선택
  const [checkedMembers, setCheckedMembers] = useState<Set<string>>(new Set());
  const [checkedNonMembers, setCheckedNonMembers] = useState<Set<string>>(new Set());

  // 멤버 영역 검색
  const [memberSearch, setMemberSearch] = useState("");
  const [nonMemberSearch, setNonMemberSearch] = useState("");

  // 메뉴 트리 펼침
  const [expandedMenus, setExpandedMenus] = useState<Set<string>>(new Set());

  // 뷰포트 xl 여부 (1280px 이상) — inline style 기반 강제 4분할 레이아웃에 사용
  const [isXl, setIsXl] = useState(false);
  useEffect(() => {
    const mql = window.matchMedia("(min-width: 1280px)");
    const update = () => setIsXl(mql.matches);
    update();
    mql.addEventListener("change", update);
    return () => mql.removeEventListener("change", update);
  }, []);

  // ─────────── 권한 그룹 목록 ───────────
  const loadRoleGroups = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const companyFilter =
        currentUser?.companyCode && currentUser.companyCode !== "*"
          ? currentUser.companyCode
          : undefined;
      const response = await roleAPI.getList({ companyCode: companyFilter });
      if (response.success && response.data) {
        setRoleGroups(response.data);
      } else {
        setError(response.message || "권한 그룹 목록을 불러오는데 실패했습니다.");
      }
    } catch (err) {
      console.error(err);
      setError("권한 그룹 목록을 불러오는 중 오류가 발생했습니다.");
    } finally {
      setIsLoading(false);
    }
  }, [currentUser?.companyCode]);

  useEffect(() => {
    if (!isAdmin) {
      setIsLoading(false);
      return;
    }
    loadRoleGroups();
  }, [isAdmin, loadRoleGroups]);

  // 선택 그룹이 사라지면 초기화
  useEffect(() => {
    if (!selectedRole) return;
    if (!roleGroups.find((r) => r.objid === selectedRole.objid)) {
      setSelectedRole(null);
    }
  }, [roleGroups, selectedRole]);

  // ─────────── 워크스페이스 로드 ───────────
  const loadWorkspace = useCallback(
    async (role: RoleGroup) => {
      setIsLoadingWorkspace(true);
      setCheckedMembers(new Set());
      setCheckedNonMembers(new Set());
      try {
        const res = await roleAPI.getWorkspace(role.objid, role.companyCode);
        if (res.success && res.data) {
          setMembers(res.data.members || []);
          setNonMembers(res.data.nonMembers || []);
          setMenus(res.data.menus || []);
          const permMap: Record<string, PermMap> = {};
          (res.data.permissions || []).forEach((p: any) => {
            const key = String(p.menuObjid ?? p.menu_objid);
            permMap[key] = {
              createYn: (p.createYn ?? p.create_yn) === "Y" ? "Y" : "N",
              readYn: (p.readYn ?? p.read_yn) === "Y" ? "Y" : "N",
              updateYn: (p.updateYn ?? p.update_yn) === "Y" ? "Y" : "N",
              deleteYn: (p.deleteYn ?? p.delete_yn) === "Y" ? "Y" : "N",
            };
          });
          setPermissions(permMap);
        }
      } catch (err) {
        console.error("워크스페이스 로드 오류:", err);
      } finally {
        setIsLoadingWorkspace(false);
      }
    },
    [],
  );

  useEffect(() => {
    if (selectedRole) {
      loadWorkspace(selectedRole);
    } else {
      setMembers([]);
      setNonMembers([]);
      setMenus([]);
      setPermissions({});
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedRole?.objid]);

  // ─────────── 권한 그룹 목록 검색 ───────────
  const filteredRoleGroups = useMemo(() => {
    if (!searchText.trim()) return roleGroups;
    const q = searchText.toLowerCase();
    return roleGroups.filter(
      (r) =>
        (r.authName || "").toLowerCase().includes(q) ||
        (r.authCode || "").toLowerCase().includes(q),
    );
  }, [roleGroups, searchText]);

  // ─────────── 멤버 이동 ───────────
  const handleAddMembers = useCallback(async () => {
    if (!selectedRole || checkedNonMembers.size === 0) return;
    const ids = Array.from(checkedNonMembers);

    const moving = nonMembers.filter((u) => checkedNonMembers.has(u.userId));
    setMembers((prev) => [...prev, ...moving]);
    setNonMembers((prev) => prev.filter((u) => !checkedNonMembers.has(u.userId)));
    setCheckedNonMembers(new Set());

    try {
      for (const userId of ids) {
        const res = await roleAPI.addSingleMember(selectedRole.objid, userId);
        if (!res.success) throw new Error(res.message);
      }
      await refreshMenus();
      loadRoleGroups();
    } catch (err) {
      console.error("멤버 추가 오류:", err);
      alert("멤버 추가에 실패했습니다. 화면을 새로고침합니다.");
      loadWorkspace(selectedRole);
    }
  }, [selectedRole, checkedNonMembers, nonMembers, refreshMenus, loadRoleGroups, loadWorkspace]);

  const handleRemoveMembers = useCallback(async () => {
    if (!selectedRole || checkedMembers.size === 0) return;
    const ids = Array.from(checkedMembers);

    const moving = members.filter((u) => checkedMembers.has(u.userId));
    setNonMembers((prev) => [...prev, ...moving]);
    setMembers((prev) => prev.filter((u) => !checkedMembers.has(u.userId)));
    setCheckedMembers(new Set());

    try {
      for (const userId of ids) {
        const res = await roleAPI.removeSingleMember(selectedRole.objid, userId);
        if (!res.success) throw new Error(res.message);
      }
      await refreshMenus();
      loadRoleGroups();
    } catch (err) {
      console.error("멤버 제거 오류:", err);
      alert("멤버 제거에 실패했습니다. 화면을 새로고침합니다.");
      loadWorkspace(selectedRole);
    }
  }, [selectedRole, checkedMembers, members, refreshMenus, loadRoleGroups, loadWorkspace]);

  // 멤버 영역 필터링
  const filteredMembers = useMemo(() => {
    if (!memberSearch.trim()) return members;
    const q = memberSearch.toLowerCase();
    return members.filter(
      (u) =>
        (u.userName || "").toLowerCase().includes(q) ||
        u.userId.toLowerCase().includes(q) ||
        (u.deptName || "").toLowerCase().includes(q),
    );
  }, [members, memberSearch]);

  const filteredNonMembers = useMemo(() => {
    if (!nonMemberSearch.trim()) return nonMembers;
    const q = nonMemberSearch.toLowerCase();
    return nonMembers.filter(
      (u) =>
        (u.userName || "").toLowerCase().includes(q) ||
        u.userId.toLowerCase().includes(q) ||
        (u.deptName || "").toLowerCase().includes(q),
    );
  }, [nonMembers, nonMemberSearch]);

  const toggleMemberCheck = useCallback((id: string) => {
    setCheckedMembers((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }, []);
  const toggleNonMemberCheck = useCallback((id: string) => {
    setCheckedNonMembers((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }, []);

  const handleMembersSelectAll = useCallback(
    (checked: boolean) => {
      setCheckedMembers(checked ? new Set(filteredMembers.map((u) => u.userId)) : new Set());
    },
    [filteredMembers],
  );
  const handleNonMembersSelectAll = useCallback(
    (checked: boolean) => {
      setCheckedNonMembers(
        checked ? new Set(filteredNonMembers.map((u) => u.userId)) : new Set(),
      );
    },
    [filteredNonMembers],
  );

  // ─────────── 메뉴 트리 빌드 ───────────
  const menuTree = useMemo<MenuNode[]>(() => {
    const map = new Map<string, MenuNode>();
    menus.forEach((m: any) => {
      const id = String(m.objid);
      const name = m.menuName ?? m.menu_name ?? m.menuNameKor ?? m.menu_name_kor ?? "";
      const parent = m.parentObjid ?? m.parent_objid ?? m.parentObjId ?? m.parent_obj_id;
      const sort = Number(m.sortOrder ?? m.sort_order ?? m.seq ?? 0);
      map.set(id, {
        objid: id,
        menuName: name,
        parentObjid: parent ? String(parent) : null,
        sortOrder: isNaN(sort) ? 0 : sort,
        children: [],
        level: 0,
      });
    });
    const roots: MenuNode[] = [];
    map.forEach((node) => {
      const parentId = node.parentObjid && node.parentObjid !== "0" ? node.parentObjid : null;
      if (parentId && map.has(parentId)) {
        const parent = map.get(parentId)!;
        node.level = parent.level + 1;
        parent.children.push(node);
      } else {
        roots.push(node);
      }
    });
    const sortTree = (nodes: MenuNode[]) => {
      nodes.sort((a, b) => {
        if (a.sortOrder !== b.sortOrder) return (a.sortOrder ?? 0) - (b.sortOrder ?? 0);
        return a.menuName.localeCompare(b.menuName);
      });
      nodes.forEach((n) => sortTree(n.children));
    };
    sortTree(roots);
    return roots;
  }, [menus]);

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

  // 메뉴-필드별 진행 중 토글 가드 (race condition 방지: 빠른 연속 클릭 시 백엔드 SELECT→UPDATE 충돌 방지)
  const inFlightToggleRef = useRef<Set<string>>(new Set());

  // ─────────── 메뉴 권한 즉시 반영 ───────────
  const applyMenuPermission = useCallback(
    async (menuId: string, changes: Partial<PermMap>) => {
      if (!selectedRole) return;

      // 같은 메뉴의 같은 필드 조합이 진행 중이면 무시 (직전 클릭 응답 대기 중)
      const guardKey = `${menuId}:${Object.keys(changes).sort().join(",")}`;
      if (inFlightToggleRef.current.has(guardKey)) return;
      inFlightToggleRef.current.add(guardKey);

      const prevPerm = permissions[menuId] || EMPTY_PERM;
      const nextPerm: PermMap = { ...prevPerm, ...changes };

      setPermissions((prev) => ({ ...prev, [menuId]: nextPerm }));

      try {
        const res = await roleAPI.toggleMenuPermission(selectedRole.objid, menuId, changes);
        if (!res.success) throw new Error(res.message);

        if (res.data) {
          setPermissions((prev) => ({
            ...prev,
            [menuId]: {
              createYn: res.data!.create_yn === "Y" ? "Y" : "N",
              readYn: res.data!.read_yn === "Y" ? "Y" : "N",
              updateYn: res.data!.update_yn === "Y" ? "Y" : "N",
              deleteYn: res.data!.delete_yn === "Y" ? "Y" : "N",
            },
          }));
        }
        await refreshMenus();
      } catch (err) {
        console.error("메뉴 권한 저장 오류:", err);
        setPermissions((prev) => ({ ...prev, [menuId]: prevPerm }));
        alert("권한 변경에 실패했습니다.");
      } finally {
        inFlightToggleRef.current.delete(guardKey);
      }
    },
    [selectedRole, permissions, refreshMenus],
  );

  const handleEditCol = useCallback(
    (menuId: string, checked: boolean) => {
      const v: "Y" | "N" = checked ? "Y" : "N";
      applyMenuPermission(menuId, { createYn: v, updateYn: v });
    },
    [applyMenuPermission],
  );
  const handleDeleteCol = useCallback(
    (menuId: string, checked: boolean) => {
      applyMenuPermission(menuId, { deleteYn: checked ? "Y" : "N" });
    },
    [applyMenuPermission],
  );
  const handleReadCol = useCallback(
    (menuId: string, checked: boolean) => {
      applyMenuPermission(menuId, { readYn: checked ? "Y" : "N" });
    },
    [applyMenuPermission],
  );

  const flatMenuIds = useMemo(() => {
    const ids: string[] = [];
    const walk = (nodes: MenuNode[]) => {
      nodes.forEach((n) => {
        ids.push(n.objid);
        walk(n.children);
      });
    };
    walk(menuTree);
    return ids;
  }, [menuTree]);

  const handleBulkColumn = useCallback(
    async (column: "edit" | "delete" | "read", checked: boolean) => {
      if (!selectedRole) return;
      const v: "Y" | "N" = checked ? "Y" : "N";
      const change: Partial<PermMap> =
        column === "edit"
          ? { createYn: v, updateYn: v }
          : column === "delete"
            ? { deleteYn: v }
            : { readYn: v };

      setPermissions((prev) => {
        const next = { ...prev };
        flatMenuIds.forEach((id) => {
          next[id] = { ...(next[id] || EMPTY_PERM), ...change };
        });
        return next;
      });

      try {
        for (const id of flatMenuIds) {
          const res = await roleAPI.toggleMenuPermission(selectedRole.objid, id, change);
          if (!res.success) throw new Error(res.message);
        }
        await refreshMenus();
      } catch (err) {
        console.error("일괄 변경 오류:", err);
        alert("일괄 변경 실패 — 화면을 새로고침합니다.");
        loadWorkspace(selectedRole);
      }
    },
    [selectedRole, flatMenuIds, refreshMenus, loadWorkspace],
  );

  const isColumnAllChecked = useCallback(
    (column: "edit" | "delete" | "read"): boolean => {
      if (flatMenuIds.length === 0) return false;
      return flatMenuIds.every((id) => {
        const p = permissions[id] || EMPTY_PERM;
        if (column === "edit") return p.createYn === "Y" && p.updateYn === "Y";
        if (column === "delete") return p.deleteYn === "Y";
        return p.readYn === "Y";
      });
    },
    [flatMenuIds, permissions],
  );

  const renderMenuRow = (node: MenuNode): React.ReactNode => {
    const perm = permissions[node.objid] || EMPTY_PERM;
    const hasChildren = node.children.length > 0;
    const isExpanded = expandedMenus.has(node.objid);
    const editChecked = perm.createYn === "Y" && perm.updateYn === "Y";

    return (
      <React.Fragment key={node.objid}>
        <tr className="hover:bg-muted/30 border-t transition-colors">
          <td className="py-2 pr-2" style={{ paddingLeft: `${node.level * 20 + 12}px` }}>
            <div className="flex items-center gap-1.5">
              {hasChildren ? (
                <button
                  onClick={() => toggleExpand(node.objid)}
                  className="hover:bg-muted rounded p-0.5"
                >
                  {isExpanded ? (
                    <ChevronDown className="text-muted-foreground h-3.5 w-3.5" />
                  ) : (
                    <ChevronRight className="text-muted-foreground h-3.5 w-3.5" />
                  )}
                </button>
              ) : (
                <span className="inline-block w-4" />
              )}
              <span className={cn("text-sm", hasChildren && "font-semibold")}>{node.menuName}</span>
            </div>
          </td>
          <td className="py-2 text-center">
            <Checkbox
              checked={editChecked}
              onCheckedChange={(c) => handleEditCol(node.objid, c === true)}
            />
          </td>
          <td className="py-2 text-center">
            <Checkbox
              checked={perm.deleteYn === "Y"}
              onCheckedChange={(c) => handleDeleteCol(node.objid, c === true)}
            />
          </td>
          <td className="py-2 text-center">
            <Checkbox
              checked={perm.readYn === "Y"}
              onCheckedChange={(c) => handleReadCol(node.objid, c === true)}
            />
          </td>
        </tr>
        {hasChildren && isExpanded && node.children.map((c) => renderMenuRow(c))}
      </React.Fragment>
    );
  };

  const handleCreateRole = () => setFormModal({ isOpen: true, editingRole: null });
  const handleEditRole = (r: RoleGroup) => setFormModal({ isOpen: true, editingRole: r });
  const handleDeleteRole = (r: RoleGroup) => setDeleteModal({ isOpen: true, role: r });
  const handleFormClose = () => setFormModal({ isOpen: false, editingRole: null });
  const handleDeleteClose = () => setDeleteModal({ isOpen: false, role: null });
  const handleModalSuccess = () => loadRoleGroups();

  if (!isAdmin) {
    return (
      <div className="bg-background flex min-h-screen flex-col">
        <div className="w-full space-y-4 p-6">
          <div className="space-y-1 border-b pb-3">
            <h1 className="text-2xl font-bold tracking-tight">권한 관리</h1>
          </div>
          <div className="bg-card flex h-64 flex-col items-center justify-center rounded-lg border p-6 shadow-sm">
            <AlertCircle className="text-destructive mb-4 h-12 w-12" />
            <h3 className="mb-2 text-lg font-semibold">접근 권한 없음</h3>
            <p className="text-muted-foreground mb-4 text-center text-sm">
              권한 관리는 관리자만 접근할 수 있습니다.
            </p>
            <Button variant="outline" onClick={() => window.history.back()}>
              뒤로 가기
            </Button>
          </div>
        </div>
        <ScrollToTop />
      </div>
    );
  }

  return (
    <div
      className="bg-background flex flex-col overflow-hidden"
      style={{ height: "100%" }}
    >
      <div className="flex w-full flex-1 flex-col gap-3 overflow-hidden p-6" style={{ minHeight: 0 }}>
        <div className="space-y-1 border-b pb-3">
          <h1 className="text-xl font-bold tracking-tight">권한 관리</h1>
          <p className="text-muted-foreground text-xs">
            권한 그룹 선택 시 권한있는/없는 직원과 메뉴 권한이 로드되고, 체크 즉시 반영됩니다.
          </p>
        </div>

        {error && (
          <div className="border-destructive/50 bg-destructive/10 rounded-lg border p-3">
            <div className="flex items-center justify-between">
              <p className="text-destructive text-sm font-semibold">{error}</p>
              <button onClick={() => setError(null)} className="text-destructive">
                <X className="h-4 w-4" />
              </button>
            </div>
          </div>
        )}

        {/* 상단 4분할 — inline style로 강제 (CSS 충돌 우회) */}
        <div
          style={{
            display: "flex",
            flexDirection: isXl ? "row" : "column",
            gap: "1rem",
            width: "100%",
            maxWidth: "100%",
            alignItems: "stretch",
          }}
        >
          {/* 권한 목록 */}
          <div
            className="bg-card flex flex-col rounded-lg border shadow-sm"
            style={isXl ? { width: 260, flex: "0 0 260px" } : {}}
          >
            <div className="flex items-center justify-between border-b p-3">
              <div className="flex items-center gap-2">
                <Shield className="text-primary h-4 w-4" />
                <h2 className="text-sm font-semibold">권한 목록</h2>
              </div>
              <Button size="sm" onClick={handleCreateRole} className="h-7 gap-1 px-2 text-xs">
                <Plus className="h-3 w-3" />
                생성
              </Button>
            </div>
            <div className="border-b p-2">
              <div className="relative">
                <Search className="text-muted-foreground absolute top-1/2 left-2.5 h-3.5 w-3.5 -translate-y-1/2" />
                <Input
                  placeholder="검색..."
                  value={searchText}
                  onChange={(e) => setSearchText(e.target.value)}
                  className="h-8 pl-8 text-xs"
                />
              </div>
            </div>
            <div
              style={{
                maxHeight: "360px",
                overflowY: "auto",
                overflowX: "hidden",
                flex: "1 1 auto",
                minHeight: 0,
              }}
            >
              {isLoading ? (
                <div className="flex h-32 items-center justify-center">
                  <div className="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent" />
                </div>
              ) : filteredRoleGroups.length === 0 ? (
                <p className="text-muted-foreground p-6 text-center text-xs">
                  등록된 권한 그룹이 없습니다
                </p>
              ) : (
                <ul className="divide-y">
                  {filteredRoleGroups.map((role) => {
                    const isSelected = selectedRole?.objid === role.objid;
                    return (
                      <li
                        key={role.objid}
                        onClick={() => setSelectedRole(role)}
                        className={cn(
                          "group cursor-pointer p-2.5 transition-colors",
                          isSelected ? "bg-primary/10" : "hover:bg-muted/50",
                        )}
                      >
                        <div className="flex items-start justify-between gap-2">
                          {/* authCode 는 내부 식별자만 — 화면에는 라벨(authName) 만 노출.
                              hover/title 로 코드값은 개발자가 확인 가능. */}
                          <div className="min-w-0 flex-1" title={role.authCode}>
                            <div
                              className={cn(
                                "truncate text-sm font-semibold",
                                isSelected && "text-primary",
                              )}
                            >
                              {role.authName}
                            </div>
                          </div>
                          <div
                            className={cn(
                              "flex flex-shrink-0 gap-0.5 transition-opacity",
                              isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100",
                            )}
                          >
                            <button
                              onClick={(e) => {
                                e.stopPropagation();
                                handleEditRole(role);
                              }}
                              className="hover:bg-background rounded p-1"
                              title="수정"
                            >
                              <Edit className="h-3 w-3" />
                            </button>
                            <button
                              onClick={(e) => {
                                e.stopPropagation();
                                handleDeleteRole(role);
                              }}
                              className="hover:bg-destructive hover:text-destructive-foreground rounded p-1"
                              title="삭제"
                            >
                              <Trash2 className="h-3 w-3" />
                            </button>
                          </div>
                        </div>
                      </li>
                    );
                  })}
                </ul>
              )}
            </div>
          </div>

          {/* 권한있는 직원 */}
          <div
            className="bg-card flex flex-col overflow-hidden rounded-lg border shadow-sm"
            style={isXl ? { flex: "1 1 0%", minWidth: 0, maxWidth: "calc(50% - 200px)" } : {}}
          >
            <div className="border-b p-3">
              <div className="mb-2 flex items-center gap-2">
                <Users className="text-primary h-4 w-4" />
                <h2 className="text-sm font-semibold">권한있는 직원 ({members.length})</h2>
              </div>
              <div className="flex items-center gap-2">
                <Checkbox
                  checked={
                    filteredMembers.length > 0 && checkedMembers.size === filteredMembers.length
                  }
                  onCheckedChange={(c) => handleMembersSelectAll(c === true)}
                  disabled={!selectedRole}
                />
                <span className="text-muted-foreground text-xs">전체선택</span>
                <div className="relative ml-auto max-w-[180px] flex-1">
                  <Search className="text-muted-foreground absolute top-1/2 left-2 h-3 w-3 -translate-y-1/2" />
                  <Input
                    placeholder="검색"
                    value={memberSearch}
                    onChange={(e) => setMemberSearch(e.target.value)}
                    className="h-7 pl-7 text-xs"
                    disabled={!selectedRole}
                  />
                </div>
              </div>
            </div>
            <div
              style={{
                height: "360px",
                overflowY: "auto",
                overflowX: "hidden",
                flex: "0 0 360px",
              }}
            >
              {!selectedRole ? (
                <div className="flex h-full items-center justify-center">
                  <p className="text-muted-foreground text-xs">권한 그룹을 선택하세요</p>
                </div>
              ) : isLoadingWorkspace ? (
                <div className="flex h-full items-center justify-center">
                  <div className="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent" />
                </div>
              ) : filteredMembers.length === 0 ? (
                <p className="text-muted-foreground p-6 text-center text-xs">
                  {memberSearch ? "검색 결과 없음" : "권한있는 직원이 없습니다"}
                </p>
              ) : (
                <ul className="divide-y">
                  {filteredMembers.map((u) => (
                    <li
                      key={u.userId}
                      onClick={() => toggleMemberCheck(u.userId)}
                      className={cn(
                        "flex cursor-pointer items-center gap-2 p-2 transition-colors",
                        checkedMembers.has(u.userId) ? "bg-muted" : "hover:bg-muted/50",
                      )}
                    >
                      <Checkbox
                        checked={checkedMembers.has(u.userId)}
                        onCheckedChange={() => toggleMemberCheck(u.userId)}
                        onClick={(e) => e.stopPropagation()}
                      />
                      <div className="flex min-w-0 flex-1 items-center gap-2">
                        <div className="truncate text-sm">{u.userName || u.userId}</div>
                        {u.deptName && (
                          <div className="text-muted-foreground ml-auto truncate text-[11px]">
                            {u.deptName}
                          </div>
                        )}
                      </div>
                    </li>
                  ))}
                </ul>
              )}
            </div>
          </div>

          {/* 이동 버튼 */}
          <div
            className="flex flex-row items-center justify-center gap-2 px-2 xl:flex-col xl:px-0"
            style={isXl ? { flex: "0 0 auto" } : {}}
          >
            <Button
              variant="default"
              size="sm"
              onClick={handleAddMembers}
              disabled={!selectedRole || checkedNonMembers.size === 0}
              className="h-9 w-20 gap-1 text-xs xl:w-24"
              title="권한 부여"
            >
              <ChevronLeft className="h-4 w-4" />
              <span>추가</span>
            </Button>
            <Button
              variant="outline"
              size="sm"
              onClick={handleRemoveMembers}
              disabled={!selectedRole || checkedMembers.size === 0}
              className="h-9 w-20 gap-1 text-xs xl:w-24"
              title="권한 회수"
            >
              <span>제거</span>
              <ChevronRight className="h-4 w-4" />
            </Button>
          </div>

          {/* 권한없는 직원 */}
          <div
            className="bg-card flex flex-col overflow-hidden rounded-lg border shadow-sm"
            style={isXl ? { flex: "1 1 0%", minWidth: 0, maxWidth: "calc(50% - 200px)" } : {}}
          >
            <div className="border-b p-3">
              <div className="mb-2 flex items-center gap-2">
                <Users className="text-muted-foreground h-4 w-4" />
                <h2 className="text-sm font-semibold">권한없는 직원 ({nonMembers.length})</h2>
              </div>
              <div className="flex items-center gap-2">
                <Checkbox
                  checked={
                    filteredNonMembers.length > 0 &&
                    checkedNonMembers.size === filteredNonMembers.length
                  }
                  onCheckedChange={(c) => handleNonMembersSelectAll(c === true)}
                  disabled={!selectedRole}
                />
                <span className="text-muted-foreground text-xs">전체선택</span>
                <div className="relative ml-auto max-w-[180px] flex-1">
                  <Search className="text-muted-foreground absolute top-1/2 left-2 h-3 w-3 -translate-y-1/2" />
                  <Input
                    placeholder="검색"
                    value={nonMemberSearch}
                    onChange={(e) => setNonMemberSearch(e.target.value)}
                    className="h-7 pl-7 text-xs"
                    disabled={!selectedRole}
                  />
                </div>
              </div>
            </div>
            <div
              style={{
                height: "360px",
                overflowY: "auto",
                overflowX: "hidden",
                flex: "0 0 360px",
              }}
            >
              {!selectedRole ? (
                <div className="flex h-full items-center justify-center">
                  <p className="text-muted-foreground text-xs">권한 그룹을 선택하세요</p>
                </div>
              ) : isLoadingWorkspace ? (
                <div className="flex h-full items-center justify-center">
                  <div className="border-primary h-5 w-5 animate-spin rounded-full border-2 border-t-transparent" />
                </div>
              ) : filteredNonMembers.length === 0 ? (
                <p className="text-muted-foreground p-6 text-center text-xs">
                  {nonMemberSearch ? "검색 결과 없음" : "권한없는 직원이 없습니다"}
                </p>
              ) : (
                <ul className="divide-y">
                  {filteredNonMembers.map((u) => (
                    <li
                      key={u.userId}
                      onClick={() => toggleNonMemberCheck(u.userId)}
                      className={cn(
                        "flex cursor-pointer items-center gap-2 p-2 transition-colors",
                        checkedNonMembers.has(u.userId) ? "bg-muted" : "hover:bg-muted/50",
                      )}
                    >
                      <Checkbox
                        checked={checkedNonMembers.has(u.userId)}
                        onCheckedChange={() => toggleNonMemberCheck(u.userId)}
                        onClick={(e) => e.stopPropagation()}
                      />
                      <div className="flex min-w-0 flex-1 items-center gap-2">
                        <div className="truncate text-sm">{u.userName || u.userId}</div>
                        {u.deptName && (
                          <div className="text-muted-foreground ml-auto truncate text-[11px]">
                            {u.deptName}
                          </div>
                        )}
                      </div>
                    </li>
                  ))}
                </ul>
              )}
            </div>
          </div>
        </div>

        {/* 메뉴 트리 — 남은 공간 모두 차지 + 자체 스크롤 */}
        <div
          className="bg-card flex flex-1 flex-col rounded-lg border shadow-sm"
          style={{ minHeight: 0 }}
        >
          <div className="border-b p-3">
            <h2 className="text-sm font-semibold">
              메뉴 전체 트리구조{" "}
              {selectedRole && (
                <span className="text-muted-foreground text-xs">({selectedRole.authName})</span>
              )}
            </h2>
            <p className="text-muted-foreground mt-0.5 text-[11px]">
              체크된 것들만 시스템에서 해당 버튼이 노출됩니다 · 체크 즉시 서버 반영
            </p>
          </div>
          {!selectedRole ? (
            <div className="flex flex-1 items-center justify-center">
              <p className="text-muted-foreground text-sm">권한 그룹을 선택하세요</p>
            </div>
          ) : isLoadingWorkspace ? (
            <div className="flex flex-1 items-center justify-center">
              <div className="border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
            </div>
          ) : menuTree.length === 0 ? (
            <p className="text-muted-foreground p-12 text-center text-sm">등록된 메뉴가 없습니다</p>
          ) : (
            <div className="flex-1 overflow-auto" style={{ minHeight: 0 }}>
              <table className="w-full text-sm">
                <thead className="bg-muted/40 sticky top-0 z-10">
                  <tr>
                    <th className="w-[40%] py-2.5 pl-3 text-left text-xs font-semibold">
                      메뉴 전체 트리구조
                    </th>
                    <th className="w-[20%] py-2.5 text-center text-xs font-semibold">
                      <div className="flex flex-col items-center gap-1">
                        <span>등록/수정</span>
                        <Checkbox
                          checked={isColumnAllChecked("edit")}
                          onCheckedChange={(c) => handleBulkColumn("edit", c === true)}
                        />
                      </div>
                    </th>
                    <th className="w-[20%] py-2.5 text-center text-xs font-semibold">
                      <div className="flex flex-col items-center gap-1">
                        <span>삭제</span>
                        <Checkbox
                          checked={isColumnAllChecked("delete")}
                          onCheckedChange={(c) => handleBulkColumn("delete", c === true)}
                        />
                      </div>
                    </th>
                    <th className="w-[20%] py-2.5 text-center text-xs font-semibold">
                      <div className="flex flex-col items-center gap-1">
                        <span>조회</span>
                        <Checkbox
                          checked={isColumnAllChecked("read")}
                          onCheckedChange={(c) => handleBulkColumn("read", c === true)}
                        />
                      </div>
                    </th>
                  </tr>
                </thead>
                <tbody>{menuTree.map((n) => renderMenuRow(n))}</tbody>
              </table>
            </div>
          )}
        </div>

        <RoleFormModal
          isOpen={formModal.isOpen}
          onClose={handleFormClose}
          onSuccess={handleModalSuccess}
          editingRole={formModal.editingRole}
        />
        <RoleDeleteModal
          isOpen={deleteModal.isOpen}
          onClose={handleDeleteClose}
          onSuccess={handleModalSuccess}
          role={deleteModal.role}
        />
      </div>
      <ScrollToTop />
    </div>
  );
}
