<script setup lang="ts">
import { ref, onMounted, onActivated, computed, watch, reactive, h, provide } from 'vue';

defineOptions({ name: 'AdminAgentManager' });
import { useRoute, useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
import { resolveFormError, resolveApiError } from '../i18n/form-validation';
import api from '../api';
import { usePermissions } from '../composables/usePermissions';
import { AdminPerm } from '../constants/permissions';

const { t, localeTag } = useAdminLocale();
const route = useRoute();
const router = useRouter();
const { hasPermission, role: staffRole } = usePermissions();

const canViewAgents = computed(() => hasPermission(AdminPerm.agentsView));
const canViewUsers = computed(() => hasPermission(AdminPerm.usersView));
const canCreateUsers = computed(() => hasPermission(AdminPerm.usersCreate));
const canCreateAgents = computed(() => hasPermission(AdminPerm.agentsCreate));
const canResetPassword = computed(() => hasPermission(AdminPerm.usersResetPassword));
const canDeposit = computed(() => hasPermission(AdminPerm.walletDeposit));
const canWithdraw = computed(() => hasPermission(AdminPerm.walletWithdraw));
const canAdjustCredit = computed(() => hasPermission(AdminPerm.agentsCredit));
const canManageSettings = computed(() => hasPermission(AdminPerm.settings));
const canEditPlayerProfile = computed(() => canCreateUsers.value);
const playerActionsReadOnly = computed(
  () => canViewUsers.value && !canCreateUsers.value && !canDeposit.value && !canWithdraw.value,
);
const playerActionFlags = computed(() => ({
  showEdit: canViewUsers.value && (canEditPlayerProfile.value || canResetPassword.value),
  showDeposit: canDeposit.value,
  showWithdraw: canWithdraw.value,
  showFreeze: canEditPlayerProfile.value,
  showDelete: canEditPlayerProfile.value,
}));

import {
  emptyPlayerCreateForm,
  emptyPlayerEditForm,
  editFormFromDetail,
  buildCreatePlayerPayload,
  type PlayerRow,
  type PlayerDetail,
  type PlayerCreateForm,
  type PlayerEditForm,
  formatPlayerAffiliationLabel,
  assertPlayerUsername,
} from './user-form';
import {
  emptyAgentEditForm,
  editFormFromAgentDetail,
  type AgentRow,
  type AgentDetail,
  type AgentEditForm,
} from './agent-form';
import { subAgentAccountStatus } from './agent/agent-sub-agent-form';

type DisplayAgentRow = AgentRow;
import {
  formatAmount,
  formatAmountFull,
  shouldCompactAmount as shouldCompact,
} from '../utils/format-amount';
import { formatAgentLevelNumeral } from '../utils/agent-level-label';
import {
  agentDirectPlayersReloadKey,
  agentPlayerActionsKey,
} from '../composables/agent-direct-players-context';
import AdminTableEmpty from '../components/AdminTableEmpty.vue';
import PlayerWalletLedgerDialog from '../components/PlayerWalletLedgerDialog.vue';
import WalletTransferContext from '../components/WalletTransferContext.vue';
import InitialDepositRemarkField from '../components/InitialDepositRemarkField.vue';
import AgentCreditContext from '../components/AgentCreditContext.vue';
import RatePercentInput from '../components/RatePercentInput.vue';
import { formatRatePercent, percentToDecimalRate, decimalRateToPercent } from '../utils/rate-percent';
import InviteCodePanel from '../components/InviteCodePanel.vue';
import InviteManageDialog from '../components/InviteManageDialog.vue';
import AdminTableWrap from '../components/AdminTableWrap.vue';
import AdminPlayerStatusCell from '../components/AdminPlayerStatusCell.vue';
import AdminAgentRowActions from '../components/AdminAgentRowActions.vue';
import AdminPlayerRowActions from '../components/AdminPlayerRowActions.vue';
import AdminDetailGrid from '../components/AdminDetailGrid.vue';
import AdminDetailItem from '../components/AdminDetailItem.vue';
import { useAdminPlayerTransfer } from '../composables/useAdminPlayerTransfer';
import {
  fetchAdminAgentCreditContext,
  maxCreditIncreaseAmount,
  type AgentCreditAdjustContext,
} from '../utils/agent-credit-context';

const inviteDialogOpen = ref(false);

/* ─── Tier-1 agent list ─── */
const tier1Agents = ref<AgentRow[]>([]);
const tier1Total = ref(0);
const tier1Page = ref(1);
const tier1PageSize = ref(10);
const tier1Keyword = ref('');
const tier1FilterStatus = ref('');

/* ─── Sub-agent lists by level (L2, L3, …) ─── */
type SubAgentLevelState = {
  agents: AgentRow[];
  total: number;
  page: number;
  pageSize: number;
  keyword: string;
  filterStatus: string;
};

const subAgentLevelState = reactive<Record<number, SubAgentLevelState>>({});
const agentLevelCounts = ref<Record<number, number>>({});
const hierarchySettings = ref({ maxAgentLevel: 0, defaultSubAgentCreditRatio: 50 });
const agentSuspendDefaults = ref({
  suspendFreezeDirectPlayers: false,
  suspendBlockPlayerLogin: false,
});

function ensureSubAgentState(level: number): SubAgentLevelState {
  if (!subAgentLevelState[level]) {
    subAgentLevelState[level] = {
      agents: [],
      total: 0,
      page: 1,
      pageSize: 10,
      keyword: '',
      filterStatus: '',
    };
  }
  return subAgentLevelState[level];
}

function agentLevelTabName(level: number) {
  return `agentLevel-${level}`;
}

function lvlLabel(level: number) {
  return formatAgentLevelNumeral(level, localeTag.value);
}

function agentTierName(level: number) {
  return t('agent.level_name', { level: lvlLabel(level) });
}

function agentLevelTabLabel(level: number) {
  const count = agentLevelCounts.value[level] ?? 0;
  return `${agentTierName(level)} (${count})`;
}

const visibleSubAgentTabLevels = computed(() => {
  const counts = agentLevelCounts.value;
  const max = hierarchySettings.value.maxAgentLevel;
  const levels = new Set<number>([2]);
  if (max === 0) {
    for (const [lvl, cnt] of Object.entries(counts)) {
      const n = Number(lvl);
      if (n >= 3 && cnt > 0) levels.add(n);
    }
  } else {
    for (let n = 3; n <= max; n++) {
      if ((counts[n] ?? 0) > 0) levels.add(n);
    }
  }
  return [...levels].sort((a, b) => a - b);
});

/* ─── View tab: players | tier1Agents | agentLevel-N ─── */
const activeViewTab = ref('players');

/* ─── All players list ─── */
const allPlayers = ref<PlayerRow[]>([]);
const playerTotal = ref(0);
const playerPage = ref(1);
const playerPageSize = ref(10);
const playerKeyword = ref('');
const playerFilterStatus = ref('');
const playerFilterAgent = ref('');
const playerLoading = ref(false);
const agentOptions = ref<{ id: string; username: string; level: number; parentUsername?: string | null }[]>([]);

const directPlayersReload = ref<(() => void) | null>(null);
provide(agentDirectPlayersReloadKey, directPlayersReload);

const isAgentChildRoute = computed(() =>
  /^\/users\/agents\/[^/]+\/players/.test(route.path) || route.path === '/users/settings',
);

const createToolbarChildLevel = ref<number | null>(null);

/* ─── Dialogs ─── */
const createVisible = ref(false);
const editPlayerVisible = ref(false);
const editAgentVisible = ref(false);
const detailPlayerVisible = ref(false);
const detailAgentVisible = ref(false);
const creditVisible = ref(false);
const createLoading = ref(false);
const editPlayerLoading = ref(false);
const editAgentLoading = ref(false);
const creditLoading = ref(false);

/* ─── Create form (unified) ─── */
const createForm = ref<PlayerCreateForm>(emptyPlayerCreateForm());
const createParentAgentId = ref('');
const createParentLocked = ref(false);
const createAccountMode = ref(0); // 0=player, 1=tier1Agent, 2=subAgent

/* ─── Edit forms ─── */
const editPlayerForm = ref<PlayerEditForm>(emptyPlayerEditForm());
const editAgentForm = ref<AgentEditForm>(emptyAgentEditForm());
const editingId = ref('');

/* ─── Detail ─── */
const playerDetail = ref<PlayerDetail | null>(null);
const agentDetail = ref<AgentDetail | null>(null);

/* ─── Credit ─── */
const creditForm = ref({ amount: 10000, remark: '' });
const creditContext = ref<AgentCreditAdjustContext | null>(null);
const creditContextLoading = ref(false);

/* ─── Init ─── */
let pageInitPromise: Promise<void> | null = null;
const pageInitLoaded = ref(false);
const DEFAULT_SUB_AGENT_CREDIT_RATIO = computed(
  () => hierarchySettings.value.defaultSubAgentCreditRatio || 50,
);
const freezeAgentVisible = ref(false);
const freezeAgentLoading = ref(false);
const freezeAgentTarget = ref<AgentRow | null>(null);
const freezeAgentForm = ref({
  freezeDirectPlayers: false,
  blockDirectPlayerLogin: false,
  unfreezeDirectPlayers: false,
});
const agentOptionsLoading = ref(false);

const createDialogTitle = computed(() => {
  if (createAccountMode.value === 1) return t('agent.dialog.create');
  if (createAccountMode.value === 2) {
    const lvl = createTargetAgentLevel.value;
    if (lvl === 2) return t('agent_portal.create_sub_agent_dialog');
    if (lvl != null) return t('agent.dialog.create_level_agent', { level: lvlLabel(lvl) });
    return t('agent.dialog.create_child_agent');
  }
  return t('user.dialog.create');
});

const createSubAgentLevelPreview = computed(() => {
  if (createAccountMode.value !== 2 || !createParentAgentId.value) return null;
  const parentLevel = resolveParentAgentLevel(createParentAgentId.value);
  return parentLevel != null ? parentLevel + 1 : null;
});

const createTargetAgentLevel = computed(() => {
  if (createAccountMode.value !== 2) return null;
  if (createToolbarChildLevel.value != null) return createToolbarChildLevel.value;
  return createSubAgentLevelPreview.value;
});

function resolveParentAgentLevel(agentId: string): number | null {
  const opt = agentOptions.value.find((a) => a.id === agentId);
  if (opt) return opt.level;
  const row = findAgentRowByUserId(agentId);
  if (row) return row.level;
  return null;
}

function parentOptionsForChildLevel(childLevel: number) {
  if (childLevel < 2) return [];
  const requiredParentLevel = childLevel - 1;
  return parentAgentOptionsForCreate.value.filter((a) => a.level === requiredParentLevel);
}

function childAgentActionLabel(parentLevel: number) {
  return t('agent.create_level_agent', { level: lvlLabel(parentLevel + 1) });
}

const createParentCreditCache = ref<Record<string, string>>({});

function findAgentRowByUserId(userId: string): AgentRow | undefined {
  const tier1 = tier1Agents.value.find((a) => a.userId === userId);
  if (tier1) return tier1;
  for (const lvl of visibleSubAgentTabLevels.value) {
    const hit = subAgentLevelState[lvl]?.agents.find((a) => a.userId === userId);
    if (hit) return hit;
  }
  return undefined;
}

async function ensureCreateParentCredit(agentId: string) {
  const hit = findAgentRowByUserId(agentId);
  if (hit) {
    createParentCreditCache.value[agentId] = hit.availableCredit;
    return;
  }
  if (createParentCreditCache.value[agentId]) return;
  try {
    const { data } = await api.get(`/admin/agents/${agentId}`);
    createParentCreditCache.value[agentId] = String(data.data.availableCredit ?? '0');
  } catch {
    createParentCreditCache.value[agentId] = '0';
  }
}

const createParentAvailableCredit = computed(() => {
  if (createAccountMode.value !== 2 || !createParentAgentId.value) return null;
  const id = createParentAgentId.value;
  const hit = findAgentRowByUserId(id);
  if (hit) return hit.availableCredit;
  return createParentCreditCache.value[id] ?? null;
});

const createSubCreditMax = computed(() => {
  const n = Number(createParentAvailableCredit.value ?? 0);
  return Number.isFinite(n) ? Math.max(0, n) : 0;
});

function computeSubAgentCreditByRatio(available: number, ratioPercent: number): number {
  if (available <= 0) return 0;
  const pct = Math.min(100, Math.max(1, ratioPercent)) / 100;
  const raw = available * pct;
  const rounded = pct >= 1 ? available : Math.floor(raw / 100) * 100;
  return Math.min(available, Math.max(0, rounded));
}

const creditQuickRatios = [10, 15, 20, 30] as const;

function computeDefaultSubAgentCreditLimit(available: number): number {
  return computeSubAgentCreditByRatio(available, DEFAULT_SUB_AGENT_CREDIT_RATIO.value);
}

function applyCreateSubAgentCreditRatio(ratioPercent: number) {
  createForm.value.creditLimit = computeSubAgentCreditByRatio(createSubCreditMax.value, ratioPercent);
}

function applyCreateSubAgentDefaultCredit() {
  if (createAccountMode.value !== 2) return;
  createForm.value.creditLimit = computeDefaultSubAgentCreditLimit(createSubCreditMax.value);
}

watch(
  () => [createVisible.value, createAccountMode.value, createParentAgentId.value] as const,
  ([visible, mode, parentId]) => {
    if (!visible || mode !== 2 || !parentId) return;
    void ensureCreateParentCredit(parentId).then(() => {
      applyCreateSubAgentDefaultCredit();
    });
  },
);

function canAgentCreateSub(row: Pick<AgentRow, 'level'>) {
  const max = hierarchySettings.value.maxAgentLevel;
  if (max === 0) return true;
  return row.level < max;
}

function parentChainLabel(row: Pick<AgentRow, 'parentChainLabel' | 'parentUsername'>) {
  if (row.parentChainLabel) return row.parentChainLabel;
  return row.parentUsername ?? '—';
}

const tier1AgentOptions = computed(() => agentOptions.value.filter((a) => a.level === 1));

const parentAgentOptionsForCreate = computed(() => {
  const max = hierarchySettings.value.maxAgentLevel;
  return agentOptions.value.filter((a) => max === 0 || a.level < max);
});

const createParentSelectOptions = computed(() => {
  const childLevel = createToolbarChildLevel.value;
  if (childLevel == null || childLevel < 2) return [];
  return parentOptionsForChildLevel(childLevel);
});

const createParentSelectPlaceholder = computed(() => {
  const childLevel = createToolbarChildLevel.value;
  if (childLevel == null || childLevel < 2) return t('user.filter.agent_ph');
  return t('agent.hint.select_parent_for_level', { level: lvlLabel(childLevel - 1) });
});

function agentOptionLabel(a: { username: string; id: string; level: number; parentUsername?: string | null }) {
  const chain = a.parentUsername ? `${a.parentUsername} / ${a.username}` : a.username;
  return `L${a.level} ${chain} (#${a.id})`;
}

function resolveCreateParentLabel(agentId: string) {
  const opt = agentOptions.value.find((a) => a.id === agentId);
  if (opt) return agentOptionLabel(opt);
  const tier1 = tier1Agents.value.find((a) => a.userId === agentId);
  if (tier1) return tier1.username;
  const row = findAgentRowByUserId(agentId);
  if (row) {
    return row.parentUsername ? `${row.parentUsername} / ${row.username}` : row.username;
  }
  return agentId;
}

function ensurePageInit(): Promise<void> {
  if (pageInitLoaded.value) return Promise.resolve();
  if (!pageInitPromise) {
    pageInitPromise = loadUsersPageInit().finally(() => {
      pageInitPromise = null;
    });
  }
  return pageInitPromise;
}

function loadActiveViewTabData() {
  const tab = activeViewTab.value;
  if (tab === 'players') {
    if (canViewUsers.value) void loadAllPlayers();
    return;
  }
  if (!canViewAgents.value) return;
  if (tab === 'tier1Agents') {
    void loadTier1Agents();
    return;
  }
  const m = /^agentLevel-(\d+)$/.exec(tab);
  if (m) {
    void ensurePageInit().then(() => loadSubAgentsAtLevel(Number(m[1])));
  }
}

onMounted(() => {
  void ensurePageInit();
  loadActiveViewTabData();
});
// KeepAlive 激活时静默刷新当前 tab 列表（不重复 page-init）
onActivated(() => {
  void ensurePageInit();
  const tab = activeViewTab.value;
  if (tab === 'players' && canViewUsers.value && allPlayers.value.length > 0) void loadAllPlayers();
  else if (tab === 'tier1Agents' && canViewAgents.value && tier1Agents.value.length > 0) void loadTier1Agents();
  else {
    const m = /^agentLevel-(\d+)$/.exec(tab);
    if (m) {
      const lvl = Number(m[1]);
      const st = subAgentLevelState[lvl];
      if (st?.agents.length) loadSubAgentsAtLevel(lvl);
    }
  }
});

async function loadAgentSuspendDefaults() {
  try {
    const { data } = await api.get('/admin/agents/settings/suspend');
    agentSuspendDefaults.value = {
      suspendFreezeDirectPlayers: Boolean(data.data?.suspendFreezeDirectPlayers),
      suspendBlockPlayerLogin: Boolean(data.data?.suspendBlockPlayerLogin),
    };
  } catch {
    /* keep defaults */
  }
}

async function loadUsersPageInit() {
  try {
    const [pageInitRes] = await Promise.all([
      api.get('/admin/users/page-init'),
      loadAgentSuspendDefaults(),
    ]);
    const { data } = pageInitRes;
    const payload = data.data as {
      hierarchySettings?: { maxAgentLevel: number; defaultSubAgentCreditRatio?: number };
      agentLevelCounts?: Record<number, number>;
    };
    if (payload.hierarchySettings) {
      hierarchySettings.value = {
        maxAgentLevel: payload.hierarchySettings.maxAgentLevel ?? 0,
        defaultSubAgentCreditRatio: payload.hierarchySettings.defaultSubAgentCreditRatio ?? 50,
      };
    }
    if (payload.agentLevelCounts) {
      agentLevelCounts.value = payload.agentLevelCounts;
      if (payload.agentLevelCounts[1] !== undefined) {
        tier1Total.value = payload.agentLevelCounts[1];
      }
    }
    pageInitLoaded.value = true;
  } catch {
    /* keep defaults */
  }
}

function openGlobalSettings() {
  void router.push('/users/settings');
}

/* ─── Load tier-1 agents ─── */
async function loadTier1Agents() {
  try {
    const { data } = await api.get('/admin/agents', {
      params: {
        page: tier1Page.value,
        pageSize: tier1PageSize.value,
        keyword: tier1Keyword.value.trim() || undefined,
        status: tier1FilterStatus.value || undefined,
        level: 1,
      },
    });
    tier1Agents.value = data.data.items as AgentRow[];
    tier1Total.value = data.data.total;
  } catch (e) {
    tier1Agents.value = [];
    tier1Total.value = 0;
    ElMessage.error(resolveApiError(e, t, 'msg.load_failed'));
  }
}

function onTier1PageChange(p: number) {
  tier1Page.value = p;
  loadTier1Agents();
}

function onTier1SizeChange(size: number) {
  tier1PageSize.value = size;
  tier1Page.value = 1;
  loadTier1Agents();
}

function searchTier1Agents() {
  tier1Page.value = 1;
  loadTier1Agents();
}

async function loadAgentLevelCounts() {
  try {
    const { data } = await api.get('/admin/agents/level-counts');
    const raw = data.data ?? {};
    const normalized: Record<number, number> = {};
    for (const [lvl, cnt] of Object.entries(raw)) {
      normalized[Number(lvl)] = Number(cnt) || 0;
    }
    agentLevelCounts.value = normalized;
    if (normalized[1] !== undefined) {
      tier1Total.value = normalized[1];
    }
  } catch {
    agentLevelCounts.value = {};
  }
}

async function loadSubAgentsAtLevel(level: number) {
  const st = ensureSubAgentState(level);
  try {
    const { data } = await api.get('/admin/agents', {
      params: {
        page: st.page,
        pageSize: st.pageSize,
        keyword: st.keyword.trim() || undefined,
        status: st.filterStatus || undefined,
        level,
      },
    });
    st.agents = data.data.items as AgentRow[];
    st.total = data.data.total;
  } catch (e) {
    st.agents = [];
    st.total = 0;
    ElMessage.error(resolveApiError(e, t, 'msg.load_failed'));
  }
}

function onSubAgentPageChange(level: number, p: number) {
  ensureSubAgentState(level).page = p;
  loadSubAgentsAtLevel(level);
}

function onSubAgentSizeChange(level: number, size: number) {
  const st = ensureSubAgentState(level);
  st.pageSize = size;
  st.page = 1;
  loadSubAgentsAtLevel(level);
}

function bindSubAgentPageChange(level: number) {
  return (p: number) => onSubAgentPageChange(level, p);
}

function bindSubAgentSizeChange(level: number) {
  return (size: number) => onSubAgentSizeChange(level, size);
}

function searchSubAgentsAtLevel(level: number) {
  ensureSubAgentState(level).page = 1;
  loadSubAgentsAtLevel(level);
}

function reloadSubAgentTabs() {
  return loadAgentLevelCounts().then(() => {
    for (const lvl of visibleSubAgentTabLevels.value) {
      loadSubAgentsAtLevel(lvl);
    }
  });
}

function reloadAgentLists() {
  loadTier1Agents();
  void reloadSubAgentTabs();
}

/* ─── Load main agent list ─── */
async function load() {
  reloadAgentLists();
}

async function loadAgentOptions(keyword = '') {
  agentOptionsLoading.value = true;
  try {
    const { data } = await api.get('/admin/agents/options', {
      params: {
        keyword: keyword.trim() || undefined,
        limit: 50,
      },
    });
    agentOptions.value = data.data;
  } catch {
    agentOptions.value = [];
  } finally {
    agentOptionsLoading.value = false;
  }
}

function onAgentOptionsSearch(keyword: string) {
  void loadAgentOptions(keyword);
}

async function loadAllPlayers() {
  playerLoading.value = true;
  try {
    const params: Record<string, unknown> = {
      page: playerPage.value,
      pageSize: playerPageSize.value,
      keyword: playerKeyword.value.trim() || undefined,
      status: playerFilterStatus.value || undefined,
    };
    if (playerFilterAgent.value) {
      params.parentId = playerFilterAgent.value;
    }
    const { data } = await api.get('/admin/users', { params });
    allPlayers.value = data.data.items as PlayerRow[];
    playerTotal.value = data.data.total;
  } catch {
    allPlayers.value = [];
    playerTotal.value = 0;
  } finally {
    playerLoading.value = false;
  }
}

function searchPlayers() {
  playerPage.value = 1;
  loadAllPlayers();
}

function onPlayerPageChange(p: number) {
  playerPage.value = p;
  loadAllPlayers();
}

function onPlayerSizeChange(size: number) {
  playerPageSize.value = size;
  playerPage.value = 1;
  loadAllPlayers();
}

function affiliationLabel(row: Pick<PlayerRow, 'affiliationAgents'>) {
  return formatPlayerAffiliationLabel(row, t('user.type.player'), t('agent.platform_row_name'));
}

function eventClickElement(event: Event): Element | null {
  const target = event.target;
  if (target instanceof Element) return target;
  if (target instanceof Node) return target.parentElement;
  return null;
}

function openAgentDirectPlayers(row: AgentRow, _column: unknown, event: Event) {
  const el = eventClickElement(event);
  if (!el) return;
  if (el.closest('button') || el.closest('.el-button') || el.closest('.admin-agent-row-actions')) return;
  void router.push({
    path: `/users/agents/${row.userId}/players`,
    query: {
      username: row.username,
      fromTab: activeViewTab.value,
    },
  });
}

function agentRowClassName() {
  return 'row-navigable';
}

watch(activeViewTab, (tab) => {
  const m = /^agentLevel-(\d+)$/.exec(tab);
  if (m) {
    void ensurePageInit().then(() => loadSubAgentsAtLevel(Number(m[1])));
    return;
  }
  if (tab === 'players' && canViewUsers.value && !allPlayers.value.length) void loadAllPlayers();
  if (tab === 'tier1Agents' && canViewAgents.value && !tier1Agents.value.length) void loadTier1Agents();
});

watch(visibleSubAgentTabLevels, (levels) => {
  const tab = activeViewTab.value;
  const m = /^agentLevel-(\d+)$/.exec(tab);
  if (!m) return;
  const activeLevel = Number(m[1]);
  if (levels.includes(activeLevel)) {
    const st = subAgentLevelState[activeLevel];
    if (!st?.agents.length) loadSubAgentsAtLevel(activeLevel);
  }
});

const walletLedgerVisible = ref(false);
const walletLedgerPlayerId = ref('');
const walletLedgerPlayerUsername = ref<string | null>(null);

function openPlayerWalletLedger(playerId: string, playerUsername?: string | null) {
  walletLedgerPlayerId.value = playerId;
  walletLedgerPlayerUsername.value = playerUsername ?? null;
  walletLedgerVisible.value = true;
}

/* ─── Create (unified) ─── */
function openCreateAccount() {
  createForm.value = emptyPlayerCreateForm();
  createForm.value.asTier1Agent = false;
  createParentAgentId.value = '';
  createParentLocked.value = false;
  createAccountMode.value = 0;
  createVisible.value = true;
}

function openCreateTier1Agent() {
  createForm.value = emptyPlayerCreateForm();
  createForm.value.asTier1Agent = true;
  createParentAgentId.value = '';
  createParentLocked.value = false;
  createAccountMode.value = 1;
  createVisible.value = true;
}

function openCreatePlayer(parentAgentUserId: string) {
  createForm.value = emptyPlayerCreateForm();
  createForm.value.asTier1Agent = false;
  createForm.value.parentId = parentAgentUserId;
  createParentAgentId.value = parentAgentUserId;
  createParentLocked.value = true;
  createAccountMode.value = 0;
  createVisible.value = true;
}

function openCreateSubAgent(parentAgentUserId: string) {
  createForm.value = emptyPlayerCreateForm();
  createForm.value.asTier1Agent = false;
  createParentAgentId.value = parentAgentUserId;
  createParentLocked.value = true;
  const parentLevel = resolveParentAgentLevel(parentAgentUserId);
  createToolbarChildLevel.value = parentLevel != null ? parentLevel + 1 : null;
  createAccountMode.value = 2;
  createVisible.value = true;
}

function openCreateSubAgentFromToolbar(childLevel: number) {
  createForm.value = emptyPlayerCreateForm();
  createForm.value.asTier1Agent = false;
  createParentLocked.value = false;
  createToolbarChildLevel.value = childLevel;
  const parents = parentOptionsForChildLevel(childLevel);
  createParentAgentId.value = parents.length === 1 ? parents[0].id : '';
  createAccountMode.value = 2;
  createVisible.value = true;
}

async function submitCreate() {
  const isSubAgent = createAccountMode.value === 2;
  const isAgent = createForm.value.asTier1Agent || isSubAgent;
  let payload: Record<string, unknown>;
  try {
    if (isSubAgent) {
      if (!createParentAgentId.value) {
        throw new Error(t('agent.hint.select_parent_for_level', { level: lvlLabel((createToolbarChildLevel.value ?? 2) - 1) }));
      }
      const parentLevel = resolveParentAgentLevel(createParentAgentId.value);
      const targetLevel = createTargetAgentLevel.value;
      if (targetLevel == null || parentLevel == null || parentLevel !== targetLevel - 1) {
        throw new Error(t('agent.err.parent_level_mismatch', { level: lvlLabel(targetLevel ?? 0) }));
      }
      if (!createForm.value.username.trim()) throw new Error(t('err.username_required'));
      if (createForm.value.password.length < 8) throw new Error(t('err.password_min'));
      if (createForm.value.password !== createForm.value.confirmPassword) throw new Error(t('err.password_mismatch'));
      if (createForm.value.creditLimit > createSubCreditMax.value) {
        throw new Error(t('err.insufficient_credit'));
      }
      payload = {
        username: createForm.value.username.trim(),
        password: createForm.value.password,
        asSubAgent: true,
        parentAgentId: createParentAgentId.value,
        phone: createForm.value.phone.trim() || undefined,
        email: createForm.value.email.trim() || undefined,
        creditLimit: createForm.value.creditLimit,
        cashbackRate: percentToDecimalRate(createForm.value.cashbackRate),
        maxSingleDeposit: createForm.value.maxSingleDeposit > 0 ? createForm.value.maxSingleDeposit : undefined,
        maxDailyDeposit: createForm.value.maxDailyDeposit > 0 ? createForm.value.maxDailyDeposit : undefined,
      };
    } else {
      payload = buildCreatePlayerPayload(createForm.value);
      // When creating player from expansion, ensure parentId is set
      if (createParentAgentId.value && !payload.parentId) {
        payload.parentId = createParentAgentId.value;
      }
    }
  } catch (e) {
    ElMessage.warning(resolveFormError(e, t));
    return;
  }
  createLoading.value = true;
  try {
    await api.post('/admin/users', payload);
    ElMessage.success(
      isAgent
        ? t('msg.agent_created')
        : t('user.msg.created_with_password', { password: createForm.value.password }),
    );
    createVisible.value = false;
    const createdLevel = isSubAgent ? createTargetAgentLevel.value : null;
    load();
    refreshExpandedParents();
    if (createdLevel != null && createdLevel >= 2) {
      activeViewTab.value = agentLevelTabName(createdLevel);
    }
    const parentId = createParentAgentId.value || createForm.value.parentId;
    if (parentId) {
      directPlayersReload.value?.();
    }
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.create_failed'));
  } finally {
    createLoading.value = false;
  }
}

/* ─── Edit Player ─── */
async function openEditPlayer(id: string) {
  const { data } = await api.get(`/admin/users/${id}`);
  const d = data.data as PlayerDetail;
  editingId.value = id;
  editPlayerForm.value = editFormFromDetail(d);
  editPlayerVisible.value = true;
}

async function submitEditPlayer() {
  if (editPlayerForm.value.newPassword && editPlayerForm.value.newPassword.length < 8) {
    ElMessage.warning(t('err.password_min'));
    return;
  }
  const newPwd = editPlayerForm.value.newPassword.trim();
  const passwordOnly =
    newPwd &&
    !canEditPlayerProfile.value &&
    canResetPassword.value &&
    !editPlayerForm.value.useCustomCashback;

  if (passwordOnly) {
    editPlayerLoading.value = true;
    try {
      const { data } = await api.post(`/admin/users/${editingId.value}/reset-password`, {
        password: newPwd,
      });
      const plain = data.data?.password ?? newPwd;
      editPlayerForm.value.managedPassword = plain;
      editPlayerForm.value.newPassword = '';
      ElMessage.success(t('user.msg.password_saved', { password: plain }));
    } catch (e: unknown) {
      const err = e as { response?: { data?: { error?: string } } };
      ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
    } finally {
      editPlayerLoading.value = false;
    }
    return;
  }

  if (!canEditPlayerProfile.value) {
    ElMessage.warning(t('msg.save_failed'));
    return;
  }

  try {
    assertPlayerUsername(editPlayerForm.value.username);
  } catch (e) {
    ElMessage.warning(resolveFormError(e, t));
    return;
  }
  editPlayerLoading.value = true;
  try {
    const payload: Record<string, unknown> = {
      username: editPlayerForm.value.username.trim(),
      phone: editPlayerForm.value.phone.trim() || undefined,
      email: editPlayerForm.value.email.trim() || undefined,
      password: newPwd || undefined,
      cashbackRate: editPlayerForm.value.useCustomCashback
        ? percentToDecimalRate(editPlayerForm.value.customCashbackRate ?? 0)
        : null,
    };
    const { data } = await api.put(`/admin/users/${editingId.value}`, payload);
    const updated = data.data as PlayerDetail;
    if (newPwd) {
      editPlayerForm.value.managedPassword = updated.managedPassword ?? newPwd;
      editPlayerForm.value.newPassword = '';
      ElMessage.success(t('user.msg.password_saved', { password: editPlayerForm.value.managedPassword }));
      return;
    }
    ElMessage.success(t('msg.saved'));
    editPlayerVisible.value = false;
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
  } finally {
    editPlayerLoading.value = false;
  }
}

/* ─── Edit Agent ─── */
async function openEditAgent(userId: string) {
  const { data } = await api.get(`/admin/agents/${userId}`);
  const d = data.data as AgentDetail;
  editingId.value = userId;
  editAgentForm.value = editFormFromAgentDetail(d);
  editAgentVisible.value = true;
}

async function submitEditAgent() {
  if (editAgentForm.value.newPassword && editAgentForm.value.newPassword.length < 8) {
    ElMessage.warning(t('err.password_min'));
    return;
  }
  editAgentLoading.value = true;
  try {
    const newPwd = editAgentForm.value.newPassword.trim();
    const { data } = await api.put(`/admin/agents/${editingId.value}`, {
      username: editAgentForm.value.username.trim(),
      status: editAgentForm.value.status,
      phone: editAgentForm.value.phone.trim() || undefined,
      email: editAgentForm.value.email.trim() || undefined,
      cashbackRate: percentToDecimalRate(editAgentForm.value.cashbackRate),
      maxSingleDeposit: editAgentForm.value.maxSingleDeposit,
      maxDailyDeposit: editAgentForm.value.maxDailyDeposit,
      password: newPwd || undefined,
    });
    const updated = data.data as AgentDetail;
    if (newPwd) {
      editAgentForm.value.managedPassword = updated.managedPassword ?? newPwd;
      editAgentForm.value.newPassword = '';
      ElMessage.success(t('user.msg.password_saved', { password: editAgentForm.value.managedPassword }));
      return;
    }
    ElMessage.success(t('msg.saved'));
    editAgentVisible.value = false;
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.save_failed'));
  } finally {
    editAgentLoading.value = false;
  }
}

/* ─── Detail ─── */
async function openDetailPlayer(id: string) {
  const { data } = await api.get(`/admin/users/${id}`);
  playerDetail.value = data.data as PlayerDetail;
  detailPlayerVisible.value = true;
}

async function openDetailAgent(userId: string) {
  const { data } = await api.get(`/admin/agents/${userId}`);
  agentDetail.value = data.data as AgentDetail;
  detailAgentVisible.value = true;
}

/* ─── Credit ─── */
async function openCredit(userId: string) {
  editingId.value = userId;
  creditForm.value = { amount: 10000, remark: '' };
  creditContext.value = null;
  creditVisible.value = true;
  creditContextLoading.value = true;
  try {
    creditContext.value = await fetchAdminAgentCreditContext(userId);
  } catch (e: unknown) {
    ElMessage.error(resolveApiError(e, t, 'msg.load_failed'));
    creditVisible.value = false;
  } finally {
    creditContextLoading.value = false;
  }
}

async function submitCredit() {
  if (creditForm.value.amount === 0) {
    ElMessage.warning(t('msg.credit_zero'));
    return;
  }
  const maxInc = maxCreditIncreaseAmount(creditContext.value);
  if (creditForm.value.amount > 0 && maxInc !== undefined && creditForm.value.amount > maxInc) {
    ElMessage.warning(t('err.insufficient_credit'));
    return;
  }
  creditLoading.value = true;
  try {
    await api.post(`/admin/agents/${editingId.value}/credit`, {
      amount: creditForm.value.amount,
      requestId: `credit-${editingId.value}-${Date.now()}`,
      remark: creditForm.value.remark || undefined,
    });
    ElMessage.success(t('msg.credit_adjusted'));
    creditVisible.value = false;
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    ElMessage.error(resolveApiError(e, t, 'msg.credit_adjust_failed'));
  } finally {
    creditLoading.value = false;
  }
}

/* ─── Freeze / Unfreeze ─── */
async function toggleFreezePlayer(row: PlayerRow) {
  const freeze = row.status === 'ACTIVE';
  const action = freeze ? t('common.freeze') : t('common.unfreeze');
  try {
    await ElMessageBox.confirm(
      t('msg.freeze_confirm_body', {
        action,
        name: row.username,
        extra: freeze ? t('msg.freeze_extra') : '',
      }),
      t('msg.freeze_confirm_title', { action }),
      { type: 'warning', confirmButtonText: action, cancelButtonText: t('common.cancel') },
    );
  } catch {
    return;
  }
  try {
    await api.put(`/admin/users/${row.id}`, {
      status: freeze ? 'SUSPENDED' : 'ACTIVE',
    });
    ElMessage.success(t('msg.freeze_done', { action }));
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.freeze_failed', { action }));
  }
}

/* ─── Delete Player ─── */
async function deletePlayer(row: PlayerRow) {
  try {
    await ElMessageBox.confirm(
      t('msg.delete_player_body', { name: row.username }),
      t('msg.delete_player_title'),
      {
        type: 'warning',
        confirmButtonText: t('common.delete'),
        cancelButtonText: t('common.cancel'),
      },
    );
  } catch {
    return;
  }
  // Second confirmation — type username to confirm
  const input = ref('');
  try {
    await ElMessageBox({
      title: t('msg.delete_player_confirm_title'),
      message: () =>
        h('div', {}, [
          h('p', { style: 'margin: 0 0 8px; font-size: 13px; color: var(--el-text-color-regular)' },
            t('msg.delete_player_confirm_hint', { name: row.username })),
          h('input', {
            value: input.value,
            placeholder: row.username,
            onInput: (e: Event) => { input.value = (e.target as HTMLInputElement).value; },
            style: 'width: 100%; padding: 6px 8px; border: 1px solid var(--el-border-color); border-radius: 4px; font-size: 13px; box-sizing: border-box',
          }),
        ]),
      showCancelButton: true,
      confirmButtonText: t('common.delete'),
      cancelButtonText: t('common.cancel'),
      beforeClose: (action: string, _instance: unknown, done: () => void) => {
        if (action === 'confirm') {
          if (input.value.trim() !== row.username) {
            ElMessage.warning(t('msg.delete_player_mismatch'));
            return;
          }
        }
        done();
      },
    });
  } catch {
    return;
  }
  try {
    await api.delete(`/admin/users/${row.id}`);
    ElMessage.success(t('msg.delete_player_done'));
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.delete_player_failed'));
  }
}

/* ─── Freeze / Unfreeze Agent ─── */
const freezeAgentIsSuspend = computed(() => {
  if (!freezeAgentTarget.value) return true;
  return subAgentAccountStatus(freezeAgentTarget.value) === 'ACTIVE';
});

function toggleFreezeAgent(row: AgentRow) {
  freezeAgentTarget.value = row;
  freezeAgentForm.value = {
    freezeDirectPlayers: agentSuspendDefaults.value.suspendFreezeDirectPlayers,
    blockDirectPlayerLogin: agentSuspendDefaults.value.suspendBlockPlayerLogin,
    unfreezeDirectPlayers: false,
  };
  freezeAgentVisible.value = true;
}

async function submitFreezeAgent() {
  const row = freezeAgentTarget.value;
  if (!row) return;
  const suspend = freezeAgentIsSuspend.value;
  const action = suspend ? t('common.freeze') : t('common.unfreeze');
  freezeAgentLoading.value = true;
  try {
    if (suspend) {
      await api.put(`/admin/agents/${row.userId}`, {
        status: 'SUSPENDED',
        freezeDirectPlayers: freezeAgentForm.value.freezeDirectPlayers,
        blockDirectPlayerLogin: freezeAgentForm.value.blockDirectPlayerLogin,
      });
      ElMessage.success(
        freezeAgentForm.value.freezeDirectPlayers
          ? t('agent.msg.cascade_freeze_done')
          : t('agent.msg.freeze_done', { action }),
      );
    } else {
      await api.put(`/admin/agents/${row.userId}`, {
        status: 'ACTIVE',
        unfreezeDirectPlayers: freezeAgentForm.value.unfreezeDirectPlayers,
      });
      ElMessage.success(
        freezeAgentForm.value.unfreezeDirectPlayers
          ? t('agent.msg.cascade_unfreeze_done')
          : t('agent.msg.freeze_done', { action }),
      );
    }
    freezeAgentVisible.value = false;
    load();
    refreshExpandedParents();
  } catch (e: unknown) {
    const err = e as { response?: { data?: { error?: string } } };
    ElMessage.error(err.response?.data?.error ?? t('msg.freeze_failed', { action }));
  } finally {
    freezeAgentLoading.value = false;
  }
}

/* ─── Helpers ─── */
function refreshExpandedParents() {
  loadAllPlayers();
  reloadAgentLists();
  directPlayersReload.value?.();
}

const {
  transferVisible,
  transferLoading,
  transferType,
  transferTarget,
  transferAmount,
  transferRemark,
  transferContext,
  transferContextLoading,
  transferAmountRange,
  transferAmountDisabled,
  transferAmountExceedsCap,
  transferAmountCapError,
  transferTitle,
  openTransfer,
  submitTransfer,
} = useAdminPlayerTransfer(async () => {
  load();
  refreshExpandedParents();
});

function creditLine(row: AgentRow) {
  return `${formatAmount(row.creditLimit)} / ${formatAmount(row.usedCredit)} / ${formatAmount(row.availableCredit)}`;
}

function creditLineFull(row: AgentRow) {
  return `${formatAmountFull(row.creditLimit)} / ${formatAmountFull(row.usedCredit)} / ${formatAmountFull(row.availableCredit)}`;
}

function formatTime(v: string) {
  if (!v) return '—';
  return new Date(v).toLocaleString(localeTag.value);
}

function formatLastLogin(v: string | null) {
  if (!v) return t('common.never_login');
  const d = new Date(v);
  const now = new Date();
  const sameDay =
    d.getFullYear() === now.getFullYear() &&
    d.getMonth() === now.getMonth() &&
    d.getDate() === now.getDate();
  if (sameDay) {
    return d.toLocaleTimeString(localeTag.value, { hour: '2-digit', minute: '2-digit' });
  }
  return d.toLocaleString(localeTag.value, {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function statusTagType(s: string) {
  return s === 'ACTIVE' ? 'success' : 'warning';
}

function statusLabel(s: string) {
  const key = `user.status.${s}`;
  const v = t(key);
  return v !== key ? v : s;
}

function creditTypeLabel(type: string) {
  if (type === 'CREDIT_INCREASE') return t('agent.credit.increase');
  if (type === 'CREDIT_DECREASE') return t('agent.credit.decrease');
  return type;
}

provide(agentPlayerActionsKey, {
  get canCreatePlayer() {
    return canCreateUsers.value;
  },
  get playerActionFlags() {
    return playerActionFlags.value;
  },
  openCreatePlayer,
  openDetailPlayer,
  openEditPlayer,
  openTransfer,
  toggleFreezePlayer,
  deletePlayer,
  openPlayerWalletLedger,
});
</script>

<template>
  <div class="agent-mgr-shell">
    <router-view v-if="isAgentChildRoute" />
    <div v-else class="admin-list-page agent-mgr-page">
    <InviteManageDialog v-model="inviteDialogOpen" />

    <div class="mgr-tabs-shell">
      <div v-if="canManageSettings" class="mgr-toolbar-actions">
        <el-button class="settings-toolbar-btn" @click="openGlobalSettings">
          {{ t('user.page_settings') }}
        </el-button>
        <el-button type="primary" class="invite-prominent-btn" @click="inviteDialogOpen = true">
          {{ t('invite.menu_btn') }}
        </el-button>
      </div>
      <el-tabs v-model="activeViewTab" class="mgr-top-tabs" :class="{ 'mgr-top-tabs--with-actions': canManageSettings }">
      <!-- ─── Tab: 全部玩家（默认） ─── -->
      <el-tab-pane v-if="canViewUsers" :label="`${t('user.type.player')} (${playerTotal})`" name="players">
        <section class="list-panel player-list-panel">
          <div class="list-panel-toolbar">
            <el-form inline class="list-chrome__grow">
              <el-form-item :label="t('common.keyword')">
                <el-input
                  v-model="playerKeyword"
                  :placeholder="t('user.filter.username_ph')"
                  clearable
                  style="width: 180px"
                  @keyup.enter="searchPlayers"
                />
              </el-form-item>
              <el-form-item v-if="canViewAgents" :label="t('user.filter.agent')">
                <el-select
                  v-model="playerFilterAgent"
                  :placeholder="t('user.filter.agent_ph')"
                  clearable
                  filterable
                  remote
                  reserve-keyword
                  :remote-method="onAgentOptionsSearch"
                  :loading="agentOptionsLoading"
                  style="width: 200px"
                  @focus="() => { if (!agentOptions.length) void loadAgentOptions(); }"
                >
                  <el-option
                    v-for="a in agentOptions"
                    :key="a.id"
                    :label="agentOptionLabel(a)"
                    :value="a.id"
                  />
                </el-select>
              </el-form-item>
              <el-form-item :label="t('common.status')">
                <el-select v-model="playerFilterStatus" :placeholder="t('common.all')" clearable style="width: 120px">
                  <el-option :label="t('user.status.ACTIVE')" value="ACTIVE" />
                  <el-option :label="t('user.status.SUSPENDED')" value="SUSPENDED" />
                </el-select>
              </el-form-item>
              <el-form-item>
                <el-button type="primary" @click="searchPlayers">{{ t('common.search') }}</el-button>
              </el-form-item>
            </el-form>
            <div v-if="canCreateUsers" class="list-chrome__actions">
              <el-button type="primary" @click="openCreateAccount">{{ t('user.create_btn') }}</el-button>
            </div>
          </div>
          <AdminTableWrap>
            <el-table v-loading="playerLoading" :data="allPlayers" stripe>
              <template #empty>
                <AdminTableEmpty />
              </template>
              <el-table-column type="index" :index="(i: number) => (playerPage - 1) * playerPageSize + i + 1" :label="t('common.seq')" width="70" align="center" />
              <el-table-column prop="username" :label="t('user.col.username')" min-width="120" />
              <el-table-column :label="t('common.status')" min-width="128">
                <template #default="{ row }">
                  <AdminPlayerStatusCell :status="row.status" :is-online="row.isOnline" />
                </template>
              </el-table-column>
              <el-table-column :label="t('user.col.agent')" min-width="200">
                <template #default="{ row }">
                  <el-tag size="small" type="info" class="affiliation-tag">{{ affiliationLabel(row) }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column :label="t('user.col.invite_code')" min-width="100" show-overflow-tooltip>
                <template #default="{ row }">
                  <code v-if="row.inviteCode" class="invite-code-cell">{{ row.inviteCode }}</code>
                  <span v-else>—</span>
                </template>
              </el-table-column>
              <el-table-column :label="t('user.col.balance')" min-width="128" align="right">
                <template #default="{ row }">
                  <el-tooltip :content="`${formatAmountFull(row.availableBalance)} / ${formatAmountFull(row.frozenBalance)}`" placement="top">
                    <span class="amount-compact">{{ formatAmount(row.availableBalance) }} / {{ formatAmount(row.frozenBalance) }}</span>
                  </el-tooltip>
                </template>
              </el-table-column>
              <el-table-column prop="betCount" :label="t('user.col.bets')" width="64" align="center" />
              <el-table-column :label="t('user.col.stake_payout')" min-width="108" align="right">
                <template #default="{ row }">
                  <span class="amount-compact">{{ formatAmount(row.totalStake) }} / {{ formatAmount(row.totalReturn) }}</span>
                </template>
              </el-table-column>
              <el-table-column :label="t('common.actions')" min-width="340" align="center">
                <template #default="{ row }">
                  <AdminPlayerRowActions
                    v-bind="playerActionFlags"
                    :row="row"
                    @detail="openDetailPlayer(row.id)"
                    @ledger="openPlayerWalletLedger(row.id, row.username)"
                    @edit="openEditPlayer(row.id)"
                    @deposit="openTransfer('deposit', row)"
                    @withdraw="openTransfer('withdraw', row)"
                    @freeze="toggleFreezePlayer(row)"
                    @delete="deletePlayer(row)"
                  />
                </template>
              </el-table-column>
            </el-table>
          </AdminTableWrap>
          <div class="pager">
            <el-pagination
              v-model:current-page="playerPage"
              v-model:page-size="playerPageSize"
              :total="playerTotal"
              :page-sizes="[10, 20, 50, 100]"
              layout="total, sizes, prev, pager, next"
              background
              @current-change="onPlayerPageChange"
              @size-change="onPlayerSizeChange"
            />
          </div>
        </section>
      </el-tab-pane>

      <!-- ─── Tab: 一级代理 ─── -->
      <el-tab-pane v-if="canViewAgents" :label="`${agentTierName(1)} (${tier1Total})`" name="tier1Agents">
    <section class="list-panel agent-list-panel">
      <div class="list-panel-toolbar">
        <el-form inline class="list-chrome__grow">
          <el-form-item :label="t('common.keyword')">
            <el-input v-model="tier1Keyword" :placeholder="t('agent.filter.username_ph')" clearable style="width: 180px" @keyup.enter="searchTier1Agents" />
          </el-form-item>
          <el-form-item :label="t('common.status')">
            <el-select v-model="tier1FilterStatus" :placeholder="t('common.all')" clearable style="width: 120px">
              <el-option :label="t('user.status.ACTIVE')" value="ACTIVE" />
              <el-option :label="t('user.status.SUSPENDED')" value="SUSPENDED" />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="searchTier1Agents">{{ t('common.search') }}</el-button>
          </el-form-item>
        </el-form>
        <div v-if="canCreateAgents" class="list-chrome__actions">
          <el-button type="primary" @click="openCreateTier1Agent">{{ t('agent.create_btn') }}</el-button>
        </div>
      </div>
      <p class="list-hint">{{ t('agent.open_agent_hint') }}</p>
      <AdminTableWrap>
        <el-table
          :data="tier1Agents"
          stripe
          row-key="userId"
          :row-class-name="agentRowClassName"
          class="compact-agent-table"
          @row-click="openAgentDirectPlayers"
        >
          <template #empty>
            <AdminTableEmpty />
          </template>

          <el-table-column type="index" :index="(i: number) => (tier1Page - 1) * tier1PageSize + i + 1" :label="t('common.seq')" width="70" align="center" />
          <el-table-column prop="username" :label="t('user.col.username')" min-width="100" show-overflow-tooltip />
          <el-table-column :label="t('common.status')" min-width="72">
            <template #default="{ row }">
              <el-tag :type="statusTagType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="level" :label="t('agent.col.level')" min-width="52" align="center">
            <template #default="{ row }">L{{ row.level }}</template>
          </el-table-column>
          <el-table-column :label="t('agent.col.credit')" min-width="148" align="right">
            <template #default="{ row }">
              <el-tooltip :content="creditLineFull(row)" placement="top">
                <span class="amount-compact">{{ creditLine(row) }}</span>
              </el-tooltip>
            </template>
          </el-table-column>
          <el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" min-width="72" align="center" />
          <el-table-column prop="childAgentCount" :label="t('agent.col.sub_agents')" min-width="72" align="center" />
          <el-table-column :label="t('agent.col.cashback')" min-width="80" align="right">
            <template #default="{ row }">{{ formatRatePercent(row.cashbackRate) }}</template>
          </el-table-column>
          <el-table-column :label="t('common.actions')" min-width="300" align="center">
            <template #default="{ row }">
              <AdminAgentRowActions
                :row="{ userId: row.userId, status: row.status }"
                :can-create-sub="canAgentCreateSub(row)"
                :create-sub-label="childAgentActionLabel(row.level)"
                @detail="openDetailAgent(row.userId)"
                @edit="openEditAgent(row.userId)"
                @credit="openCredit(row.userId)"
                @create-sub="openCreateSubAgent(row.userId)"
                @freeze="toggleFreezeAgent(row)"
              />
            </template>
          </el-table-column>
        </el-table>
      </AdminTableWrap>
      <div class="pager">
        <el-pagination
          v-model:current-page="tier1Page"
          v-model:page-size="tier1PageSize"
          :total="tier1Total"
          :page-sizes="[10, 20, 50, 100]"
          layout="total, sizes, prev, pager, next"
          background
          @current-change="onTier1PageChange"
          @size-change="onTier1SizeChange"
        />
      </div>
    </section>
      </el-tab-pane>

      <!-- ─── Tab: L2+ 各级代理 ─── -->
      <template v-if="canViewAgents">
      <el-tab-pane
        v-for="agentLevel in visibleSubAgentTabLevels"
        :key="agentLevel"
        :label="agentLevelTabLabel(agentLevel)"
        :name="agentLevelTabName(agentLevel)"
      >
        <section class="list-panel agent-list-panel">
          <div class="list-panel-toolbar">
            <el-form inline class="list-chrome__grow">
              <el-form-item :label="t('common.keyword')">
                <el-input
                  v-model="ensureSubAgentState(agentLevel).keyword"
                  :placeholder="t('agent.filter.username_ph')"
                  clearable
                  style="width: 180px"
                  @keyup.enter="searchSubAgentsAtLevel(agentLevel)"
                />
              </el-form-item>
              <el-form-item :label="t('common.status')">
                <el-select
                  v-model="ensureSubAgentState(agentLevel).filterStatus"
                  :placeholder="t('common.all')"
                  clearable
                  style="width: 120px"
                >
                  <el-option :label="t('user.status.ACTIVE')" value="ACTIVE" />
                  <el-option :label="t('user.status.SUSPENDED')" value="SUSPENDED" />
                </el-select>
              </el-form-item>
              <el-form-item>
                <el-button type="primary" @click="searchSubAgentsAtLevel(agentLevel)">{{ t('common.search') }}</el-button>
              </el-form-item>
            </el-form>
            <div class="list-chrome__actions">
              <el-button type="primary" @click="openCreateSubAgentFromToolbar(agentLevel)">
                {{ agentLevel === 2 ? t('agent.create_sub_btn') : t('agent.create_level_agent_btn', { level: lvlLabel(agentLevel) }) }}
              </el-button>
            </div>
          </div>
          <p class="list-hint">{{ t('agent.open_agent_hint') }}</p>
          <AdminTableWrap>
            <el-table
              :data="ensureSubAgentState(agentLevel).agents"
              stripe
              row-key="userId"
              :row-class-name="agentRowClassName"
              class="compact-agent-table"
              @row-click="openAgentDirectPlayers"
            >
              <template #empty>
                <AdminTableEmpty />
              </template>
              <el-table-column type="index" :index="(i: number) => (ensureSubAgentState(agentLevel).page - 1) * ensureSubAgentState(agentLevel).pageSize + i + 1" :label="t('common.seq')" width="70" align="center" />
              <el-table-column prop="username" :label="t('user.col.username')" min-width="100" show-overflow-tooltip />
              <el-table-column :label="t('agent.col.parent_chain')" min-width="120" show-overflow-tooltip>
                <template #default="{ row }">{{ parentChainLabel(row) }}</template>
              </el-table-column>
              <el-table-column :label="t('common.status')" min-width="72">
                <template #default="{ row }">
                  <el-tag :type="statusTagType(subAgentAccountStatus(row))" size="small">{{ statusLabel(subAgentAccountStatus(row)) }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column :label="t('agent.col.credit')" min-width="148" align="right">
                <template #default="{ row }">
                  <el-tooltip :content="creditLineFull(row)" placement="top">
                    <span class="amount-compact">{{ creditLine(row) }}</span>
                  </el-tooltip>
                </template>
              </el-table-column>
              <el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" min-width="72" align="center" />
              <el-table-column :label="t('common.actions')" min-width="300" align="center">
                <template #default="{ row }">
                  <AdminAgentRowActions
                    :row="{ userId: row.userId, status: subAgentAccountStatus(row) }"
                    :can-create-sub="canAgentCreateSub(row)"
                    :create-sub-label="childAgentActionLabel(row.level)"
                    @detail="openDetailAgent(row.userId)"
                    @edit="openEditAgent(row.userId)"
                    @credit="openCredit(row.userId)"
                    @create-sub="openCreateSubAgent(row.userId)"
                    @freeze="toggleFreezeAgent(row)"
                  />
                </template>
              </el-table-column>
            </el-table>
          </AdminTableWrap>
          <div class="pager">
            <el-pagination
              :current-page="ensureSubAgentState(agentLevel).page"
              :page-size="ensureSubAgentState(agentLevel).pageSize"
              :total="ensureSubAgentState(agentLevel).total"
              :page-sizes="[10, 20, 50, 100]"
              layout="total, sizes, prev, pager, next"
              background
              @current-change="bindSubAgentPageChange(agentLevel)"
              @size-change="bindSubAgentSizeChange(agentLevel)"
            />
          </div>
        </section>
      </el-tab-pane>
      </template>
    </el-tabs>
    </div>
    </div>

    <!-- ═══════════ DIALOGS ═══════════ -->

    <!-- ── Freeze / Unfreeze Agent ── -->
    <el-dialog
      v-model="freezeAgentVisible"
      :title="freezeAgentIsSuspend ? t('agent.freeze.confirm_freeze_title') : t('agent.unfreeze.confirm_title')"
      width="480px"
      destroy-on-close
    >
      <template v-if="freezeAgentTarget">
        <p class="freeze-agent-intro">
          {{
            freezeAgentIsSuspend
              ? t('agent.freeze.confirm_freeze_body', { name: freezeAgentTarget.username })
              : t('agent.freeze.confirm_unfreeze_body', { name: freezeAgentTarget.username })
          }}
        </p>
        <div v-if="freezeAgentIsSuspend" class="freeze-agent-options">
          <el-checkbox
            v-if="freezeAgentTarget.directPlayerCount > 0"
            v-model="freezeAgentForm.freezeDirectPlayers"
          >
            {{ t('agent.freeze.opt_freeze_direct_players') }}
          </el-checkbox>
          <el-checkbox v-model="freezeAgentForm.blockDirectPlayerLogin">
            {{ t('agent.freeze.opt_block_player_login') }}
          </el-checkbox>
        </div>
        <div v-else class="freeze-agent-options">
          <el-checkbox
            v-if="freezeAgentTarget.directPlayerCount > 0"
            v-model="freezeAgentForm.unfreezeDirectPlayers"
          >
            {{ t('agent.unfreeze.opt_unfreeze_direct_players') }}
          </el-checkbox>
        </div>
      </template>
      <template #footer>
        <el-button @click="freezeAgentVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button
          :type="freezeAgentIsSuspend ? 'warning' : 'primary'"
          :loading="freezeAgentLoading"
          @click="submitFreezeAgent"
        >
          {{ freezeAgentIsSuspend ? t('common.freeze') : t('common.unfreeze') }}
        </el-button>
      </template>
    </el-dialog>

    <!-- ── Create (unified) ── -->
    <el-dialog
      v-model="createVisible"
      :title="createDialogTitle"
      width="460px"
      align-center
      destroy-on-close
      class="create-account-dialog"
    >
      <div
        v-if="createAccountMode === 2 && createParentAgentId && createParentLocked"
        class="create-meta-bar"
      >
        <div class="create-meta-row">
          <span class="create-meta-label">{{ t('agent.field.parent_agent') }}</span>
          <span class="create-meta-value">{{ resolveCreateParentLabel(createParentAgentId) }}</span>
          <el-tag v-if="createTargetAgentLevel" size="small" type="info">L{{ createTargetAgentLevel }}</el-tag>
        </div>
        <div class="create-meta-row">
          <span class="create-meta-label">{{ t('agent.field.available_credit') }}</span>
          <span class="create-meta-value c-green">{{ formatAmount(createParentAvailableCredit ?? '0') }}</span>
        </div>
      </div>

      <el-form label-width="100px" size="small" class="compact-create-form">
        <template v-if="createAccountMode === 2 && !createParentLocked">
          <el-form-item :label="t('agent.field.parent_agent')" required>
            <el-select
              v-model="createParentAgentId"
              :placeholder="createParentSelectPlaceholder"
              style="width: 100%"
            >
              <el-option
                v-for="a in createParentSelectOptions"
                :key="a.id"
                :label="agentOptionLabel(a)"
                :value="a.id"
              />
            </el-select>
            <div v-if="createTargetAgentLevel" class="field-hint">
              {{ t('agent.hierarchy.create_level_hint', { n: lvlLabel(createTargetAgentLevel) }) }}
              · {{ t('agent.hint.select_parent_for_level', { level: lvlLabel(createTargetAgentLevel - 1) }) }}
            </div>
          </el-form-item>
          <div v-if="createParentAgentId" class="create-meta-bar create-meta-bar--inline">
            <div class="create-meta-row">
              <span class="create-meta-label">{{ t('agent.field.available_credit') }}</span>
              <span class="create-meta-value c-green">{{ formatAmount(createParentAvailableCredit ?? '0') }}</span>
              <el-tag v-if="createTargetAgentLevel" size="small" type="info">L{{ createTargetAgentLevel }}</el-tag>
            </div>
          </div>
        </template>

        <el-form-item :label="t('user.col.username')" required>
          <el-input
            v-model="createForm.username"
            :placeholder="createAccountMode === 0 ? t('user.ph.username_player') : t('user.ph.username_unique')"
          />
        </el-form-item>
        <div class="create-form-pair">
          <el-form-item :label="t('user.field.password')" required>
            <el-input v-model="createForm.password" type="text" autocomplete="off" />
          </el-form-item>
          <el-form-item :label="t('user.field.confirm_password')" required>
            <el-input v-model="createForm.confirmPassword" type="text" autocomplete="off" />
          </el-form-item>
        </div>

        <el-form-item v-if="createAccountMode === 0 && createParentLocked" :label="t('user.filter.agent')">
          <el-tag type="info" class="account-type-tag">{{ resolveCreateParentLabel(createParentAgentId) }}</el-tag>
        </el-form-item>

        <el-form-item v-if="createAccountMode === 0 && !createParentLocked" :label="t('user.filter.agent')">
          <el-select v-model="createForm.parentId" :placeholder="t('user.ph.no_agent')" clearable style="width: 100%">
            <el-option v-for="a in agentOptions" :key="a.id" :label="agentOptionLabel(a)" :value="a.id" />
          </el-select>
        </el-form-item>

        <template v-if="createAccountMode === 1 || createAccountMode === 2">
          <el-form-item :label="t('agent.field.credit_limit')" required>
            <el-input-number
              v-model="createForm.creditLimit"
              :min="0"
              :max="createAccountMode === 2 ? createSubCreditMax : undefined"
              :step="1000"
              style="width: 100%"
            />
            <div v-if="createAccountMode === 2 && createParentAgentId" class="credit-quick-row">
              <div class="field-hint">
                {{ t('agent.hierarchy.create_credit_quick_hint', { amount: formatAmount(createParentAvailableCredit ?? '0') }) }}
              </div>
              <div class="credit-quick-btns">
                <el-button
                  v-for="ratio in creditQuickRatios"
                  :key="ratio"
                  size="small"
                  :disabled="createSubCreditMax <= 0"
                  @click="applyCreateSubAgentCreditRatio(ratio)"
                >
                  {{ ratio }}%
                </el-button>
              </div>
            </div>
            <div v-else class="field-hint">{{ t('agent.hint.credit_limit') }}</div>
          </el-form-item>
          <el-form-item :label="t('agent.field.cashback_rate')">
            <RatePercentInput v-model="createForm.cashbackRate" />
          </el-form-item>
          <el-form-item :label="t('agent.field.max_single_deposit')">
            <el-input-number v-model="createForm.maxSingleDeposit" :min="0" :step="100" style="width: 100%" />
          </el-form-item>
          <el-form-item :label="t('agent.field.max_daily_deposit')">
            <el-input-number v-model="createForm.maxDailyDeposit" :min="0" :step="1000" style="width: 100%" />
          </el-form-item>
        </template>

        <template v-if="createAccountMode === 0">
          <div class="create-form-pair">
            <el-form-item :label="t('user.field.phone')">
              <el-input v-model="createForm.phone" :placeholder="t('common.optional')" />
            </el-form-item>
            <el-form-item :label="t('user.field.email')">
              <el-input v-model="createForm.email" :placeholder="t('common.optional')" />
            </el-form-item>
          </div>
          <el-form-item :label="t('user.field.initial_balance')">
            <el-input-number v-model="createForm.initialDeposit" :min="0" :step="100" style="width: 100%" />
          </el-form-item>
          <el-form-item
            v-if="createForm.initialDeposit > 0"
            :label="t('user.field.initial_deposit_kind')"
          >
            <InitialDepositRemarkField
              v-model:kind="createForm.initialDepositRemarkKind"
              v-model:custom="createForm.initialDepositRemarkCustom"
              operator="admin"
            />
          </el-form-item>
        </template>

        <template v-else-if="createAccountMode === 1">
          <div class="create-form-pair">
            <el-form-item :label="t('user.field.phone')">
              <el-input v-model="createForm.phone" :placeholder="t('common.optional')" />
            </el-form-item>
            <el-form-item :label="t('user.field.email')">
              <el-input v-model="createForm.email" :placeholder="t('common.optional')" />
            </el-form-item>
          </div>
        </template>
      </el-form>
      <template #footer>
        <el-button @click="createVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button type="primary" :loading="createLoading" @click="submitCreate">{{ t('user.btn.create') }}</el-button>
      </template>
    </el-dialog>

    <!-- ── Edit Player ── -->
    <el-dialog
      v-model="editPlayerVisible"
      :title="canEditPlayerProfile ? t('user.dialog.edit') : t('user.field.reset_password')"
      width="560px"
      destroy-on-close
      class="user-edit-dialog"
    >
      <el-form label-width="84px" size="small" class="compact-edit-form">
        <div class="edit-meta">
          <span>ID {{ editPlayerForm.id }}</span>
          <el-tag :type="statusTagType(editPlayerForm.status)" size="small">{{ statusLabel(editPlayerForm.status) }}</el-tag>
        </div>

        <div class="edit-form-section">
          <div class="section-title">{{ t('user.section.basic_info') }}</div>
          <el-form-item :label="t('user.col.username')">
            <el-input
              v-model="editPlayerForm.username"
              :readonly="!canEditPlayerProfile"
              :placeholder="t('user.ph.username_player')"
            />
            <div class="field-hint">{{ t('user.hint.username_player') }}</div>
          </el-form-item>
        </div>

        <div v-if="!canEditPlayerProfile" class="edit-form-section">
          <div class="section-title">{{ t('user.section.affiliation') }}</div>
          <el-tag size="small" type="info" class="affiliation-tag">{{ affiliationLabel(editPlayerForm) }}</el-tag>
        </div>

        <div class="edit-form-section">
          <div class="password-mgmt-block">
            <div class="block-title">{{ t('user.section.password_mgmt') }}</div>
            <div class="password-field-row">
              <span class="password-field-label">{{ t('user.field.current_password') }}</span>
              <span v-if="editPlayerForm.managedPassword" class="password-plain">{{ editPlayerForm.managedPassword }}</span>
              <span v-else class="password-empty">—</span>
            </div>
            <p v-if="!editPlayerForm.managedPassword" class="field-hint block-hint">{{ t('user.hint.password_reset_to_view') }}</p>
            <div class="password-field-row">
              <span class="password-field-label">{{ t('user.field.reset_password') }}</span>
              <el-input v-model="editPlayerForm.newPassword" type="text" autocomplete="off" :placeholder="t('user.ph.reset_password_short')" />
            </div>
          </div>
        </div>

        <div v-if="canEditPlayerProfile" class="edit-form-section">
          <div class="section-title">{{ t('user.section.affiliation') }}</div>
          <el-form-item :label="t('user.col.agent')">
            <el-tag size="small" type="info" class="affiliation-tag">{{ affiliationLabel(editPlayerForm) }}</el-tag>
            <div class="field-hint">{{ t('user.hint.agent_readonly') }}</div>
          </el-form-item>
          <el-form-item :label="t('agent.field.cashback_rate')">
            <div class="cashback-edit-block">
              <el-checkbox v-model="editPlayerForm.useCustomCashback">
                {{ t('cashback.use_custom_rate') }}
              </el-checkbox>
              <RatePercentInput
                v-if="editPlayerForm.useCustomCashback"
                v-model="editPlayerForm.customCashbackRate"
              />
              <span v-else class="field-hint inline-hint">
                {{ `${editPlayerForm.defaultCashbackRate.toFixed(2)}%` }}
              </span>
            </div>
          </el-form-item>
        </div>

        <div v-if="canEditPlayerProfile" class="edit-form-section">
          <div class="section-title">{{ t('user.section.contact') }}</div>
          <el-row :gutter="12" class="contact-row">
            <el-col :span="12">
              <el-form-item :label="t('user.field.phone')">
                <el-input v-model="editPlayerForm.phone" :placeholder="t('common.optional')" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item :label="t('user.field.email')">
                <el-input v-model="editPlayerForm.email" :placeholder="t('common.optional')" />
              </el-form-item>
            </el-col>
          </el-row>
        </div>

        <div class="edit-form-section edit-stats-panel">
          <div class="section-title">{{ t('user.section.account_overview') }}</div>
          <AdminDetailGrid :columns="3">
            <AdminDetailItem :label="t('user.field.available')">{{ formatAmount(editPlayerForm.availableBalance) }}</AdminDetailItem>
            <AdminDetailItem :label="t('user.field.frozen_balance')">{{ formatAmount(editPlayerForm.frozenBalance) }}</AdminDetailItem>
            <AdminDetailItem :label="t('user.field.bets_summary')">
              {{ t('user.bets_edit_value', { n: editPlayerForm.betCount, stake: formatAmount(editPlayerForm.totalStake) }) }}
            </AdminDetailItem>
            <AdminDetailItem :label="t('user.field.total_payout')">{{ formatAmount(editPlayerForm.totalReturn) }}</AdminDetailItem>
            <AdminDetailItem :label="t('user.col.last_login')" :span="2">
              {{ editPlayerForm.lastLoginAt ? formatTime(editPlayerForm.lastLoginAt) : t('common.never_login') }}
              · {{ t('user.login_fail_value', { n: editPlayerForm.loginFailCount }) }}
            </AdminDetailItem>
          </AdminDetailGrid>
        </div>
      </el-form>
      <template #footer>
        <el-button size="small" @click="editPlayerVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button size="small" type="primary" :loading="editPlayerLoading" @click="submitEditPlayer">
          {{ canEditPlayerProfile ? t('user.btn.save_profile') : t('user.field.reset_password') }}
        </el-button>
      </template>
    </el-dialog>

    <!-- ── Edit Agent ── -->
    <el-dialog v-model="editAgentVisible" :title="t('agent.dialog.edit')" width="520px" destroy-on-close class="agent-edit-dialog">
      <el-form label-width="88px" size="small" class="compact-edit-form">
        <div class="edit-meta">
          <span>ID {{ editAgentForm.id }}</span>
          <el-tag :type="statusTagType(editAgentForm.status)" size="small">{{ statusLabel(editAgentForm.status) }}</el-tag>
          <el-tag size="small" type="info">L{{ editAgentForm.level }}</el-tag>
          <span v-if="editAgentForm.parentUsername" class="text-muted">{{ t('user.col.agent') }}: {{ editAgentForm.parentUsername }}</span>
        </div>
        <el-form-item :label="t('user.col.username')">
          <el-input v-model="editAgentForm.username" :placeholder="t('user.ph.username_unique')" />
        </el-form-item>
        <div class="password-mgmt-block">
          <div class="block-title">{{ t('user.section.password_mgmt') }}</div>
          <el-form-item :label="t('user.field.current_password')">
            <span v-if="editAgentForm.managedPassword" class="password-plain">{{ editAgentForm.managedPassword }}</span>
            <span v-else class="password-empty">—</span>
          </el-form-item>
          <p v-if="!editAgentForm.managedPassword" class="field-hint block-hint">{{ t('user.hint.password_reset_to_view') }}</p>
          <el-form-item :label="t('user.field.reset_password')">
            <el-input v-model="editAgentForm.newPassword" type="text" autocomplete="off" :placeholder="t('user.ph.reset_password_short')" />
          </el-form-item>
        </div>
        <el-form-item :label="t('user.field.account_status')">
          <el-radio-group v-model="editAgentForm.status">
            <el-radio value="ACTIVE">{{ t('user.status.ACTIVE') }}</el-radio>
            <el-radio value="SUSPENDED">{{ t('user.status.SUSPENDED') }}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item :label="t('agent.field.cashback_rate')">
          <RatePercentInput v-model="editAgentForm.cashbackRate" />
        </el-form-item>
        <el-form-item :label="t('agent.field.max_single_deposit')">
          <el-input-number v-model="editAgentForm.maxSingleDeposit" :min="0" :step="100" style="width: 100%" />
          <div class="field-hint">{{ t('agent.hint.deposit_limit_empty') }}</div>
        </el-form-item>
        <el-form-item :label="t('agent.field.max_daily_deposit')">
          <el-input-number v-model="editAgentForm.maxDailyDeposit" :min="0" :step="1000" style="width: 100%" />
          <div class="field-hint">{{ t('agent.hint.deposit_limit_empty') }}</div>
        </el-form-item>
        <el-form-item :label="t('user.field.phone')">
          <el-input v-model="editAgentForm.phone" :placeholder="t('common.optional')" />
        </el-form-item>
        <el-form-item :label="t('user.field.email')">
          <el-input v-model="editAgentForm.email" :placeholder="t('common.optional')" />
        </el-form-item>
        <el-descriptions :column="2" size="small" border class="edit-stats">
          <el-descriptions-item :label="t('agent.field.credit_limit')">{{ formatAmount(editAgentForm.creditLimit) }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.available_credit')">{{ formatAmount(editAgentForm.availableCredit) }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.used_credit')">{{ formatAmount(editAgentForm.usedCredit) }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.direct_players')">{{ editAgentForm.directPlayerCount }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.sub_agents')">{{ editAgentForm.childAgentCount }}</el-descriptions-item>
          <el-descriptions-item :label="t('user.col.last_login')" :span="2">
            {{ editAgentForm.lastLoginAt ? formatTime(editAgentForm.lastLoginAt) : t('common.never_login') }}
            · {{ t('user.login_fail_value', { n: editAgentForm.loginFailCount }) }}
          </el-descriptions-item>
        </el-descriptions>
      </el-form>
      <template #footer>
        <el-button size="small" @click="editAgentVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button size="small" type="primary" :loading="editAgentLoading" @click="submitEditAgent">{{ t('user.btn.save_profile') }}</el-button>
      </template>
    </el-dialog>

    <!-- ── Player transfer (deposit / withdraw) ── -->
    <el-dialog v-model="transferVisible" :title="transferTitle()" width="520px" destroy-on-close>
      <WalletTransferContext
        :context="transferContext"
        :mode="transferType"
        :loading="transferContextLoading"
      />
      <el-form label-width="88px">
        <el-form-item :label="t('common.col_id')">
          <span>{{ transferTarget?.id }}</span>
        </el-form-item>
        <el-form-item :label="t('user.field.amount')" :error="transferAmountCapError">
          <el-input-number
            v-model="transferAmount"
            :min="transferAmountRange.min"
            :disabled="transferAmountDisabled"
            :step="10"
            :precision="2"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item :label="t('user.field.remark')">
          <el-input v-model="transferRemark" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="transferVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button
          type="primary"
          :loading="transferLoading"
          :disabled="transferAmountDisabled || transferAmountExceedsCap"
          @click="submitTransfer"
        >
          {{ t('common.confirm') }}
        </el-button>
      </template>
    </el-dialog>

    <!-- ── Credit ── -->
    <el-dialog v-model="creditVisible" :title="t('agent.dialog.credit')" width="520px" destroy-on-close>
      <AgentCreditContext
        :context="creditContext"
        :loading="creditContextLoading"
        :adjust-amount="creditForm.amount"
      />
      <el-form label-width="88px">
        <el-form-item :label="t('agent.field.agent_id')">
          <el-input :model-value="editingId" disabled />
        </el-form-item>
        <el-form-item :label="t('agent.field.adjust_amount')">
          <el-input-number v-model="creditForm.amount" :step="1000" style="width: 100%" />
          <div class="field-hint">{{ t('agent.hint.credit_adjust') }}</div>
        </el-form-item>
        <el-form-item :label="t('user.field.remark')">
          <el-input v-model="creditForm.remark" :placeholder="t('agent.hint.credit_remark')" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="creditVisible = false">{{ t('common.cancel') }}</el-button>
        <el-button type="primary" :loading="creditLoading" @click="submitCredit">{{ t('agent.btn.confirm_adjust') }}</el-button>
      </template>
    </el-dialog>

    <!-- ── Player Detail ── -->
    <el-dialog v-model="detailPlayerVisible" :title="t('user.dialog.detail')" width="780px" destroy-on-close class="entity-detail-dialog">
      <template v-if="playerDetail">
        <AdminDetailGrid :columns="3">
          <AdminDetailItem :label="t('common.col_id')">{{ playerDetail.id }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.col.username')">{{ playerDetail.username }}</AdminDetailItem>
          <AdminDetailItem :label="t('common.status')">
            <el-tag :type="statusTagType(playerDetail.status)" size="small">{{ statusLabel(playerDetail.status) }}</el-tag>
          </AdminDetailItem>
          <AdminDetailItem :label="t('user.col.agent')">{{ affiliationLabel(playerDetail) }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.col.invite_code')">{{ playerDetail.inviteCode ?? '—' }}</AdminDetailItem>
          <AdminDetailItem :label="t('agent.field.cashback_rate')">
            {{ formatRatePercent(playerDetail.customCashbackRate ?? playerDetail.defaultCashbackRate) }}
          </AdminDetailItem>
          <AdminDetailItem :label="t('user.field.available')">
            {{ formatAmount(playerDetail.availableBalance) }}
            <span v-if="shouldCompact(playerDetail.availableBalance)" class="amount-full-hint">（{{ formatAmountFull(playerDetail.availableBalance) }}）</span>
          </AdminDetailItem>
          <AdminDetailItem :label="t('user.field.frozen_balance')">
            {{ formatAmount(playerDetail.frozenBalance) }}
            <span v-if="shouldCompact(playerDetail.frozenBalance)" class="amount-full-hint">（{{ formatAmountFull(playerDetail.frozenBalance) }}）</span>
          </AdminDetailItem>
          <AdminDetailItem :label="t('user.field.bet_count')">{{ playerDetail.betCount }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.total_stake')">{{ formatAmount(playerDetail.totalStake) }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.total_payout')">{{ formatAmount(playerDetail.totalReturn) }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.phone')">{{ playerDetail.phone ?? '—' }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.col.last_login')">
            {{ playerDetail.lastLoginAt ? formatTime(playerDetail.lastLoginAt) : t('common.never_login') }}
          </AdminDetailItem>
          <AdminDetailItem :label="t('user.field.login_fail')">{{ t('user.login_fail_value', { n: playerDetail.loginFailCount }) }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.email')">{{ playerDetail.email ?? '—' }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.registered_at')">{{ formatTime(playerDetail.createdAt) }}</AdminDetailItem>
          <AdminDetailItem :label="t('user.field.current_password')" :span="2">
            {{ playerDetail.managedPassword ?? '—' }}
            <span v-if="!playerDetail.managedPassword" class="admin-detail-hint">{{ t('user.hint.password_reset_to_view') }}</span>
          </AdminDetailItem>
        </AdminDetailGrid>
      </template>
    </el-dialog>

    <!-- ── Agent Detail ── -->
    <el-dialog v-model="detailAgentVisible" :title="t('agent.dialog.detail')" width="640px" destroy-on-close>
      <template v-if="agentDetail">
        <el-descriptions :column="2" border size="small" class="detail-block">
          <el-descriptions-item :label="t('common.col_id')">{{ agentDetail.userId }}</el-descriptions-item>
          <el-descriptions-item :label="t('user.col.username')">{{ agentDetail.username }}</el-descriptions-item>
          <el-descriptions-item :label="t('common.status')">
            <el-tag :type="statusTagType(agentDetail.status)" size="small">{{ statusLabel(agentDetail.status) }}</el-tag>
          </el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.level')">L{{ agentDetail.level }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.credit_limit')">
            {{ formatAmount(agentDetail.creditLimit) }}
            <span v-if="shouldCompact(agentDetail.creditLimit)" class="amount-full-hint">（{{ formatAmountFull(agentDetail.creditLimit) }}）</span>
          </el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.used_credit')">
            {{ formatAmount(agentDetail.usedCredit) }}
            <span v-if="shouldCompact(agentDetail.usedCredit)" class="amount-full-hint">（{{ formatAmountFull(agentDetail.usedCredit) }}）</span>
          </el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.available_credit')">
            {{ formatAmount(agentDetail.availableCredit) }}
            <span v-if="shouldCompact(agentDetail.availableCredit)" class="amount-full-hint">（{{ formatAmountFull(agentDetail.availableCredit) }}）</span>
          </el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.direct_players')">{{ agentDetail.directPlayerCount }} {{ t('common.people') }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.sub_agents')">{{ agentDetail.childAgentCount }} {{ t('common.people') }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.player_liability')">{{ formatAmount(agentDetail.directPlayerLiability) }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.field.sub_agent_exposure')">{{ formatAmount(agentDetail.childAgentExposure) }}</el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.cashback')">{{ formatRatePercent(agentDetail.cashbackRate) }}</el-descriptions-item>
          <el-descriptions-item :label="t('user.field.phone')">{{ agentDetail.phone ?? '—' }}</el-descriptions-item>
          <el-descriptions-item :label="t('user.field.email')">{{ agentDetail.email ?? '—' }}</el-descriptions-item>
          <el-descriptions-item :label="t('user.col.last_login')" :span="2">
            {{ agentDetail.lastLoginAt ? formatTime(agentDetail.lastLoginAt) : t('common.never_login') }}
          </el-descriptions-item>
          <el-descriptions-item :label="t('agent.col.created')" :span="2">{{ formatTime(agentDetail.createdAt) }}</el-descriptions-item>
        </el-descriptions>

        <InviteCodePanel :invite-code="agentDetail.inviteCode" compact readonly class="detail-invite" />

        <div class="section-title section-title--row">
          <span>{{ t('agent.section.credit_log') }}</span>
          <el-button
            v-if="agentDetail"
            link
            type="primary"
            @click="router.push({ path: '/finance-logs', query: { tab: 'credit', agentId: agentDetail.userId } })"
          >
            {{ t('agent.credit_tx.view_all') }}
          </el-button>
        </div>
        <el-table :data="agentDetail.recentCreditTransactions" size="small" stripe :empty-text="t('agent.col.no_records')">
          <el-table-column :label="t('agent.col.credit_type')" width="80">
            <template #default="{ row }">{{ creditTypeLabel(row.transactionType) }}</template>
          </el-table-column>
          <el-table-column :label="t('agent.col.credit_change')" width="96" align="right">
            <template #default="{ row }">
              <el-tooltip :content="formatAmountFull(row.amount)" placement="top">
                <span>{{ formatAmount(row.amount) }}</span>
              </el-tooltip>
            </template>
          </el-table-column>
          <el-table-column :label="t('agent.col.credit_after')" width="96" align="right">
            <template #default="{ row }">
              <el-tooltip :content="formatAmountFull(row.creditAfter)" placement="top">
                <span>{{ formatAmount(row.creditAfter) }}</span>
              </el-tooltip>
            </template>
          </el-table-column>
          <el-table-column prop="remark" :label="t('user.field.remark')" min-width="120" show-overflow-tooltip />
          <el-table-column :label="t('audit.col.time')" min-width="150">
            <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
          </el-table-column>
        </el-table>
      </template>
    </el-dialog>

    <PlayerWalletLedgerDialog
      v-model="walletLedgerVisible"
      :player-id="walletLedgerPlayerId"
      :player-username="walletLedgerPlayerUsername"
    />
  </div>
</template>

<style scoped>
.agent-mgr-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.agent-mgr-shell > :deep(.agent-direct-players-page),
.agent-mgr-shell > :deep(.global-settings-page) {
  flex: 1;
  min-height: 0;
}

.compact-agent-table :deep(.row-navigable) {
  cursor: pointer;
}

.mgr-tabs-shell {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.mgr-top-tabs--with-actions :deep(.el-tabs__header) {
  padding-right: 248px;
}

.mgr-toolbar-actions {
  position: absolute;
  right: 16px;
  top: 14px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
}

.settings-toolbar-btn {
  min-width: 96px;
  height: 38px;
  padding: 0 16px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 8px;
}

.invite-prominent-btn {
  position: static;
  min-width: 96px;
  height: 38px;
  padding: 0 22px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.06em;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(64, 158, 255, 0.28);
}

.agent-mgr-page > .mgr-tabs-shell {
  flex: 1;
  min-height: 0;
}

.mgr-tabs-shell .mgr-top-tabs {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.agent-mgr-page > .agent-list-panel,
.agent-mgr-page > .mgr-tabs-shell .mgr-top-tabs {
  flex: 1;
  min-height: 0;
}
.agent-mgr-page > .mgr-tabs-shell .mgr-top-tabs :deep(.el-tabs__content) {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.agent-mgr-page > .mgr-tabs-shell .mgr-top-tabs :deep(.el-tab-pane) {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.player-list-panel,
.agent-list-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.mgr-top-tabs :deep(.el-tabs__item) {
  font-size: 14px;
  font-weight: 700;
}

.freeze-agent-intro {
  margin: 0 0 16px;
  line-height: 1.5;
  color: var(--el-text-color-regular);
}

.freeze-agent-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ─── Table toolbar（间距见 App.vue 全局 .list-panel-toolbar） ─── */
.list-panel-toolbar .list-chrome__grow {
  flex: 1 1 280px;
  min-width: 0;
  flex-wrap: wrap;
  row-gap: 8px;
}
.list-panel-toolbar .list-chrome__actions {
  flex-shrink: 0;
}

/* ─── Expansion ─── */
.expand-panel {
  margin: 4px 0 8px;
  padding: 12px 14px 14px;
  background: #141414;
  border: 1px solid #2a2a2a;
  border-radius: 8px;
}
.expand-loading {
  text-align: center;
  padding: 16px;
  color: #888;
  font-size: 13px;
}
.expand-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.expand-section-title {
  font-size: 13px;
  font-weight: 600;
  color: #888;
}
.expandable-table :deep(.row-expandable) {
  cursor: pointer;
}
.compact-agent-table :deep(.el-table__header .el-table__cell) {
  padding: 4px 8px;
}
.compact-agent-table :deep(.el-table__body .el-table__cell) {
  padding: 6px 8px;
}
.compact-agent-table :deep(.el-table__header .cell) {
  line-height: 1.25;
  white-space: nowrap;
  font-size: 12px;
  padding: 0;
}
.compact-agent-table :deep(.el-table__body .cell) {
  padding: 0;
  line-height: 1.35;
  font-size: 13px;
}
.compact-agent-table :deep(.el-tag) {
  max-width: 100%;
}
.nested-table {
  margin-bottom: 4px;
}
.inner-tabs {
  margin-top: 0;
}
.inner-tabs :deep(.el-tabs__header) {
  margin-bottom: 4px;
}
.inner-tabs :deep(.el-tabs__nav-wrap) {
  margin-bottom: 0;
}
.inner-tabs :deep(.el-tabs__item) {
  height: 34px;
  line-height: 34px;
  font-size: 13px;
  font-weight: 500;
  color: #888;
  padding: 0 14px;
}
.inner-tabs :deep(.el-tabs__item.is-active) {
  color: #d4fde5;
  font-weight: 700;
}
.inner-tabs :deep(.el-tabs__active-bar) {
  background-color: var(--gold-bright);
  height: 2px;
}
.inner-tabs :deep(.el-tabs__content) {
  padding: 0;
}
.inner-table {
  border-radius: 6px;
}
.inner-table :deep(.el-table__cell) {
  font-size: 13px;
}
.account-type-tag {
  font-size: 13px;
  font-weight: 600;
}
.player-list-panel .table-wrap :deep(.el-table),
.agent-list-panel .table-wrap :deep(.el-table) {
  min-width: 880px;
}

.expand-panel .inner-table {
  width: 100%;
}

.expand-panel :deep(.el-table__body-wrapper) {
  overflow-x: auto;
}

@media (max-width: 900px) {
  .mgr-top-tabs--with-actions :deep(.el-tabs__header) {
    padding-right: 0;
  }

  .mgr-toolbar-actions {
    position: static;
    align-self: flex-end;
    margin: 0 0 8px;
  }
}

/* ─── Inherited from old pages ─── */
.settings-form :deep(.el-form-item) {
  margin-bottom: 0;
  margin-right: 12px;
}
.field-hint { font-size: 12px; color: #888; margin-top: 4px; }
:deep(.create-account-dialog .el-dialog__body) {
  max-height: none !important;
  overflow: visible !important;
  padding: 14px 20px 6px;
}
:deep(.create-account-dialog .el-dialog__footer) {
  padding-top: 4px;
}
.compact-create-form :deep(.el-form-item) {
  margin-bottom: 10px;
}
.compact-create-form :deep(.el-form-item__label) {
  font-size: 13px;
  white-space: nowrap;
}
.credit-quick-row {
  margin-top: 2px;
}
.credit-quick-btns {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.create-meta-bar {
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid #2a2a2a;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}
.create-meta-bar--inline {
  margin-top: -4px;
}
.create-meta-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  line-height: 1.4;
}
.create-meta-row + .create-meta-row {
  margin-top: 6px;
}
.create-meta-label {
  flex-shrink: 0;
  color: #888;
}
.create-meta-value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #ddd;
  font-weight: 600;
}
.create-meta-row .c-green {
  font-size: 14px;
}
.create-form-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 10px;
}
.c-green { color: var(--gold-text); }
.amount-compact { white-space: nowrap; font-variant-numeric: tabular-nums; cursor: default; }
.invite-code-cell {
  font-family: ui-monospace, monospace;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--el-color-primary);
}

.affiliation-tag {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.amount-full-hint { font-size: 11px; color: #666; margin-left: 4px; }
.text-muted { color: #666; font-size: 12px; }
.list-settings-block--danger {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed rgba(245, 108, 108, 0.35);
}
.list-settings-hint {
  font-size: 12px;
  color: #888;
  margin: 0 0 10px;
  line-height: 1.5;
}
.list-settings-unit {
  margin-left: 4px;
  font-size: 12px;
  color: #888;
}
.reset-db-alert { margin-bottom: 10px; }
.detail-block { margin-bottom: 16px; }
.section-title {
  font-size: 13px;
  font-weight: 600;
  color: #666;
  margin-bottom: 8px;
}

.section-title--row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.expand-panel {
  background: #fbfaf7;
  border-color: var(--border);
}

.expand-loading,
.expand-section-title,
.inner-tabs :deep(.el-tabs__item),
.field-hint,
.create-meta-label,
.amount-full-hint,
.text-muted,
.list-settings-hint,
.list-settings-unit,
.section-title {
  color: var(--text-muted);
}

.inner-tabs :deep(.el-tabs__item.is-active) {
  color: var(--text);
}

.inner-tabs :deep(.el-tabs__active-bar) {
  background-color: var(--primary);
}

.create-meta-bar {
  border-color: var(--border);
  background: #fbfaf7;
}

.create-meta-value {
  color: var(--text);
}

.c-green,
.create-meta-row .c-green {
  color: var(--success-text);
}

.invite-code-cell {
  color: var(--primary-link);
}

.list-settings-block--danger {
  border-top-color: var(--danger-border);
}

@media (max-width: 760px) {
  .list-panel-toolbar,
  .list-panel-toolbar .list-chrome__grow,
  .list-panel-toolbar .list-chrome__actions {
    width: 100%;
  }

  .create-form-pair {
    grid-template-columns: 1fr;
  }
}
</style>
