import { useState, useEffect, useCallback, useRef } from 'react';
import type { ApiKey, ApiKeyDetail, ApiKeyRequest } from '../../types/apikey';
import type { ServiceInfo, ServiceApi } from '../../types/service';
import {
  getAllKeys,
  getKeyDetail,
  revokeKey,
  getAllRequests,
  reviewRequest,
} from '../../services/apiKeyService';
import { getServices, getServiceApis } from '../../services/serviceService';
import Badge, { type BadgeVariant } from '../../components/ui/Badge';
import Button from '../../components/ui/Button';

const STATUS_VARIANT: Record<string, BadgeVariant> = {
  ACTIVE: 'success',
  PENDING: 'warning',
  REVOKED: 'danger',
  EXPIRED: 'default',
  INACTIVE: 'default',
  APPROVED: 'success',
  REJECTED: 'danger',
};

const METHOD_BADGE_STYLE: Record<string, string> = {
  GET: 'bg-emerald-900/80 text-emerald-300 border border-emerald-700/50',
  POST: 'bg-amber-900/80 text-amber-300 border border-amber-700/50',
  PUT: 'bg-blue-900/80 text-blue-300 border border-blue-700/50',
  DELETE: 'bg-red-900/80 text-red-300 border border-red-700/50',
};

const KEY_STATUS_CONFIG: Record<string, { label: string; dotClass: string; bgClass: string; borderClass: string; textClass: string; iconBgClass: string; iconBorderClass: string }> = {
  ACTIVE: { label: '활성', dotClass: 'bg-green-500', bgClass: 'bg-green-500/10', borderClass: 'border-green-500/30', textClass: 'text-green-400', iconBgClass: 'bg-green-500/15', iconBorderClass: 'border-green-500/30' },
  PENDING: { label: '대기중', dotClass: 'bg-yellow-500', bgClass: 'bg-yellow-500/10', borderClass: 'border-yellow-500/30', textClass: 'text-yellow-400', iconBgClass: 'bg-yellow-500/15', iconBorderClass: 'border-yellow-500/30' },
  REVOKED: { label: '폐기됨', dotClass: 'bg-red-500', bgClass: 'bg-red-500/10', borderClass: 'border-red-500/30', textClass: 'text-red-400', iconBgClass: 'bg-red-500/15', iconBorderClass: 'border-red-500/30' },
  EXPIRED: { label: '만료됨', dotClass: 'bg-gray-500', bgClass: 'bg-gray-500/10', borderClass: 'border-gray-500/30', textClass: 'text-gray-400', iconBgClass: 'bg-gray-500/15', iconBorderClass: 'border-gray-500/30' },
};

const formatDateTime = (dateStr: string | null): string => {
  if (!dateStr) return '-';
  return new Date(dateStr).toLocaleString('ko-KR');
};

interface AllApisMap {
  services: ServiceInfo[];
  apisByService: Record<number, ServiceApi[]>;
  apiById: Record<number, ServiceApi & { serviceName: string }>;
}

const IndeterminateCheckbox = ({ checked, indeterminate, onChange, className }: { checked: boolean; indeterminate: boolean; onChange: () => void; className?: string }) => {
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    if (ref.current) ref.current.indeterminate = indeterminate;
  }, [indeterminate]);
  return <input ref={ref} type="checkbox" checked={checked} onChange={onChange} className={className || 'rounded'} />;
};

const KeyAdminPage = () => {
  const [activeTab, setActiveTab] = useState<'requests' | 'keys'>('requests');
  const [requestFilter, setRequestFilter] = useState('ALL');
  const [keyFilter, setKeyFilter] = useState('ALL');
  const [searchQuery, setSearchQuery] = useState('');
  const [requestPage, setRequestPage] = useState(0);
  const [keyPage, setKeyPage] = useState(0);
  const PAGE_SIZE = 15;

  // Shared state
  const [allApisMap, setAllApisMap] = useState<AllApisMap>({ services: [], apisByService: {}, apiById: {} });
  const [error, setError] = useState<string | null>(null);

  // Requests tab state
  const [requests, setRequests] = useState<ApiKeyRequest[]>([]);
  const [requestsLoading, setRequestsLoading] = useState(true);
  const [isReviewModalOpen, setIsReviewModalOpen] = useState(false);
  const [selectedRequest, setSelectedRequest] = useState<ApiKeyRequest | null>(null);
  const [adjustedApiIds, setAdjustedApiIds] = useState<Set<number>>(new Set());
  const [reviewComment, setReviewComment] = useState('');
  const [adjustedFromDate, setAdjustedFromDate] = useState('');
  const [adjustedToDate, setAdjustedToDate] = useState('');
  const [adjustedDailyLimit, setAdjustedDailyLimit] = useState('');
  const [activeReviewTab, setActiveReviewTab] = useState<'info' | 'apis'>('info');
  const [showRejectConfirm, setShowRejectConfirm] = useState(false);
  const [showApproveConfirm, setShowApproveConfirm] = useState(false);
  const [rejectReason, setRejectReason] = useState('');
  const [expandedReviewServices, setExpandedReviewServices] = useState<Set<number>>(new Set());
  const [showAddApiModal, setShowAddApiModal] = useState(false);
  const [addApiSearch, setAddApiSearch] = useState('');
  const [addApiSelected, setAddApiSelected] = useState<Set<number>>(new Set());

  // Read-only mode for processed requests
  const [isReviewReadOnly, setIsReviewReadOnly] = useState(false);

  // Keys tab state
  const [allKeys, setAllKeys] = useState<ApiKey[]>([]);
  const [keysLoading, setKeysLoading] = useState(true);
  const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
  const [selectedKeyDetail, setSelectedKeyDetail] = useState<ApiKeyDetail | null>(null);
  const [detailCopied, setDetailCopied] = useState(false);
  const [keyVisible, setKeyVisible] = useState(false);

  // Raw key modal (after approve)
  const [rawKeyModal, setRawKeyModal] = useState<{ keyName: string; rawKey: string } | null>(null);
  const [rawKeyCopied, setRawKeyCopied] = useState(false);

  const fetchAllApis = useCallback(async () => {
    try {
      const servicesRes = await getServices();
      if (servicesRes.success && servicesRes.data) {
        const services = servicesRes.data;
        const apisByService: Record<number, ServiceApi[]> = {};
        const apiById: Record<number, ServiceApi & { serviceName: string }> = {};

        await Promise.all(
          services.map(async (service) => {
            const apisRes = await getServiceApis(service.serviceId);
            if (apisRes.success && apisRes.data) {
              apisByService[service.serviceId] = apisRes.data;
              apisRes.data.forEach((api) => {
                apiById[api.apiId] = { ...api, serviceName: service.serviceName };
              });
            }
          }),
        );

        setAllApisMap({ services, apisByService, apiById });
      }
    } catch {
      // Silently fail for API map loading
    }
  }, []);

  const fetchRequests = useCallback(async () => {
    try {
      setRequestsLoading(true);
      const res = await getAllRequests();
      if (res.success && res.data) {
        setRequests(res.data);
      }
    } catch {
      setError('신청 목록을 불러오는데 실패했습니다.');
    } finally {
      setRequestsLoading(false);
    }
  }, []);

  const fetchKeys = useCallback(async () => {
    try {
      setKeysLoading(true);
      const res = await getAllKeys();
      if (res.success && res.data) {
        setAllKeys(res.data);
      }
    } catch {
      setError('키 목록을 불러오는데 실패했습니다.');
    } finally {
      setKeysLoading(false);
    }
  }, []);

  useEffect(() => {
    fetchAllApis();
    fetchRequests();
    fetchKeys();
  }, [fetchAllApis, fetchRequests, fetchKeys]);

  // --- Requests tab handlers ---

  const handleOpenDetail = (req: ApiKeyRequest) => {
    // 검토 모달을 읽기 전용으로 열기
    setSelectedRequest(req);
    setAdjustedApiIds(new Set(req.requestedApiIds));
    setReviewComment(req.reviewComment || '');
    setAdjustedFromDate(req.usageFromDate ? req.usageFromDate.split('T')[0] : '');
    setAdjustedToDate(req.usageToDate ? req.usageToDate.split('T')[0] : '');
    setAdjustedDailyLimit(req.dailyRequestEstimate != null ? String(req.dailyRequestEstimate) : '');
    setActiveReviewTab('info');
    setShowRejectConfirm(false);
    setShowApproveConfirm(false);
    setRejectReason('');
    setExpandedReviewServices(new Set());
    setIsReviewReadOnly(true);
    setIsReviewModalOpen(true);
    setError(null);
  };

  const handleOpenReview = (req: ApiKeyRequest) => {
    setSelectedRequest(req);
    setAdjustedApiIds(new Set(req.requestedApiIds));
    setReviewComment('');
    setAdjustedFromDate(req.usageFromDate ? req.usageFromDate.split('T')[0] : '');
    setAdjustedToDate(req.usageToDate ? req.usageToDate.split('T')[0] : '');
    setAdjustedDailyLimit(req.dailyRequestEstimate != null ? String(req.dailyRequestEstimate) : '');
    setActiveReviewTab('info');
    setShowRejectConfirm(false);
    setShowApproveConfirm(false);
    setRejectReason('');
    setExpandedReviewServices(new Set());
    setIsReviewReadOnly(false);
    setError(null);
    setIsReviewModalOpen(true);
  };

  const handleCloseReview = () => {
    setIsReviewModalOpen(false);
    setSelectedRequest(null);
    setShowRejectConfirm(false);
    setShowApproveConfirm(false);
    setRejectReason('');
    setError(null);
  };

  const handleReviewSubmit = async (status: 'APPROVED' | 'REJECTED') => {
    if (!selectedRequest) return;
    setError(null);

    const comment = status === 'REJECTED' ? rejectReason : reviewComment;

    try {
      const res = await reviewRequest(selectedRequest.requestId, {
        status,
        reviewComment: comment || undefined,
        adjustedApiIds: status === 'APPROVED' ? Array.from(adjustedApiIds) : undefined,
        adjustedFromDate: status === 'APPROVED' && adjustedFromDate ? adjustedFromDate : undefined,
        adjustedToDate: status === 'APPROVED' && adjustedToDate ? adjustedToDate : undefined,
        adjustedDailyRequestLimit: status === 'APPROVED' && adjustedDailyLimit ? Number(adjustedDailyLimit) : undefined,
      });

      if (res.success) {
        handleCloseReview();
        await fetchRequests();
        await fetchKeys();

        if (status === 'APPROVED' && res.data) {
          setRawKeyModal({ keyName: res.data.keyName, rawKey: res.data.rawKey });
        }
      } else {
        setError(res.message || '검토 처리에 실패했습니다.');
      }
    } catch {
      setError('검토 처리에 실패했습니다.');
    }
  };

  // --- Keys tab handlers ---

  const handleViewDetail = async (key: ApiKey) => {
    setError(null);
    try {
      const res = await getKeyDetail(key.apiKeyId);
      if (res.success && res.data) {
        setSelectedKeyDetail(res.data);
        setDetailCopied(false);
        setIsDetailModalOpen(true);
      } else {
        setError(res.message || '키 상세 정보를 불러오는데 실패했습니다.');
      }
    } catch {
      setError('키 상세 정보를 불러오는데 실패했습니다.');
    }
  };

  const handleCopyDecryptedKey = async () => {
    if (!selectedKeyDetail) return;
    await navigator.clipboard.writeText(selectedKeyDetail.decryptedKey);
    setDetailCopied(true);
    setTimeout(() => setDetailCopied(false), 2000);
  };

  const handleRevokeKey = async (key: ApiKey) => {
    if (!window.confirm(`'${key.keyName}' 키를 폐기하시겠습니까? 이 작업은 되돌릴 수 없습니다.`)) return;

    try {
      const res = await revokeKey(key.apiKeyId);
      if (res.success) {
        await fetchKeys();
      } else {
        setError(res.message || '키 폐기에 실패했습니다.');
      }
    } catch {
      setError('키 폐기에 실패했습니다.');
    }
  };

  const handleCopyRawKey = async () => {
    if (!rawKeyModal) return;
    await navigator.clipboard.writeText(rawKeyModal.rawKey);
    setRawKeyCopied(true);
    setTimeout(() => setRawKeyCopied(false), 2000);
  };

  // --- Computed KPI values ---
  const pendingCount = requests.filter((r) => r.status === 'PENDING').length;
  const activeKeyCount = allKeys.filter((k) => k.status === 'ACTIVE').length;
  const now = Date.now();
  const fourteenDaysMs = 14 * 24 * 60 * 60 * 1000;
  const expiringCount = allKeys.filter((k) => k.status === 'ACTIVE' && k.expiresAt && (new Date(k.expiresAt).getTime() - now) > 0 && (new Date(k.expiresAt).getTime() - now) <= fourteenDaysMs).length;
  const revokedCount = allKeys.filter((k) => k.status === 'REVOKED').length;

  // --- Filter logic ---
  const filteredRequests = requests.filter((r) => {
    if (requestFilter !== 'ALL' && r.status !== requestFilter) return false;
    if (searchQuery) {
      const q = searchQuery.toLowerCase();
      if (!r.keyName.toLowerCase().includes(q) && !r.userName.toLowerCase().includes(q)) return false;
    }
    return true;
  });

  const filteredKeys = allKeys.filter((k) => {
    if (keyFilter !== 'ALL' && k.status !== keyFilter) return false;
    if (searchQuery) {
      const q = searchQuery.toLowerCase();
      if (!k.keyName.toLowerCase().includes(q) && !k.maskedKey.toLowerCase().includes(q)) return false;
    }
    return true;
  });

  const pagedRequests = filteredRequests.slice(requestPage * PAGE_SIZE, (requestPage + 1) * PAGE_SIZE);
  const requestTotalPages = Math.ceil(filteredRequests.length / PAGE_SIZE);
  const pagedKeys = filteredKeys.slice(keyPage * PAGE_SIZE, (keyPage + 1) * PAGE_SIZE);
  const keyTotalPages = Math.ceil(filteredKeys.length / PAGE_SIZE);

  const getDaysUntilExpiry = (expiresAt: string | null): number | null => {
    if (!expiresAt) return null;
    const diff = new Date(expiresAt).getTime() - now;
    if (diff <= 0) return 0;
    return Math.ceil(diff / (1000 * 60 * 60 * 24));
  };

  const handleTabSwitch = (tab: 'requests' | 'keys') => {
    setActiveTab(tab);
    setSearchQuery('');
  };

  // --- Filter chip counts ---
  const requestStatusCounts: Record<string, number> = { ALL: requests.length, PENDING: 0, APPROVED: 0, REJECTED: 0 };
  requests.forEach((r) => { requestStatusCounts[r.status] = (requestStatusCounts[r.status] || 0) + 1; });

  const keyStatusCounts: Record<string, number> = { ALL: allKeys.length, ACTIVE: 0, EXPIRED: 0, REVOKED: 0 };
  allKeys.forEach((k) => { if (k.status in keyStatusCounts) keyStatusCounts[k.status] = (keyStatusCounts[k.status] || 0) + 1; });

  return (
    <div className="max-w-7xl mx-auto">
      {/* Page Header */}
      <div className="mb-6">
        <div className="flex items-center gap-3 mb-1">
          <div className="w-10 h-10 rounded-xl bg-[var(--color-primary-subtle)] flex items-center justify-center">
            <svg className="w-5 h-5 text-[var(--color-primary)]" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
          </div>
          <div>
            <h1 className="text-2xl font-bold text-[var(--color-text-primary)]">API Key 관리</h1>
            <p className="text-sm text-[var(--color-text-secondary)]">API Key 신청 검토 및 발급된 키의 수명주기를 관리합니다</p>
          </div>
        </div>
      </div>

      {error && !isReviewModalOpen && !isDetailModalOpen && (
        <div className="mb-4 p-3 bg-red-50 text-red-700 rounded-lg text-sm border border-red-200">{error}</div>
      )}

      {/* KPI Cards */}
      <div className="flex flex-wrap gap-4 mb-6">
        <div className="bg-[var(--color-bg-surface)] border border-[var(--color-border)] rounded-xl p-5 flex items-center gap-4 flex-1 min-w-[200px]">
          <div className="w-11 h-11 rounded-xl flex items-center justify-center bg-amber-50">
            <svg className="w-5 h-5 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
          </div>
          <div>
            <div className="text-3xl font-bold text-[var(--color-text-primary)]">{pendingCount}</div>
            <div className="text-sm text-[var(--color-text-secondary)]">대기 중 신청</div>
          </div>
        </div>
        <div className="bg-[var(--color-bg-surface)] border border-[var(--color-border)] rounded-xl p-5 flex items-center gap-4 flex-1 min-w-[200px]">
          <div className="w-11 h-11 rounded-xl flex items-center justify-center bg-green-50">
            <svg className="w-5 h-5 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>
          </div>
          <div>
            <div className="text-3xl font-bold text-[var(--color-text-primary)]">{activeKeyCount}</div>
            <div className="text-sm text-[var(--color-text-secondary)]">활성 키</div>
          </div>
        </div>
        <div className="bg-[var(--color-bg-surface)] border border-[var(--color-border)] rounded-xl p-5 flex items-center gap-4 flex-1 min-w-[200px]">
          <div className="w-11 h-11 rounded-xl flex items-center justify-center bg-amber-50">
            <svg className="w-5 h-5 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
          </div>
          <div>
            <div className="text-3xl font-bold text-[var(--color-text-primary)]">{expiringCount}</div>
            <div className="text-sm text-[var(--color-text-secondary)]">만료 임박 (14일)</div>
          </div>
        </div>
        <div className="bg-[var(--color-bg-surface)] border border-[var(--color-border)] rounded-xl p-5 flex items-center gap-4 flex-1 min-w-[200px]">
          <div className="w-11 h-11 rounded-xl flex items-center justify-center bg-red-50">
            <svg className="w-5 h-5 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" /></svg>
          </div>
          <div>
            <div className="text-3xl font-bold text-[var(--color-text-primary)]">{revokedCount}</div>
            <div className="text-sm text-[var(--color-text-secondary)]">폐기된 키</div>
          </div>
        </div>
      </div>

      {/* Unified Card Container */}
      <div className="bg-[var(--color-bg-surface)] border border-[var(--color-border)] rounded-xl overflow-hidden">
        {/* Tab Header */}
        <div className="flex items-center justify-between border-b border-[var(--color-border)] px-4">
          <div className="flex">
            <button
              onClick={() => handleTabSwitch('requests')}
              className={`flex items-center gap-2 px-4 py-3.5 text-sm -mb-px border-b-2 transition-colors ${
                activeTab === 'requests'
                  ? 'border-[var(--color-primary)] text-[var(--color-primary)] font-semibold'
                  : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'
              }`}
            >
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
              신청 관리
              {pendingCount > 0 && (
                <Badge variant="warning" size="sm">{pendingCount}</Badge>
              )}
            </button>
            <button
              onClick={() => handleTabSwitch('keys')}
              className={`flex items-center gap-2 px-4 py-3.5 text-sm -mb-px border-b-2 transition-colors ${
                activeTab === 'keys'
                  ? 'border-[var(--color-primary)] text-[var(--color-primary)] font-semibold'
                  : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'
              }`}
            >
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>
              키 관리
              {activeKeyCount > 0 && (
                <Badge variant="primary" size="sm">{activeKeyCount}</Badge>
              )}
            </button>
          </div>
          <div className="relative">
            <svg className="w-4 h-4 text-[var(--color-text-tertiary)] absolute left-3 top-1/2 -translate-y-1/2" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
            <input
              type="text"
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              placeholder="검색..."
              className="bg-[var(--color-bg-base)] border border-[var(--color-border)] rounded-lg pl-9 pr-3 py-2 text-sm text-[var(--color-text-primary)] placeholder-gray-400 w-56 focus:ring-2 focus:ring-[var(--color-primary)] focus:outline-none"
            />
          </div>
        </div>

        {/* Filter Chips */}
        <div className="px-4 py-3 border-b border-[var(--color-border)] flex items-center gap-2">
          {activeTab === 'requests' ? (
            <>
              {([['ALL', '전체'], ['PENDING', '대기'], ['APPROVED', '승인'], ['REJECTED', '반려']] as const).map(([value, label]) => (
                <button
                  key={value}
                  onClick={() => { setRequestFilter(value); setRequestPage(0); }}
                  className={`px-3.5 py-1.5 rounded-full text-sm font-medium border cursor-pointer transition-colors ${
                    requestFilter === value
                      ? 'bg-[var(--color-primary-subtle)] border-[var(--color-primary)] text-[var(--color-primary)]'
                      : 'border-[var(--color-border)] text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-base)]'
                  }`}
                >
                  {label}
                  <Badge size="sm" className="ml-1.5 opacity-70">{requestStatusCounts[value] ?? 0}</Badge>
                </button>
              ))}
            </>
          ) : (
            <>
              {([['ALL', '전체'], ['ACTIVE', '활성'], ['EXPIRED', '만료'], ['REVOKED', '폐기']] as const).map(([value, label]) => (
                <button
                  key={value}
                  onClick={() => { setKeyFilter(value); setKeyPage(0); }}
                  className={`px-3.5 py-1.5 rounded-full text-sm font-medium border cursor-pointer transition-colors ${
                    keyFilter === value
                      ? 'bg-[var(--color-primary-subtle)] border-[var(--color-primary)] text-[var(--color-primary)]'
                      : 'border-[var(--color-border)] text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-base)]'
                  }`}
                >
                  {label}
                  <Badge size="sm" className="ml-1.5 opacity-70">{keyStatusCounts[value] ?? 0}</Badge>
                </button>
              ))}
            </>
          )}
        </div>

        {/* Tab: 신청 관리 */}
        {activeTab === 'requests' && (
          <>
            {requestsLoading ? (
              <div className="text-center py-10 text-[var(--color-text-secondary)]">로딩 중...</div>
            ) : (
              <>
                <div className="overflow-x-auto">
                  <table className="w-full divide-y divide-[var(--color-border)] text-sm">
                    <thead className="bg-[var(--color-bg-base)]">
                      <tr>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">신청자</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">키 이름</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">상태</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">API 수</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">신청일시</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">Actions</th>
                      </tr>
                    </thead>
                    <tbody className="divide-y divide-[var(--color-border)]">
                      {pagedRequests.map((req) => (
                        <tr key={req.requestId} className="hover:bg-[var(--color-bg-base)] transition-colors">
                          <td className="px-4 py-3 text-[var(--color-text-primary)]">
                            <span className="inline-flex items-center gap-1.5">
                              <svg className="w-3.5 h-3.5 text-[var(--color-text-tertiary)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
                              {req.userName}
                            </span>
                          </td>
                          <td className="px-4 py-3 text-[var(--color-text-primary)] font-medium">{req.keyName}</td>
                          <td className="px-4 py-3">
                            <Badge variant={STATUS_VARIANT[req.status] || 'default'}>
                              {req.status === 'PENDING' ? '대기' : req.status === 'APPROVED' ? '승인' : '반려'}
                            </Badge>
                          </td>
                          <td className="px-4 py-3">
                            <span className="bg-[var(--color-primary-subtle)] text-[var(--color-primary)] px-2 py-0.5 rounded text-xs font-semibold">{req.requestedApiIds.length}개</span>
                          </td>
                          <td className="px-4 py-3 text-[var(--color-text-secondary)]">{formatDateTime(req.createdAt)}</td>
                          <td className="px-4 py-3">
                            <div className="flex gap-2">
                              {req.status === 'PENDING' && (
                                <Button
                                  onClick={() => handleOpenReview(req)}
                                  variant="secondary"
                                  size="sm"
                                >
                                  검토
                                </Button>
                              )}
                              {(req.status === 'APPROVED' || req.status === 'REJECTED') && (
                                <Button
                                  onClick={() => handleOpenDetail(req)}
                                  variant="secondary"
                                  size="sm"
                                >
                                  상세
                                </Button>
                              )}
                            </div>
                          </td>
                        </tr>
                      ))}
                      {filteredRequests.length === 0 && (
                        <tr>
                          <td colSpan={6} className="px-4 py-8 text-center text-[var(--color-text-tertiary)]">
                            {searchQuery || requestFilter !== 'ALL' ? '조건에 맞는 신청이 없습니다.' : '신청 내역이 없습니다.'}
                          </td>
                        </tr>
                      )}
                    </tbody>
                  </table>
                </div>
                {/* Table Footer */}
                <div className="px-4 py-3 border-t border-[var(--color-border)] flex items-center justify-between">
                  <span className="text-sm text-[var(--color-text-secondary)]">총 {filteredRequests.length}건</span>
                  {requestTotalPages > 1 && (
                    <div className="flex items-center gap-1">
                      <button onClick={() => setRequestPage(Math.max(0, requestPage - 1))} disabled={requestPage === 0}
                        className="px-2.5 py-1 text-xs rounded border border-[var(--color-border)] text-[var(--color-text-secondary)] disabled:opacity-40">이전</button>
                      <span className="text-xs text-[var(--color-text-secondary)] px-2">{requestPage + 1} / {requestTotalPages}</span>
                      <button onClick={() => setRequestPage(Math.min(requestTotalPages - 1, requestPage + 1))} disabled={requestPage >= requestTotalPages - 1}
                        className="px-2.5 py-1 text-xs rounded border border-[var(--color-border)] text-[var(--color-text-secondary)] disabled:opacity-40">다음</button>
                    </div>
                  )}
                </div>
              </>
            )}
          </>
        )}

        {/* Tab: 키 관리 */}
        {activeTab === 'keys' && (
          <>
            {keysLoading ? (
              <div className="text-center py-10 text-[var(--color-text-secondary)]">로딩 중...</div>
            ) : (
              <>
                <div className="overflow-x-auto">
                  <table className="w-full divide-y divide-[var(--color-border)] text-sm">
                    <thead className="bg-[var(--color-bg-base)]">
                      <tr>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">소유자</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">키 이름</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">Prefix</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">상태</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">만료일시</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">마지막 사용일시</th>
                        <th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[var(--color-text-secondary)]">Actions</th>
                      </tr>
                    </thead>
                    <tbody className="divide-y divide-[var(--color-border)]">
                      {pagedKeys.map((key) => {
                        const daysLeft = getDaysUntilExpiry(key.expiresAt);
                        const isExpiringSoon = key.status === 'ACTIVE' && daysLeft !== null && daysLeft <= 14;
                        return (
                          <tr key={key.apiKeyId} className="hover:bg-[var(--color-bg-base)] transition-colors">
                            <td className="px-4 py-3 text-[var(--color-text-primary)]">
                              <span className="inline-flex items-center gap-1.5">
                                <svg className="w-3.5 h-3.5 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
                                {key.userName || '-'}
                              </span>
                            </td>
                            <td className="px-4 py-3 text-gray-900 dark:text-gray-100 font-medium">{key.keyName}</td>
                            <td className="px-4 py-3">
                              <Badge variant="info" className="font-mono font-semibold">{key.apiKeyPrefix}</Badge>
                            </td>
                            <td className="px-4 py-3">
                              <Badge variant={STATUS_VARIANT[key.status] || 'default'}>
                                {KEY_STATUS_CONFIG[key.status]?.label || key.status}
                              </Badge>
                            </td>
                            <td className="px-4 py-3">
                              <div className="flex items-center gap-2">
                                <span className="text-[var(--color-text-secondary)]">{key.expiresAt ? formatDateTime(key.expiresAt) : '영구'}</span>
                                {isExpiringSoon && (
                                  <Badge variant={daysLeft !== null && daysLeft <= 3 ? 'danger' : 'warning'} size="sm">
                                    &#x26A0; {daysLeft}일 남음
                                  </Badge>
                                )}
                              </div>
                            </td>
                            <td className="px-4 py-3 text-gray-500 dark:text-gray-400">{formatDateTime(key.lastUsedAt)}</td>
                            <td className="px-4 py-3">
                              <div className="flex gap-2">
                                <Button
                                  onClick={() => handleViewDetail(key)}
                                  variant="secondary"
                                  size="sm"
                                >
                                  상세
                                </Button>
                                {key.status === 'ACTIVE' && (
                                  <Button
                                    onClick={() => handleRevokeKey(key)}
                                    variant="danger"
                                    size="sm"
                                  >
                                    폐기
                                  </Button>
                                )}
                              </div>
                            </td>
                          </tr>
                        );
                      })}
                      {filteredKeys.length === 0 && (
                        <tr>
                          <td colSpan={7} className="px-4 py-8 text-center text-gray-400 dark:text-gray-500">
                            {searchQuery || keyFilter !== 'ALL' ? '조건에 맞는 키가 없습니다.' : '등록된 API Key가 없습니다.'}
                          </td>
                        </tr>
                      )}
                    </tbody>
                  </table>
                </div>
                {/* Table Footer */}
                <div className="px-4 py-3 border-t border-gray-100 dark:border-gray-700/50 flex items-center justify-between">
                  <span className="text-sm text-gray-500 dark:text-gray-400">총 {filteredKeys.length}건</span>
                  {keyTotalPages > 1 && (
                    <div className="flex items-center gap-1">
                      <button onClick={() => setKeyPage(Math.max(0, keyPage - 1))} disabled={keyPage === 0}
                        className="px-2.5 py-1 text-xs rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 disabled:opacity-40">이전</button>
                      <span className="text-xs text-gray-500 dark:text-gray-400 px-2">{keyPage + 1} / {keyTotalPages}</span>
                      <button onClick={() => setKeyPage(Math.min(keyTotalPages - 1, keyPage + 1))} disabled={keyPage >= keyTotalPages - 1}
                        className="px-2.5 py-1 text-xs rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 disabled:opacity-40">다음</button>
                    </div>
                  )}
                </div>
              </>
            )}
          </>
        )}
      </div>

      {/* Review Modal */}
      {isReviewModalOpen && selectedRequest && (() => {
        const requestedApis = selectedRequest.requestedApiIds
          .map((id) => allApisMap.apiById[id])
          .filter(Boolean);
        const apiCountByService: Record<string, number> = {};
        requestedApis.forEach((api) => {
          apiCountByService[api.serviceName] = (apiCountByService[api.serviceName] || 0) + 1;
        });
        const totalApiCount = selectedRequest.requestedApiIds.length;

        const usageDays = adjustedFromDate && adjustedToDate
          ? Math.ceil((new Date(adjustedToDate).getTime() - new Date(adjustedFromDate).getTime()) / (1000 * 60 * 60 * 24))
          : null;

        const riskWarnings: string[] = [];
        if (selectedRequest.dailyRequestEstimate != null && selectedRequest.dailyRequestEstimate > 1000) {
          riskWarnings.push(`일 ${Number(selectedRequest.dailyRequestEstimate).toLocaleString()}건 요청`);
        }
        if (totalApiCount > 15) {
          riskWarnings.push(`${totalApiCount}개 API 요청`);
        }
        if (usageDays !== null && usageDays > 180) {
          riskWarnings.push(`${usageDays}일 사용 기간`);
        }

        // Computed: original API IDs and change tracking
        const originalApiIds = new Set(selectedRequest.requestedApiIds);
        const allRelevantIds = new Set([...originalApiIds, ...adjustedApiIds]);
        const allRelevantApis = Array.from(allRelevantIds)
          .map((id) => allApisMap.apiById[id])
          .filter(Boolean);

        // Group by service > domain for the editable tree
        const reviewTreeByService: Record<number, { serviceName: string; domains: Record<string, { apiDomain: string; apis: (typeof allRelevantApis) }> }> = {};
        allRelevantApis.forEach((api) => {
          const sId = api.serviceId;
          if (!reviewTreeByService[sId]) {
            reviewTreeByService[sId] = { serviceName: api.serviceName, domains: {} };
          }
          const domain = api.apiDomain || '미분류';
          if (!reviewTreeByService[sId].domains[domain]) {
            reviewTreeByService[sId].domains[domain] = { apiDomain: domain, apis: [] };
          }
          reviewTreeByService[sId].domains[domain].apis.push(api);
        });

        const changes = {
          added: Array.from(adjustedApiIds).filter((id) => !originalApiIds.has(id)).length,
          removed: Array.from(originalApiIds).filter((id) => !adjustedApiIds.has(id)).length,
        };

        // Count APIs per service for summary (only adjusted/enabled ones)
        const adjustedApiCountByService: Record<string, number> = {};
        adjustedApiIds.forEach((id) => {
          const api = allApisMap.apiById[id];
          if (api) {
            adjustedApiCountByService[api.serviceName] = (adjustedApiCountByService[api.serviceName] || 0) + 1;
          }
        });

        const toggleReviewService = (serviceId: number) => {
          setExpandedReviewServices((prev) => {
            const next = new Set(prev);
            if (next.has(serviceId)) next.delete(serviceId);
            else next.add(serviceId);
            return next;
          });
        };

        const handleToggleReviewApi = (apiId: number) => {
          if (isReviewReadOnly) return;
          setAdjustedApiIds((prev) => {
            const next = new Set(prev);
            next.has(apiId) ? next.delete(apiId) : next.add(apiId);
            return next;
          });
        };

        const handleToggleReviewDomain = (apiIds: number[]) => {
          if (isReviewReadOnly) return;
          const allSelected = apiIds.every((id) => adjustedApiIds.has(id));
          setAdjustedApiIds((prev) => {
            const next = new Set(prev);
            apiIds.forEach((id) => allSelected ? next.delete(id) : next.add(id));
            return next;
          });
        };

        const handleToggleReviewService = (apiIds: number[]) => {
          if (isReviewReadOnly) return;
          const allSelected = apiIds.every((id) => adjustedApiIds.has(id));
          setAdjustedApiIds((prev) => {
            const next = new Set(prev);
            apiIds.forEach((id) => allSelected ? next.delete(id) : next.add(id));
            return next;
          });
        };

        const handleResetReviewApis = () => {
          if (isReviewReadOnly) return;
          if (selectedRequest) {
            setAdjustedApiIds(new Set(selectedRequest.requestedApiIds));
          }
        };

        const dateChanged = (
          adjustedFromDate !== (selectedRequest.usageFromDate?.split('T')[0] || '') ||
          adjustedToDate !== (selectedRequest.usageToDate?.split('T')[0] || '')
        );

        const handleResetDates = () => {
          setAdjustedFromDate(selectedRequest.usageFromDate ? selectedRequest.usageFromDate.split('T')[0] : '');
          setAdjustedToDate(selectedRequest.usageToDate ? selectedRequest.usageToDate.split('T')[0] : '');
        };

        const apiChanged = adjustedApiIds.size !== originalApiIds.size || Array.from(adjustedApiIds).some((id) => !originalApiIds.has(id));
        const hasChanges = apiChanged || dateChanged;

        return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
          <div className="bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-2xl w-full max-w-3xl max-h-[90vh] flex flex-col">
            {/* Header */}
            <div className="bg-gray-50 dark:bg-gray-800/80 border-b border-gray-200 dark:border-gray-700 rounded-t-2xl px-6 py-4">
              <div className="flex items-center justify-between mb-3">
                <div className="flex items-center gap-3">
                  <div className="w-9 h-9 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center">
                    <svg className="w-5 h-5 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>
                  </div>
                  <div>
                    <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{isReviewReadOnly ? '신청 상세' : 'API Key 신청 검토'}</h2>
                    <p className="text-xs text-gray-500 dark:text-gray-500">{formatDateTime(selectedRequest.createdAt)}</p>
                  </div>
                  <Badge variant={STATUS_VARIANT[selectedRequest.status] || 'default'}>
                    {selectedRequest.status === 'PENDING' ? '대기중' : selectedRequest.status === 'APPROVED' ? '승인' : '반려'}
                  </Badge>
                </div>
                <button onClick={handleCloseReview} className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 p-1 rounded-lg hover:bg-gray-200/50 dark:hover:bg-gray-700/50">
                  <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
                </button>
              </div>
              {/* Tab switcher */}
              <div className="flex gap-1">
                <button
                  onClick={() => setActiveReviewTab('info')}
                  className={`px-4 py-1.5 text-sm font-medium rounded-t-lg transition-colors ${
                    activeReviewTab === 'info'
                      ? 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-b-2 border-blue-500'
                      : 'text-gray-500 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
                  }`}
                >
                  신청 정보
                </button>
                <button
                  onClick={() => setActiveReviewTab('apis')}
                  className={`px-4 py-1.5 text-sm font-medium rounded-t-lg transition-colors ${
                    activeReviewTab === 'apis'
                      ? 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-b-2 border-blue-500'
                      : 'text-gray-500 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
                  }`}
                >
                  API 권한 ({adjustedApiIds.size})
                </button>
              </div>
            </div>

            {/* Body */}
            <div className="flex-1 overflow-y-auto px-6 py-4 space-y-4">
              {error && (
                <div className="p-3 bg-red-50 dark:bg-red-900/10 text-red-700 dark:text-red-400 rounded-lg text-sm border border-red-200 dark:border-red-800/30">{error}</div>
              )}

              {/* Tab: 신청 정보 */}
              {activeReviewTab === 'info' && (
                <>
                  {/* 2-column: 신청자 + Key Name */}
                  <div className="grid grid-cols-2 gap-3">
                    <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
                      <div className="flex items-center gap-2 mb-1.5">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">신청자</span>
                      </div>
                      <p className="text-sm font-semibold text-gray-900 dark:text-gray-100 pl-10">{selectedRequest.userName}</p>
                    </div>
                    <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
                      <div className="flex items-center gap-2 mb-1.5">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">Key Name</span>
                      </div>
                      <p className="text-sm font-semibold text-gray-900 dark:text-gray-100 pl-10">{selectedRequest.keyName}</p>
                    </div>
                  </div>

                  {/* 목적 */}
                  <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
                    <div className="flex items-center gap-2 mb-1.5">
                      <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                        <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
                      </div>
                      <span className="text-xs text-gray-500 dark:text-gray-500">목적</span>
                    </div>
                    <p className="text-sm text-gray-900 dark:text-gray-100 pl-10">{selectedRequest.purpose || '-'}</p>
                  </div>

                  {/* 2-column: 서비스 IP, 예상 요청량 */}
                  <div className="grid grid-cols-2 gap-3">
                    <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
                      <div className="flex items-center gap-2 mb-1.5">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9" /></svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">서비스 IP</span>
                      </div>
                      <p className="text-sm font-semibold text-gray-900 dark:text-gray-100 pl-10 font-mono">{selectedRequest.serviceIp || '-'}</p>
                      {selectedRequest.servicePurpose && (
                        <p className="text-xs text-gray-500 dark:text-gray-500 pl-10 mt-0.5">{selectedRequest.servicePurpose}</p>
                      )}
                    </div>
                    <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
                      <div className="flex items-center gap-2 mb-1.5">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">예상 요청량</span>
                      </div>
                      {isReviewReadOnly ? (
                        <p className="text-sm font-semibold text-gray-900 dark:text-gray-100 pl-10">
                          {selectedRequest.dailyRequestEstimate != null
                            ? `${Number(selectedRequest.dailyRequestEstimate).toLocaleString()}건/일`
                            : '-'}
                        </p>
                      ) : (
                        <div className="pl-10">
                          <select
                            value={adjustedDailyLimit}
                            onChange={(e) => setAdjustedDailyLimit(e.target.value)}
                            className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-2 py-1 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
                          >
                            <option value="">선택하세요</option>
                            <option value="100">100 이하</option>
                            <option value="500">100~500</option>
                            <option value="1000">500~1,000</option>
                            <option value="5000">1,000~5,000</option>
                            <option value="10000">5,000~10,000</option>
                            <option value="50000">10,000 이상</option>
                          </select>
                        </div>
                      )}
                      <p className="text-xs text-gray-500 dark:text-gray-500 pl-10 mt-0.5">{totalApiCount}개 API</p>
                    </div>
                  </div>

                  {/* Editable date section */}
                  <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-4 mb-4">
                    <div className="flex items-center justify-between mb-3">
                      <div className="flex items-center gap-2">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                            <rect x="2" y="3" width="12" height="11" rx="2" strokeWidth="1.5"/>
                            <path d="M2 7h12M5 1v4M11 1v4" strokeWidth="1.5" strokeLinecap="round"/>
                          </svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">사용 기간</span>
                        {dateChanged && (
                          <span className="text-[10px] font-semibold px-1.5 py-0.5 rounded bg-amber-100 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-700/30">
                            수정됨
                          </span>
                        )}
                      </div>
                      {dateChanged && (
                        <button type="button" onClick={handleResetDates} className="text-xs text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300">
                          원래대로
                        </button>
                      )}
                    </div>
                    <div className="flex items-center gap-2">
                      <input type="date" value={adjustedFromDate} onChange={(e) => setAdjustedFromDate(e.target.value)}
                        disabled={isReviewReadOnly}
                        className={`flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none ${isReviewReadOnly ? 'opacity-60 cursor-not-allowed' : ''}`} />
                      <span className="text-gray-400 dark:text-gray-500">~</span>
                      <input type="date" value={adjustedToDate} onChange={(e) => setAdjustedToDate(e.target.value)}
                        disabled={isReviewReadOnly}
                        className={`flex-1 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none ${isReviewReadOnly ? 'opacity-60 cursor-not-allowed' : ''}`} />
                    </div>
                    {usageDays !== null && (
                      <div className="mt-2 text-xs text-gray-500 dark:text-gray-400">{usageDays}일</div>
                    )}
                  </div>

                  {/* API summary */}
                  <div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
                    <div className="flex items-center justify-between mb-3">
                      <div className="flex items-center gap-2">
                        <div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/30 flex items-center justify-center text-blue-500">
                          <svg className="w-4 h-4" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                            <path d="M8 1.5L2.5 4v4c0 3.5 2.5 5.5 5.5 6.5 3-1 5.5-3 5.5-6.5V4L8 1.5z" strokeWidth="1.5" strokeLinejoin="round"/>
                          </svg>
                        </div>
                        <span className="text-xs text-gray-500 dark:text-gray-500">요청 API 요약</span>
                        {(changes.added > 0 || changes.removed > 0) && (
                          <span className="text-[10px] font-semibold px-1.5 py-0.5 rounded bg-amber-100 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-700/30">
                            수정됨
                          </span>
                        )}
                      </div>
                      <button
                        onClick={() => setActiveReviewTab('apis')}
                        className="text-xs text-blue-600 dark:text-blue-400 hover:underline"
                      >
                        상세 보기 →
                      </button>
                    </div>
                    <div className="flex flex-wrap gap-1.5">
                      {allApisMap.services.map((svc) => {
                        const svcApis = allApisMap.apisByService[svc.serviceId] || [];
                        const svcApiIds = svcApis.map((a) => a.apiId);
                        const enabledCount = svcApiIds.filter((id) => adjustedApiIds.has(id)).length;
                        if (enabledCount === 0) return null;
                        return (
                          <span key={svc.serviceId} className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs bg-gray-100 dark:bg-gray-700/80 border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300">
                            {svc.serviceName}
                            <span className="font-bold text-blue-600 dark:text-blue-400">{enabledCount}</span>
                            <span className="text-gray-400 dark:text-gray-500">/{svcApiIds.length}</span>
                          </span>
                        );
                      })}
                      {adjustedApiIds.size === 0 && (
                        <span className="text-xs text-gray-400 dark:text-gray-500">선택된 API 없음</span>
                      )}
                      {(changes.added > 0 || changes.removed > 0) && (
                        <span className="inline-flex items-center gap-1 px-2 py-1 rounded-md text-[11px] font-medium bg-amber-50 dark:bg-amber-900/10 border border-amber-200 dark:border-amber-700/30 text-amber-600 dark:text-amber-400">
                          {changes.added > 0 && <span className="text-green-600 dark:text-green-400">+{changes.added}</span>}
                          {changes.added > 0 && changes.removed > 0 && <span className="text-gray-400">/</span>}
                          {changes.removed > 0 && <span className="text-red-600 dark:text-red-400">-{changes.removed}</span>}
                        </span>
                      )}
                    </div>
                  </div>
                </>
              )}

              {/* Tab: API 권한 (editable) */}
              {activeReviewTab === 'apis' && (
                <div className="space-y-3">
                  {/* Header bar */}
                  <div className="flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <span className="text-blue-500 dark:text-blue-400">
                        <svg className="w-4 h-4" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                          <path d="M8 1.5L2.5 4v4c0 3.5 2.5 5.5 5.5 6.5 3-1 5.5-3 5.5-6.5V4L8 1.5z" strokeWidth="1.5" strokeLinejoin="round"/>
                        </svg>
                      </span>
                      <span className="text-sm font-semibold text-gray-900 dark:text-gray-100">API 권한 관리</span>
                      <span className="text-xs font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-2 py-0.5 rounded-full">{adjustedApiIds.size}개 허용</span>
                    </div>
                    {!isReviewReadOnly && (
                      <div className="flex items-center gap-2">
                        {(changes.added > 0 || changes.removed > 0) && (
                          <button onClick={handleResetReviewApis} className="text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 flex items-center gap-1">
                            <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
                            원래대로
                          </button>
                        )}
                        <button
                          onClick={() => { setAddApiSearch(''); setAddApiSelected(new Set()); setShowAddApiModal(true); }}
                          className="text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 border border-blue-200 dark:border-blue-800/30 px-2.5 py-1 rounded-lg flex items-center gap-1 transition-colors"
                        >
                          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 14 14" stroke="currentColor"><path d="M7 3v8M3 7h8" strokeWidth="1.5" strokeLinecap="round"/></svg>
                          API 추가
                        </button>
                      </div>
                    )}
                  </div>

                  {/* Change summary banner */}
                  {(changes.added > 0 || changes.removed > 0) && (
                    <div className="bg-amber-50 dark:bg-amber-900/5 border border-amber-200 dark:border-amber-700/30 rounded-lg p-3 flex items-center gap-2">
                      <svg className="w-4 h-4 text-amber-500 dark:text-amber-400 shrink-0" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                        <path d="M8 1.5L1 13.5h14L8 1.5z" strokeWidth="1.3" strokeLinejoin="round"/>
                        <path d="M8 6v3M8 11.5v.5" strokeWidth="1.5" strokeLinecap="round"/>
                      </svg>
                      <p className="text-xs text-gray-600 dark:text-gray-400">
                        신청 원본 대비 변경:
                        {changes.added > 0 && <span className="font-semibold text-green-600 dark:text-green-400 ml-1">+{changes.added} 추가</span>}
                        {changes.added > 0 && changes.removed > 0 && <span className="text-gray-400 mx-1">/</span>}
                        {changes.removed > 0 && <span className="font-semibold text-red-600 dark:text-red-400">-{changes.removed} 제외</span>}
                      </p>
                    </div>
                  )}

                  {/* Service groups */}
                  {Object.entries(reviewTreeByService).map(([sIdStr, svc]) => {
                    const sId = Number(sIdStr);
                    const isExpanded = expandedReviewServices.has(sId);
                    const allSvcApiIds = Object.values(svc.domains).flatMap((d) => d.apis.map((a) => a.apiId));
                    const selectedSvcCount = allSvcApiIds.filter((id) => adjustedApiIds.has(id)).length;
                    const allSvcSelected = allSvcApiIds.length > 0 && selectedSvcCount === allSvcApiIds.length;
                    const someSvcSelected = selectedSvcCount > 0 && !allSvcSelected;
                    const hasSvcSelections = selectedSvcCount > 0;

                    return (
                      <div key={sId} className={`rounded-lg border overflow-hidden ${hasSvcSelections ? 'border-blue-200 dark:border-blue-700/50' : 'border-gray-200 dark:border-gray-700'}`}>
                        {/* Service header */}
                        <div className="bg-gray-50 dark:bg-gray-700/50 px-3 py-2.5 flex items-center gap-2">
                          <button onClick={() => toggleReviewService(sId)} className="p-0.5">
                            <svg className={`w-4 h-4 text-gray-400 transition-transform ${isExpanded ? 'rotate-90' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /></svg>
                          </button>
                          <label className="flex items-center cursor-pointer" onClick={(e) => e.stopPropagation()}>
                            <IndeterminateCheckbox
                              checked={allSvcSelected}
                              indeterminate={someSvcSelected}
                              onChange={() => handleToggleReviewService(allSvcApiIds)}
                              className="rounded"
                            />
                          </label>
                          <span className="text-sm font-medium text-gray-900 dark:text-gray-100 flex-1">{svc.serviceName}</span>
                          <span className="text-xs px-1.5 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400">{selectedSvcCount}/{allSvcApiIds.length}</span>
                        </div>

                        {/* Domains */}
                        {isExpanded && (
                          <div className="py-1">
                            {Object.values(svc.domains).map((domainGroup) => {
                              const domainApiIds = domainGroup.apis.map((a) => a.apiId);
                              const selectedDomainCount = domainApiIds.filter((id) => adjustedApiIds.has(id)).length;
                              const allDomainSelected = domainApiIds.length > 0 && selectedDomainCount === domainApiIds.length;
                              const someDomainSelected = selectedDomainCount > 0 && !allDomainSelected;

                              return (
                                <div key={domainGroup.apiDomain} className="mb-1 last:mb-0">
                                  {/* Domain row */}
                                  <div className="px-3 py-1.5 flex items-center gap-2">
                                    <div className="w-4" />
                                    <label className="flex items-center cursor-pointer" onClick={(e) => e.stopPropagation()}>
                                      <IndeterminateCheckbox
                                        checked={allDomainSelected}
                                        indeterminate={someDomainSelected}
                                        onChange={() => handleToggleReviewDomain(domainApiIds)}
                                        className="rounded"
                                      />
                                    </label>
                                    <span className="text-xs font-medium text-gray-600 dark:text-gray-400">{domainGroup.apiDomain}</span>
                                    <span className="text-[10px] text-gray-400 dark:text-gray-500">{selectedDomainCount}/{domainApiIds.length}</span>
                                  </div>

                                  {/* API rows */}
                                  <div className="ml-6">
                                    {domainGroup.apis.map((api) => {
                                      const isSelected = adjustedApiIds.has(api.apiId);
                                      const isAdded = isSelected && !originalApiIds.has(api.apiId);
                                      const isRemoved = !isSelected && originalApiIds.has(api.apiId);

                                      return (
                                        <div
                                          key={api.apiId}
                                          onClick={() => handleToggleReviewApi(api.apiId)}
                                          className={`flex items-center gap-2.5 px-3 py-2 border-b border-gray-100 dark:border-gray-700/30 last:border-b-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors ${isAdded ? 'bg-green-50/50 dark:bg-green-900/5' : ''} ${isRemoved ? 'opacity-50' : ''}`}
                                        >
                                          <input
                                            type="checkbox"
                                            checked={isSelected}
                                            onChange={() => handleToggleReviewApi(api.apiId)}
                                            onClick={(e) => e.stopPropagation()}
                                            className="rounded"
                                          />
                                          <span className={`inline-block px-2 py-0.5 rounded text-xs font-bold ${METHOD_BADGE_STYLE[api.apiMethod] || 'bg-gray-700 text-gray-300 border border-gray-600'}`}>
                                            {api.apiMethod}
                                          </span>
                                          <span className={`font-mono text-sm text-gray-700 dark:text-gray-300 ${isRemoved ? 'line-through' : ''}`}>{api.apiPath}</span>
                                          <span className="text-xs text-gray-500 dark:text-gray-400">{api.apiName}</span>
                                          {isAdded && (
                                            <span className="bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-400 text-[9px] font-bold px-1.5 py-0.5 rounded">추가</span>
                                          )}
                                          {isRemoved && (
                                            <span className="bg-red-100 dark:bg-red-900/20 text-red-700 dark:text-red-400 text-[9px] font-bold px-1.5 py-0.5 rounded">제외</span>
                                          )}
                                        </div>
                                      );
                                    })}
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        )}
                      </div>
                    );
                  })}
                  {Object.keys(reviewTreeByService).length === 0 && (
                    <div className="text-center py-8 text-gray-400 dark:text-gray-500 text-sm">요청된 API가 없습니다.</div>
                  )}

                  {/* Add API Modal (overlay inside the tab) */}
                  {showAddApiModal && (
                    <div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50">
                      <div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 shadow-2xl w-full max-w-xl mx-4 max-h-[80vh] flex flex-col">
                        <div className="px-5 py-3.5 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
                          <div className="flex items-center gap-2">
                            <svg className="w-4 h-4 text-blue-500" fill="none" viewBox="0 0 14 14" stroke="currentColor"><path d="M7 3v8M3 7h8" strokeWidth="1.5" strokeLinecap="round"/></svg>
                            <span className="text-sm font-semibold text-gray-900 dark:text-gray-100">API 추가</span>
                            {addApiSelected.size > 0 && (
                              <span className="text-xs font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-2 py-0.5 rounded-full">{addApiSelected.size}개 선택</span>
                            )}
                          </div>
                          <input
                            type="text"
                            value={addApiSearch}
                            onChange={(e) => setAddApiSearch(e.target.value)}
                            placeholder="API 검색..."
                            className="bg-gray-100 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg px-3 py-1.5 text-xs text-gray-900 dark:text-gray-100 placeholder-gray-400 w-44 focus:ring-2 focus:ring-blue-500 focus:outline-none"
                          />
                        </div>
                        <div className="flex-1 overflow-y-auto p-3 space-y-1">
                          {(() => {
                            const query = addApiSearch.toLowerCase();
                            const availableApis = allApisMap.services.flatMap((svc) => {
                              const apis = allApisMap.apisByService[svc.serviceId] || [];
                              return apis
                                .filter((a) => !adjustedApiIds.has(a.apiId))
                                .filter((a) => !query || a.apiName.toLowerCase().includes(query) || a.apiPath.toLowerCase().includes(query) || svc.serviceName.toLowerCase().includes(query))
                                .map((a) => ({ ...a, serviceName: svc.serviceName }));
                            });

                            if (availableApis.length === 0) {
                              return <div className="text-center py-8 text-gray-400 dark:text-gray-500 text-sm">{addApiSearch ? '검색 결과가 없습니다' : '추가 가능한 API가 없습니다'}</div>;
                            }

                            return availableApis.map((api) => (
                              <div
                                key={api.apiId}
                                onClick={() => setAddApiSelected((prev) => { const next = new Set(prev); next.has(api.apiId) ? next.delete(api.apiId) : next.add(api.apiId); return next; })}
                                className={`flex items-center gap-2.5 px-3 py-2 rounded-lg cursor-pointer transition-colors ${addApiSelected.has(api.apiId) ? 'bg-blue-50 dark:bg-blue-900/10' : 'hover:bg-gray-50 dark:hover:bg-gray-700/30'}`}
                              >
                                <input type="checkbox" checked={addApiSelected.has(api.apiId)} readOnly className="rounded" />
                                <span className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-bold ${METHOD_BADGE_STYLE[api.apiMethod] || 'bg-gray-700 text-gray-300'}`}>{api.apiMethod}</span>
                                <div className="flex-1 min-w-0">
                                  <div className="font-mono text-xs text-gray-600 dark:text-gray-400 truncate">{api.apiPath}</div>
                                  <div className="text-[11px] text-gray-400 dark:text-gray-500">{api.serviceName} · {api.apiName}</div>
                                </div>
                              </div>
                            ));
                          })()}
                        </div>
                        <div className="px-5 py-3 border-t border-gray-200 dark:border-gray-700 flex justify-end gap-2">
                          <button onClick={() => setShowAddApiModal(false)} className="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200">취소</button>
                          <button
                            disabled={addApiSelected.size === 0}
                            onClick={() => {
                              setAdjustedApiIds((prev) => { const next = new Set(prev); addApiSelected.forEach((id) => next.add(id)); return next; });
                              setShowAddApiModal(false);
                            }}
                            className="px-4 py-1.5 text-sm font-medium rounded-lg bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50 disabled:cursor-not-allowed"
                          >
                            {addApiSelected.size > 0 ? `${addApiSelected.size}개 API 추가` : 'API 추가'}
                          </button>
                        </div>
                      </div>
                    </div>
                  )}
                </div>
              )}
            </div>

            {/* Footer */}
            <div className="border-t border-gray-200 dark:border-gray-700 px-6 py-4 space-y-3 rounded-b-2xl">
              {/* Reject confirmation panel */}
              {showRejectConfirm && (
                <div className="bg-red-50 dark:bg-red-900/5 border border-red-200 dark:border-red-800/30 rounded-xl p-4 space-y-3">
                  <div className="flex items-center gap-2 text-red-600 dark:text-red-400">
                    <svg className="w-4 h-4" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                      <path d="M8 1.5L1 13.5h14L8 1.5z" strokeWidth="1.3" strokeLinejoin="round"/>
                      <path d="M8 6v3M8 11.5v.5" strokeWidth="1.5" strokeLinecap="round"/>
                    </svg>
                    <span className="text-sm font-semibold">반려 확인</span>
                  </div>
                  <div>
                    <label className="text-xs text-red-600 dark:text-red-400 mb-1 block">반려 사유 (필수)</label>
                    <textarea
                      value={rejectReason}
                      onChange={(e) => setRejectReason(e.target.value)}
                      rows={2}
                      placeholder="반려 사유를 입력하세요"
                      className="w-full border border-red-300 dark:border-red-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-red-500 focus:outline-none"
                    />
                  </div>
                  <div className="flex justify-end gap-2">
                    <button
                      onClick={() => setShowRejectConfirm(false)}
                      className="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200"
                    >
                      취소
                    </button>
                    <button
                      onClick={() => {
                        if (!rejectReason.trim()) return;
                        handleReviewSubmit('REJECTED');
                      }}
                      disabled={!rejectReason.trim()}
                      className="px-4 py-1.5 text-sm font-medium rounded-lg bg-red-600 hover:bg-red-700 text-white disabled:opacity-50 disabled:cursor-not-allowed"
                    >
                      반려 확정
                    </button>
                  </div>
                </div>
              )}

              {/* Approve confirmation panel */}
              {showApproveConfirm && (
                <div className="bg-green-50 dark:bg-green-900/5 border border-green-200 dark:border-green-800/30 rounded-xl p-4 space-y-3">
                  <div className="flex items-center gap-2 text-green-600 dark:text-green-400">
                    <svg className="w-4 h-4" fill="none" viewBox="0 0 16 16" stroke="currentColor">
                      <circle cx="8" cy="8" r="6.5" strokeWidth="1.5"/>
                      <path d="M5.5 8l2 2 3.5-4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                    <span className="text-sm font-semibold">승인 확인</span>
                  </div>
                  <p className="text-sm text-gray-600 dark:text-gray-400">
                    <span className="font-semibold text-gray-900 dark:text-gray-100">{selectedRequest.userName}</span>에게
                    <span className="font-semibold text-blue-600 dark:text-blue-400"> {adjustedApiIds.size}개 API</span> 접근 권한이 포함된 API Key를 발급합니다.
                  </p>

                  {hasChanges ? (
                    <div>
                      <div className="flex items-center gap-1.5 mb-2">
                        <svg className="w-3.5 h-3.5 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z" /></svg>
                        <span className="text-xs font-semibold text-amber-600 dark:text-amber-400">관리자 수정 내역</span>
                      </div>
                      <div className="border border-amber-200 dark:border-amber-700/30 rounded-lg overflow-hidden mb-3">
                        <table className="w-full">
                          <thead>
                            <tr className="bg-amber-50/50 dark:bg-amber-900/10">
                              <th className="text-[11px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 px-3 py-2 text-left">항목</th>
                              <th className="text-[11px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 px-3 py-2 text-left">신청 원본</th>
                              <th className="text-[11px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 px-3 py-2 text-center w-8"></th>
                              <th className="text-[11px] font-semibold uppercase tracking-wider text-amber-600 dark:text-amber-400 px-3 py-2 text-left">승인 내용</th>
                            </tr>
                          </thead>
                          <tbody>
                            {apiChanged && (
                              <tr className="bg-amber-50/30 dark:bg-amber-900/5">
                                <td className="px-3 py-2 text-sm text-gray-700 dark:text-gray-300">API 권한</td>
                                <td className="px-3 py-2 text-sm text-gray-500 dark:text-gray-400">{originalApiIds.size}개 API</td>
                                <td className="text-center text-gray-400 w-8">&rarr;</td>
                                <td className="px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-medium">
                                  {adjustedApiIds.size}개 API
                                  {changes.added > 0 && <span className="ml-1.5 bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-400 text-[10px] font-semibold px-1.5 rounded">+{changes.added}</span>}
                                  {changes.removed > 0 && <span className="ml-1 bg-red-100 dark:bg-red-900/20 text-red-700 dark:text-red-400 text-[10px] font-semibold px-1.5 rounded">-{changes.removed}</span>}
                                </td>
                              </tr>
                            )}
                            {dateChanged && (
                              <tr className="bg-amber-50/30 dark:bg-amber-900/5">
                                <td className="px-3 py-2 text-sm text-gray-700 dark:text-gray-300">사용 기간</td>
                                <td className="px-3 py-2 text-sm text-gray-500 dark:text-gray-400 line-through">
                                  {selectedRequest.usageFromDate?.split('T')[0] || '-'} ~ {selectedRequest.usageToDate?.split('T')[0] || '-'}
                                </td>
                                <td className="text-center text-gray-400 w-8">&rarr;</td>
                                <td className="px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-medium">
                                  {adjustedFromDate || '-'} ~ {adjustedToDate || '-'}
                                </td>
                              </tr>
                            )}
                          </tbody>
                        </table>
                      </div>
                    </div>
                  ) : (
                    <div className="bg-green-50 dark:bg-green-900/5 border border-green-200 dark:border-green-700/30 rounded-lg px-3 py-2">
                      <p className="text-xs text-green-700 dark:text-green-400 font-medium">신청 원본 그대로 승인합니다.</p>
                    </div>
                  )}

                  <div className="flex justify-end gap-2">
                    <button
                      onClick={() => setShowApproveConfirm(false)}
                      className="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200"
                    >
                      돌아가기
                    </button>
                    <button
                      onClick={() => handleReviewSubmit('APPROVED')}
                      className="px-4 py-1.5 text-sm font-medium rounded-lg bg-gradient-to-r from-emerald-600 to-emerald-700 hover:from-emerald-700 hover:to-emerald-800 text-white"
                    >
                      승인 확정
                    </button>
                  </div>
                </div>
              )}

              {/* Read-only mode: show review result + close button */}
              {isReviewReadOnly && (
                <>
                  {(selectedRequest.reviewerName || selectedRequest.reviewComment) && (
                    <div className={`rounded-lg border p-3 mb-3 ${selectedRequest.status === 'APPROVED' ? 'bg-green-50 dark:bg-green-900/5 border-green-200 dark:border-green-700/30' : 'bg-red-50 dark:bg-red-900/5 border-red-200 dark:border-red-700/30'}`}>
                      <div className={`flex items-center gap-2 text-xs font-semibold mb-1 ${selectedRequest.status === 'APPROVED' ? 'text-green-700 dark:text-green-400' : 'text-red-700 dark:text-red-400'}`}>
                        {selectedRequest.status === 'APPROVED' ? (
                          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 16 16" stroke="currentColor"><circle cx="8" cy="8" r="6.5" strokeWidth="1.5"/><path d="M5.5 8l2 2 3.5-4" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
                        ) : (
                          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 16 16" stroke="currentColor"><path d="M8 1.5L1 13.5h14L8 1.5z" strokeWidth="1.3" strokeLinejoin="round"/><path d="M8 6v3M8 11.5v.5" strokeWidth="1.5" strokeLinecap="round"/></svg>
                        )}
                        {selectedRequest.status === 'APPROVED' ? '승인' : '반려'} - {selectedRequest.reviewerName || '관리자'}
                      </div>
                      {selectedRequest.reviewedAt && (
                        <div className="text-xs text-gray-500 dark:text-gray-500 mb-1">{formatDateTime(selectedRequest.reviewedAt)}</div>
                      )}
                      {selectedRequest.reviewComment && (
                        <div className="text-sm text-gray-700 dark:text-gray-300">{selectedRequest.reviewComment}</div>
                      )}
                    </div>
                  )}
                  <div className="flex justify-end">
                    <button
                      onClick={handleCloseReview}
                      className="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-lg text-sm font-medium"
                    >
                      닫기
                    </button>
                  </div>
                </>
              )}

              {/* Edit mode: action buttons only */}
              {!isReviewReadOnly && !showRejectConfirm && !showApproveConfirm && (
                <>
                  <div className="flex justify-end gap-2">
                    <button
                      type="button"
                      onClick={handleCloseReview}
                      className="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-lg text-sm font-medium"
                    >
                      취소
                    </button>
                    <button
                      onClick={() => setShowRejectConfirm(true)}
                      className="border border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/10 text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/20 px-4 py-2 rounded-lg text-sm font-medium"
                    >
                      반려
                    </button>
                    <button
                      onClick={() => setShowApproveConfirm(true)}
                      className="bg-gradient-to-r from-emerald-600 to-emerald-700 hover:from-emerald-700 hover:to-emerald-800 text-white px-4 py-2 rounded-lg text-sm font-medium"
                    >
                      승인
                    </button>
                  </div>
                </>
              )}
            </div>
          </div>
        </div>
        );
      })()}

      {/* Detail Modal */}
      {isDetailModalOpen && selectedKeyDetail && (() => {
        const statusConf = KEY_STATUS_CONFIG[selectedKeyDetail.status] || KEY_STATUS_CONFIG.EXPIRED;
        const isDisabled = selectedKeyDetail.status === 'REVOKED' || selectedKeyDetail.status === 'EXPIRED';
        return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
          <div className="bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-2xl w-full max-w-lg mx-4">
            {/* Header */}
            <div className="bg-gray-50 dark:bg-gray-800/80 border-b border-gray-200 dark:border-gray-700 px-6 py-5 rounded-t-2xl flex items-center justify-between">
              <div className="flex items-center gap-3">
                <div className={`w-10 h-10 rounded-xl border ${statusConf.iconBorderClass} ${statusConf.iconBgClass} flex items-center justify-center`}>
                  <svg className={`w-5 h-5 ${statusConf.textClass}`} fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" />
                  </svg>
                </div>
                <div>
                  <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">키 상세 정보</h2>
                  <p className="text-xs text-gray-500 dark:text-gray-400">API Key 발급 정보 및 상태</p>
                </div>
              </div>
              <button
                onClick={() => { setIsDetailModalOpen(false); setSelectedKeyDetail(null); setKeyVisible(false); }}
                className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
              >
                <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                </svg>
              </button>
            </div>

            {/* Status Banner */}
            <div className="px-6 pt-5">
              <div className={`bg-gray-50 dark:bg-gray-800/60 border ${statusConf.borderClass} rounded-lg px-4 py-3 flex items-center justify-between`}>
                <span className="font-mono font-bold text-sm text-gray-900 dark:text-gray-100">{selectedKeyDetail.keyName}</span>
                <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold border ${statusConf.bgClass} ${statusConf.borderClass} ${statusConf.textClass}`}>
                  <span className={`w-1.5 h-1.5 rounded-full ${statusConf.dotClass}`} />
                  {statusConf.label}
                </span>
              </div>
            </div>

            {/* Body - Info Rows */}
            <div className="px-6 pt-4 pb-2">
              {/* USER */}
              <div className="flex items-center gap-3 py-3 border-b border-gray-100 dark:border-gray-700/30">
                <div className="w-7 h-7 rounded-md bg-blue-50 dark:bg-blue-900/15 flex items-center justify-center text-blue-500 shrink-0">
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
                  </svg>
                </div>
                <div className="min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500">USER</div>
                  <div className="text-sm font-medium text-gray-900 dark:text-gray-100">{selectedKeyDetail.userName}</div>
                </div>
              </div>
              {/* PREFIX */}
              <div className="flex items-center gap-3 py-3 border-b border-gray-100 dark:border-gray-700/30">
                <div className="w-7 h-7 rounded-md bg-blue-50 dark:bg-blue-900/15 flex items-center justify-center text-blue-500 shrink-0">
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z" />
                    <path strokeLinecap="round" strokeLinejoin="round" d="M6 6h.008v.008H6V6Z" />
                  </svg>
                </div>
                <div className="min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500">PREFIX</div>
                  <div className="text-sm font-medium font-mono text-gray-900 dark:text-gray-100">{selectedKeyDetail.apiKeyPrefix}</div>
                </div>
              </div>
              {/* EXPIRES AT */}
              <div className="flex items-center gap-3 py-3 border-b border-gray-100 dark:border-gray-700/30">
                <div className="w-7 h-7 rounded-md bg-blue-50 dark:bg-blue-900/15 flex items-center justify-center text-blue-500 shrink-0">
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />
                  </svg>
                </div>
                <div className="min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500">EXPIRES AT</div>
                  <div className="text-sm font-medium text-gray-900 dark:text-gray-100">{selectedKeyDetail.expiresAt ? formatDateTime(selectedKeyDetail.expiresAt) : '영구'}</div>
                </div>
              </div>
              {/* LAST USED */}
              <div className="flex items-center gap-3 py-3 border-b border-gray-100 dark:border-gray-700/30">
                <div className="w-7 h-7 rounded-md bg-blue-50 dark:bg-blue-900/15 flex items-center justify-center text-blue-500 shrink-0">
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
                  </svg>
                </div>
                <div className="min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500">LAST USED</div>
                  <div className="text-sm font-medium text-gray-900 dark:text-gray-100">{selectedKeyDetail.lastUsedAt ? formatDateTime(selectedKeyDetail.lastUsedAt) : '-'}</div>
                </div>
              </div>
              {/* CREATED AT */}
              <div className="flex items-center gap-3 py-3 border-b border-gray-100 dark:border-gray-700/30">
                <div className="w-7 h-7 rounded-md bg-blue-50 dark:bg-blue-900/15 flex items-center justify-center text-blue-500 shrink-0">
                  <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />
                  </svg>
                </div>
                <div className="min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500">CREATED AT</div>
                  <div className="text-sm font-medium text-gray-900 dark:text-gray-100">{formatDateTime(selectedKeyDetail.createdAt)}</div>
                </div>
              </div>
            </div>

            {/* Body - API Key Section */}
            <div className="px-6 pt-3 pb-4">
              <div className="flex items-center justify-between mb-2">
                <span className="text-[10px] uppercase tracking-wider text-gray-400 dark:text-gray-500 font-semibold">API KEY</span>
                <button
                  onClick={() => setKeyVisible((v) => !v)}
                  className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors p-1"
                  title={keyVisible ? '숨기기' : '보기'}
                >
                  {keyVisible ? (
                    <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" />
                    </svg>
                  ) : (
                    <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
                      <path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
                    </svg>
                  )}
                </button>
              </div>
              <div className="flex items-center gap-2">
                <div className="flex-1 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2.5 font-mono text-sm break-all text-gray-900 dark:text-gray-100 select-all">
                  {keyVisible ? selectedKeyDetail.decryptedKey : `${selectedKeyDetail.apiKeyPrefix}${'••••••••••••••••••••'}`}
                </div>
                <button
                  onClick={handleCopyDecryptedKey}
                  className={`shrink-0 px-3 py-1.5 rounded-md text-xs font-semibold transition-colors ${
                    detailCopied
                      ? 'bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400'
                      : 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/30'
                  }`}
                >
                  {detailCopied ? (
                    <span className="flex items-center gap-1">
                      <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
                        <path strokeLinecap="round" strokeLinejoin="round" d="m4.5 12.75 6 6 9-13.5" />
                      </svg>
                      완료
                    </span>
                  ) : '복사'}
                </button>
              </div>
              {isDisabled && (
                <div className={`mt-3 rounded-md px-3 py-2 text-xs border ${statusConf.bgClass} ${statusConf.borderClass} ${statusConf.textClass}`}>
                  이 키는 {selectedKeyDetail.status === 'REVOKED' ? '폐기' : '만료'}되어 더 이상 사용할 수 없습니다.
                </div>
              )}
            </div>

            {/* Footer */}
            <div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end">
              <button
                onClick={() => { setIsDetailModalOpen(false); setSelectedKeyDetail(null); setKeyVisible(false); }}
                className="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
              >
                닫기
              </button>
            </div>
          </div>
        </div>
        );
      })()}

      {/* Raw Key Modal (after approve) */}
      {rawKeyModal && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
          <div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-lg mx-4">
            <div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
              <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">API Key 생성 완료</h2>
            </div>
            <div className="px-6 py-4 space-y-4">
              <div className="p-3 bg-yellow-50 border border-yellow-200 text-yellow-800 rounded-lg text-sm">
                이 키는 다시 표시되지 않습니다. 안전한 곳에 보관하세요.
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Key Name</label>
                <p className="text-gray-900 dark:text-gray-100">{rawKeyModal.keyName}</p>
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">API Key</label>
                <div className="flex items-center gap-2">
                  <code className="flex-1 bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded-lg text-sm font-mono break-all text-gray-900 dark:text-gray-100">
                    {rawKeyModal.rawKey}
                  </code>
                  <button
                    onClick={handleCopyRawKey}
                    className="shrink-0 bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded-lg text-sm font-medium"
                  >
                    {rawKeyCopied ? '복사됨!' : '복사'}
                  </button>
                </div>
              </div>
            </div>
            <div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end">
              <button
                onClick={() => {
                  setRawKeyModal(null);
                  setRawKeyCopied(false);
                }}
                className="bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-500 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-lg text-sm font-medium"
              >
                닫기
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default KeyAdminPage;
