"use client";

import { Eye, Pencil, Trash2 } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useExportLabels } from "@/hooks/use-export-labels";
import { useTranslation } from "react-i18next";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { toast } from "sonner";

import { getAgentNodeProfile, getAgentTree } from "@/api/admin-agents";
import {
  deleteAdminPlayer,
  getAdminPlayer,
  getAdminPlayers,
  postAdminPlayer,
  postAdminPlayerFreeze,
  postAdminPlayerUnfreeze,
  putAdminPlayer,
} from "@/api/admin-player";
import { PlayerCreditLimitField } from "@/components/admin/player-credit-limit-field";
import { flattenAgentTree, type FlatAgentOption } from "@/lib/admin-agent-tree";
import { useAdminSiteCodeOptions } from "@/hooks/use-admin-site-code-options";
import { AdminAgentCell, AdminAgentHead } from "@/components/admin/admin-agent-columns";
import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
import { AdminListPaginationFooter } from "@/components/admin/admin-list-pagination-footer";
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
import { AdminTableMoney } from "@/components/admin/admin-table-money";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { ConfirmableSwitch } from "@/components/admin/confirmable-switch";
import { resolvePlayerStatusTone } from "@/lib/admin-status-tone";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { PRD_PLAYER_FREEZE_MANAGE, PRD_USERS_MANAGE } from "@/lib/admin-prd";
import { isSiteAdminOperator } from "@/lib/admin-session-variants";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog";
import { adminPlayerDetailPath } from "@/lib/admin-player-paths";
import { useAdminProfile } from "@/stores/admin-session";
import { PlayerFundingModeBadge } from "@/components/admin/player-funding-badges";
import {
  isCreditFundingPlayer,
  playerBalanceCells,
} from "@/lib/admin-player-display";
import {
  validateNativePlayerPassword,
  validateNativePlayerUsername,
} from "@/lib/admin-input-validation";
import { ADMIN_SELECT_FILTER_ALL, adminSiteSelectLabel } from "@/lib/admin-select-display";
import { formatAdminMinorUnits } from "@/lib/money";
import { LotteryApiBizError } from "@/types/api/errors";
import type { AdminPlayerRow } from "@/types/api/admin-player";

function playerStatusLabelT(status: number, t: (key: string) => string): string {
  if (status === 0) return t("statusNormal");
  if (status === 1) return t("statusFrozen");
  if (status === 2) return t("statusBanned");
  return String(status);
}

const PLAYER_STATUS_OPTIONS = [
  { value: 0, label: "statusNormal" },
  { value: 1, label: "statusFrozen" },
  { value: 2, label: "statusBanned" },
];

type PlayerFundingModeChoice = "wallet" | "credit";

function parseRiskTagsInput(text: string): string[] {
  return Array.from(
    new Set(
      text
        .split(/[,，\s]+/)
        .map((tag) => tag.trim())
        .filter((tag) => tag.length > 0),
    ),
  );
}

export function PlayersConsole(): React.ReactElement {
  const { t } = useTranslation(["players", "common"]);
  const tRef = useTranslationRef(["players", "common"]);
  const { request: requestConfirm, ConfirmDialog, busy: confirmBusy } = useConfirmAction();
  const formatDt = useAdminDateTimeFormatter();
  const exportLabels = useExportLabels("players");
  const profile = useAdminProfile();
  const isSuperAdmin = profile?.is_super_admin === true;
  const isSiteAdmin = isSiteAdminOperator(profile);
  const boundAgent = profile?.agent ?? null;
  const { sites: siteOptions, canChooseSite } = useAdminSiteCodeOptions();
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const keywordFromUrl = (searchParams.get("keyword") ?? "").trim();
  useAdminCurrencyCatalog();
  const canManagePlayers = adminHasAnyPermission(profile?.permissions, [PRD_USERS_MANAGE]);
  const canPickAgentOnCreate = isSuperAdmin || isSiteAdmin;
  const canFreezePlayers = adminHasAnyPermission(profile?.permissions, [PRD_PLAYER_FREEZE_MANAGE]);
  const [page, setPage] = useState(1);
  const [perPage, setPerPage] = useState(10);
  const [keyword, setKeyword] = useState(keywordFromUrl);
  const [query, setQuery] = useState(keywordFromUrl);
  const [siteFilter, setSiteFilter] = useState("");

  const [items, setItems] = useState<AdminPlayerRow[]>([]);
  const [total, setTotal] = useState(0);
  const [lastPage, setLastPage] = useState(1);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);

  const [accountOpen, setAccountOpen] = useState(false);
  const [accountMode, setAccountMode] = useState<"create" | "edit">("create");
  const [accountSaving, setAccountSaving] = useState(false);
  const [editingAccountId, setEditingAccountId] = useState<number | null>(null);
  const [formSiteCode, setFormSiteCode] = useState("");
  const [formSitePlayerId, setFormSitePlayerId] = useState("");
  const [formUsername, setFormUsername] = useState("");
  const [formNickname, setFormNickname] = useState("");
  const [formDefaultCurrency, setFormDefaultCurrency] = useState("NPR");
  const [formStatus, setFormStatus] = useState(0);
  const [formRiskTags, setFormRiskTags] = useState("");
  const [formAgentNodeId, setFormAgentNodeId] = useState<number | undefined>(undefined);
  const [formFundingMode, setFormFundingMode] = useState<PlayerFundingModeChoice>("wallet");
  const [formPassword, setFormPassword] = useState("");
  const [formCreditLimit, setFormCreditLimit] = useState("");
  const [parentAvailableCredit, setParentAvailableCredit] = useState<number | null>(null);
  const [accountDetailLoading, setAccountDetailLoading] = useState(false);
  const [createAgentOptions, setCreateAgentOptions] = useState<FlatAgentOption[]>([]);
  const [createAgentLoading, setCreateAgentLoading] = useState(false);

  const [deleteTarget, setDeleteTarget] = useState<AdminPlayerRow | null>(null);
  const [deleteBusy, setDeleteBusy] = useState(false);
  const [freezeBusyId, setFreezeBusyId] = useState<number | null>(null);

  const editingPlayer = useMemo(
    () => items.find((p) => p.id === editingAccountId) ?? null,
    [items, editingAccountId],
  );

  const effectiveAgentNodeId =
    accountMode === "edit" &&
    editingPlayer?.agent_node_id != null &&
    editingPlayer.agent_node_id > 0
      ? editingPlayer.agent_node_id
      : formAgentNodeId != null && formAgentNodeId > 0
        ? formAgentNodeId
        : (boundAgent?.id ?? null);

  const editingUsesCredit = editingPlayer != null && isCreditFundingPlayer(editingPlayer);

  const showSiteFilter =
    isSuperAdmin || (profile?.accessible_sites?.length ?? 0) > 1;

  const scopeHint = useMemo(() => {
    if (isSuperAdmin) {
      return siteFilter.trim() !== ""
        ? t("scopeFilteredSite", { site: siteFilter.trim() })
        : t("scopeAllSites");
    }
    if (boundAgent) {
      return t("scopeAgentLine", {
        site: boundAgent.site_code,
        name: boundAgent.name,
      });
    }
    const sites = profile?.accessible_sites ?? [];
    if (sites.length === 1) {
      return t("scopeSingleSite", { site: sites[0].code });
    }
    if (sites.length > 1) {
      return siteFilter.trim() !== ""
        ? t("scopeFilteredSite", { site: siteFilter.trim() })
        : t("scopeMultiSite", { count: sites.length });
    }
    return "";
  }, [boundAgent, isSuperAdmin, profile?.accessible_sites, siteFilter, t]);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const scopedSite = siteFilter.trim();
      const data = await getAdminPlayers({
        page,
        per_page: perPage,
        keyword: query.trim() || undefined,
        ...(scopedSite !== "" ? { site_code: scopedSite } : {}),
      });
      setItems(data.items);
      setTotal(data.meta.total);
      setLastPage(Math.max(1, data.meta.last_page));
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : tRef.current("loadFailed");
      setErr(msg);
      setItems([]);
      setTotal(0);
      setLastPage(1);
    } finally {
      setLoading(false);
    }
  }, [page, perPage, query, siteFilter, tRef]);

  useAsyncEffect(() => {
    void load();
  }, [page, perPage, query, siteFilter]);

  useAsyncEffect(() => {
    setKeyword(keywordFromUrl);
    setQuery(keywordFromUrl);
    setPage(1);
  }, [keywordFromUrl]);

  function openCreateAccount(): void {
    setAccountMode("create");
    setEditingAccountId(null);
    const agentSite = boundAgent?.site_code?.trim() ?? "";
    const siteAdminSite = profile?.site?.code?.trim() ?? "";
    const defaultSite =
      agentSite !== ""
        ? agentSite
        : siteAdminSite !== ""
          ? siteAdminSite
          : siteOptions[0]?.code ?? "";
    setFormSiteCode(defaultSite);
    setFormAgentNodeId(boundAgent?.id);
    setFormSitePlayerId("");
    setFormUsername("");
    setFormPassword("");
    setFormNickname("");
    setFormFundingMode("wallet");
    setFormCreditLimit("");
    setParentAvailableCredit(null);
    setFormDefaultCurrency("NPR");
    setFormStatus(0);
    setFormRiskTags("");
    setAccountOpen(true);
  }

  useAsyncEffect(() => {
    if (!accountOpen || accountMode !== "create" || !canPickAgentOnCreate) {
      return;
    }
    const siteCode = formSiteCode.trim();
    const site = siteOptions.find((s) => s.code === siteCode);
    if (!site) {
      setCreateAgentOptions([]);
      setFormAgentNodeId(undefined);
      return;
    }

    let cancelled = false;
    setCreateAgentLoading(true);
    getAgentTree(site.id)
      .then((data) => {
        if (cancelled) {
          return;
        }
        const flat = flattenAgentTree(data.tree);
        setCreateAgentOptions(flat);
        const root = flat.find((o) => o.depth === 0) ?? flat[0];
        setFormAgentNodeId((prev) => {
          if (prev !== undefined && flat.some((o) => o.id === prev)) {
            return prev;
          }
          return root?.id;
        });
      })
      .catch(() => {
        if (!cancelled) {
          setCreateAgentOptions([]);
          setFormAgentNodeId(undefined);
        }
      })
      .finally(() => {
        if (!cancelled) {
          setCreateAgentLoading(false);
        }
      });

    return () => {
      cancelled = true;
    };
  }, [accountOpen, accountMode, canPickAgentOnCreate, formSiteCode, siteOptions]);

  useAsyncEffect(() => {
    const needsCreditCap =
      accountOpen &&
      ((accountMode === "create" && formFundingMode === "credit") ||
        (accountMode === "edit" && editingUsesCredit));
    if (!needsCreditCap || effectiveAgentNodeId === null) {
      if (!needsCreditCap) {
        setParentAvailableCredit(null);
      }
      return;
    }

    let cancelled = false;
    void getAgentNodeProfile(effectiveAgentNodeId)
      .then((profile) => {
        if (!cancelled) {
          setParentAvailableCredit(profile.available_credit ?? null);
        }
      })
      .catch(() => {
        if (!cancelled) {
          setParentAvailableCredit(null);
        }
      });

    return () => {
      cancelled = true;
    };
  }, [
    accountMode,
    accountOpen,
    editingUsesCredit,
    effectiveAgentNodeId,
    formFundingMode,
  ]);

  function applyEditFormFromPlayer(row: AdminPlayerRow): void {
    setFormSiteCode(row.site_code);
    setFormSitePlayerId(row.site_player_id);
    setFormUsername(row.username ?? "");
    setFormNickname(row.nickname ?? "");
    setFormDefaultCurrency(row.default_currency);
    setFormStatus(row.status);
    setFormRiskTags((row.risk_tags ?? []).join(", "));
    setFormCreditLimit(String(row.credit_limit ?? 0));
  }

  function openEditAccount(row: AdminPlayerRow): void {
    setAccountMode("edit");
    setEditingAccountId(row.id);
    applyEditFormFromPlayer(row);
    setAccountOpen(true);
    setAccountDetailLoading(true);
    void getAdminPlayer(row.id)
      .then((full) => {
        setItems((prev) => prev.map((item) => (item.id === full.id ? full : item)));
        applyEditFormFromPlayer(full);
      })
      .catch(() => {
        toast.error(t("loadFailed"));
      })
      .finally(() => {
        setAccountDetailLoading(false);
      });
  }

  function handleAccountDialogOpenChange(open: boolean): void {
    setAccountOpen(open);
    if (!open) {
      setEditingAccountId(null);
    }
  }

  async function submitAccount(): Promise<void> {
    if (accountMode === "create") {
      if (!isSuperAdmin && !boundAgent && !isSiteAdmin) {
        toast.error(t("createAgentRequired"));
        return;
      }
      if (formSiteCode.trim() === "") {
        toast.error(t("siteCodeRequired"));
        return;
      }
      if (canPickAgentOnCreate && (formAgentNodeId === undefined || formAgentNodeId <= 0)) {
        toast.error(t("createAgentRequired"));
        return;
      }

      const isCreditCreate = formFundingMode === "credit";
      if (!isCreditCreate && formSitePlayerId.trim() === "") {
        toast.error(t("sitePlayerIdRequired"));
        return;
      }

      if (isCreditCreate) {
        if (formUsername.trim() === "" || formPassword.trim() === "") {
          toast.error(t("createLoginRequired"));
          return;
        }
        const usernameIssue = validateNativePlayerUsername(formUsername);
        if (usernameIssue === "invalid_charset") {
          toast.error(t("usernameInvalidCharset"));
          return;
        }
        const passwordIssue = validateNativePlayerPassword(formPassword);
        if (passwordIssue === "too_short") {
          toast.error(t("passwordMinLength"));
          return;
        }
      }

      const parsedCreditLimit =
        formCreditLimit.trim() === "" ? 0 : Number.parseInt(formCreditLimit, 10);
      if (
        isCreditCreate &&
        (Number.isNaN(parsedCreditLimit) ||
          parsedCreditLimit < 0 ||
          !Number.isInteger(parsedCreditLimit))
      ) {
        toast.error(t("creditLimitInvalid"));
        return;
      }
      if (
        isCreditCreate &&
        effectiveAgentNodeId !== null &&
        parentAvailableCredit === null
      ) {
        toast.error(t("creditCapLoading"));
        return;
      }
      if (
        isCreditCreate &&
        parentAvailableCredit !== null &&
        parsedCreditLimit > parentAvailableCredit
      ) {
        toast.error(t("creditLimitExceeded"));
        return;
      }

      setAccountSaving(true);
      try {
        const created = await postAdminPlayer({
          site_code: formSiteCode.trim(),
          ...(formSitePlayerId.trim() !== "" ? { site_player_id: formSitePlayerId.trim() } : {}),
          username: isCreditCreate ? formUsername.trim() : formUsername.trim() || null,
          ...(isCreditCreate ? { password: formPassword } : {}),
          nickname: formNickname.trim() || null,
          default_currency: formDefaultCurrency,
          status: formStatus,
          ...(canPickAgentOnCreate && formAgentNodeId
            ? { agent_node_id: formAgentNodeId }
            : {}),
          ...(isCreditCreate ? { credit_limit: parsedCreditLimit } : {}),
        });
        setItems((prev) => [created, ...prev]);
        setTotal((t) => t + 1);
        toast.success(
          isCreditCreate
            ? t("createSuccessNative", { name: created.username ?? created.site_player_id })
            : t("createSuccess", { name: created.username ?? created.site_player_id }),
        );
        handleAccountDialogOpenChange(false);
      } catch (e) {
        const msg = e instanceof LotteryApiBizError ? e.message : t("createFailed");
        toast.error(msg);
      } finally {
        setAccountSaving(false);
      }
    } else {
      const id = editingAccountId;
      if (id === null) return;

      const body: Parameters<typeof putAdminPlayer>[1] = {};
      if (formUsername.trim() !== "") {
        body.username = formUsername.trim();
      }
      if (formNickname !== editingPlayer?.nickname) {
        body.nickname = formNickname.trim() || null;
      }
      if (formDefaultCurrency !== editingPlayer?.default_currency) {
        body.default_currency = formDefaultCurrency.trim().toUpperCase();
      }
      if (formStatus !== editingPlayer?.status) {
        body.status = formStatus;
      }
      const nextRiskTags = parseRiskTagsInput(formRiskTags);
      const prevRiskTags = editingPlayer?.risk_tags ?? [];
      if (JSON.stringify(nextRiskTags) !== JSON.stringify(prevRiskTags)) {
        body.risk_tags = nextRiskTags;
      }

      if (editingUsesCredit) {
        const baselineCredit = editingPlayer?.credit_limit ?? 0;
        const parsedCreditLimit =
          formCreditLimit.trim() === "" ? baselineCredit : Number.parseInt(formCreditLimit, 10);
        if (
          Number.isNaN(parsedCreditLimit) ||
          parsedCreditLimit < 0 ||
          !Number.isInteger(parsedCreditLimit)
        ) {
          toast.error(t("creditLimitInvalid"));
          return;
        }
        if (
          parsedCreditLimit !== baselineCredit &&
          effectiveAgentNodeId !== null &&
          parentAvailableCredit === null
        ) {
          toast.error(t("creditCapLoading"));
          return;
        }
        if (
          parentAvailableCredit !== null &&
          parsedCreditLimit > baselineCredit + parentAvailableCredit
        ) {
          toast.error(t("creditLimitExceeded"));
          return;
        }
        if (parsedCreditLimit !== baselineCredit) {
          body.credit_limit = parsedCreditLimit;
        }
      }

      if (Object.keys(body).length === 0) {
        toast.success(t("noChanges"));
        handleAccountDialogOpenChange(false);
        return;
      }

      setAccountSaving(true);
      try {
        const updated = await putAdminPlayer(id, body);
        setItems((prev) => prev.map((row) => (row.id === updated.id ? updated : row)));
        toast.success(t("updateSuccess", { name: updated.username ?? updated.site_player_id }));
        handleAccountDialogOpenChange(false);
      } catch (e) {
        const msg = e instanceof LotteryApiBizError ? e.message : t("updateFailed");
        toast.error(msg);
      } finally {
        setAccountSaving(false);
      }
    }
  }

  async function toggleFreeze(row: AdminPlayerRow, freeze: boolean): Promise<void> {
    setFreezeBusyId(row.id);
    try {
      const updated = freeze
        ? await postAdminPlayerFreeze(row.id)
        : await postAdminPlayerUnfreeze(row.id);
      setItems((prev) => prev.map((r) => (r.id === updated.id ? updated : r)));
      const name = updated.username ?? updated.site_player_id;
      toast.success(freeze ? t("freezeSuccess", { name }) : t("unfreezeSuccess", { name }));
    } catch (e) {
      const msg =
        e instanceof LotteryApiBizError
          ? e.message
          : freeze
            ? t("freezeFailed")
            : t("unfreezeFailed");
      toast.error(msg);
    } finally {
      setFreezeBusyId(null);
    }
  }

  async function confirmDelete(): Promise<void> {
    if (!deleteTarget) return;
    setDeleteBusy(true);
    try {
      await deleteAdminPlayer(deleteTarget.id);
      setItems((prev) => prev.filter((r) => r.id !== deleteTarget.id));
      setTotal((t) => Math.max(0, t - 1));
      toast.success(t("deleteSuccess", { name: deleteTarget.username ?? deleteTarget.site_player_id }));
      setDeleteTarget(null);
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : t("deleteFailed");
      toast.error(msg);
    } finally {
      setDeleteBusy(false);
    }
  }

  return (
    <div className="flex w-full max-w-none flex-col gap-6">
      <Card className="admin-list-card">
        <CardHeader className="admin-list-header flex flex-col gap-4">
          <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
            <CardTitle className="admin-list-title">{t("listTitle")}</CardTitle>
            {canManagePlayers ? (
              <Button type="button" size="sm" onClick={() => openCreateAccount()}>
                {t("createPlayer")}
              </Button>
            ) : null}
          </div>
          {scopeHint ? (
            <p className="text-xs text-muted-foreground">{scopeHint}</p>
          ) : null}
          <div className="admin-list-toolbar">
            {showSiteFilter ? (
              <div className="admin-list-field">
                <Label htmlFor="player-site-filter" className="sm:w-20 sm:shrink-0">
                  {t("filterSite")}
                </Label>
                <Select
                  value={siteFilter || ADMIN_SELECT_FILTER_ALL}
                  onValueChange={(value) => {
                    setSiteFilter(
                      value == null || value === ADMIN_SELECT_FILTER_ALL ? "" : value,
                    );
                    setPage(1);
                  }}
                >
                  <SelectTrigger id="player-site-filter" className="w-full sm:w-[12rem]">
                    <SelectValue>
                      {(v) =>
                        adminSiteSelectLabel(
                          v,
                          isSuperAdmin ? siteOptions : profile?.accessible_sites ?? [],
                          t("filterAllSites"),
                        )
                      }
                    </SelectValue>
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value={ADMIN_SELECT_FILTER_ALL}>{t("filterAllSites")}</SelectItem>
                    {(isSuperAdmin ? siteOptions : profile?.accessible_sites ?? []).map((site) => (
                      <SelectItem key={site.code} value={site.code}>
                        {site.name ? `${site.name} (${site.code})` : site.code}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
            ) : null}
            <div className="admin-list-field xl:min-w-0">
              <Label htmlFor="player-search" className="sm:w-20 sm:shrink-0">
                {t("search")}
              </Label>
              <Input
                id="player-search"
                value={keyword}
                className="w-full sm:w-[18rem] xl:w-[24rem]"
                placeholder={t("searchPlaceholder")}
                onChange={(e) => setKeyword(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter") {
                    setPage(1);
                    setQuery(keyword.trim());
                    const nextParams = new URLSearchParams(searchParams.toString());
                    if (keyword.trim()) {
                      nextParams.set("keyword", keyword.trim());
                    } else {
                      nextParams.delete("keyword");
                    }
                    const queryString = nextParams.toString();
                    router.replace(`${pathname}${queryString ? `?${queryString}` : ""}`, { scroll: false });
                  }
                }}
              />
            </div>
            <div className="admin-list-actions">
              <AdminTableExportButton
                tableId="players-table"
                filename={exportLabels.filename}
              sheetName={exportLabels.sheetName}
              />
              <Button
                type="button"
                onClick={() => {
                  setPage(1);
                  setQuery(keyword.trim());
                  const nextParams = new URLSearchParams(searchParams.toString());
                  if (keyword.trim()) {
                    nextParams.set("keyword", keyword.trim());
                  } else {
                    nextParams.delete("keyword");
                  }
                  const queryString = nextParams.toString();
                  router.replace(`${pathname}${queryString ? `?${queryString}` : ""}`, { scroll: false });
                }}
              >
                {t("search")}
              </Button>
              <Button type="button" variant="secondary" onClick={() => void load()}>
                {t("refresh")}
              </Button>
            </div>
          </div>
        </CardHeader>
        <CardContent className="admin-list-content">
          {err ? <p className="text-sm text-destructive">{err}</p> : null}
          <div className="admin-table-shell">
            <Table id="players-table">
              <TableHeader>
                <TableRow>
                  <TableHead className="w-16">{t("table.id", { ns: "common" })}</TableHead>
                  <TableHead>{t("site")}</TableHead>
                  <TableHead className="whitespace-nowrap">{t("fundingMode")}</TableHead>
                  <TableHead className="whitespace-nowrap text-center">{t("balance")}</TableHead>
                  <TableHead className="whitespace-nowrap text-center">{t("available")}</TableHead>
                  <TableHead className="w-20 whitespace-nowrap">{t("status")}</TableHead>
                  <AdminAgentHead />
                  <TableHead>{t("sitePlayerId")}</TableHead>
                  <TableHead>{t("username")}</TableHead>
                  <TableHead>{t("nickname")}</TableHead>
                  <TableHead className="whitespace-nowrap">{t("riskTags", { defaultValue: "风控标签" })}</TableHead>
                  <TableHead className="whitespace-nowrap">{t("currency")}</TableHead>
                  <TableHead className="whitespace-nowrap">{t("lastLogin")}</TableHead>
                  <TableHead className="sticky right-0 z-20 bg-muted w-14 text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">{t("table.actions", { ns: "common" })}</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {loading && items.length === 0 ? (
                  <AdminTableLoadingRow colSpan={14} />
                ) : items.length === 0 ? (
                  <AdminTableNoResourceRow colSpan={14} className="text-muted-foreground" />
                ) : (
                  items.map((row) => {
                    const balances = playerBalanceCells(row, formatAdminMinorUnits);
                    const riskTags = row.risk_tags ?? [];
                    return (
                    <TableRow key={row.id}>
                      <TableCell className="tabular-nums">#{row.id}</TableCell>
                      <TableCell>
                        <span className="font-mono text-xs">{row.site_code}</span>
                      </TableCell>
                      <TableCell>
                        <PlayerFundingModeBadge row={row} />
                      </TableCell>
                      <TableCell className="text-center text-xs">
                        <AdminTableMoney>{balances.balance}</AdminTableMoney>
                      </TableCell>
                      <TableCell className="text-center text-xs">
                        <AdminTableMoney>{balances.available}</AdminTableMoney>
                      </TableCell>
                      <TableCell>
                        {row.status === 2 ? (
                          <div className="flex justify-center">
                            <AdminStatusBadge status={row.status} tone={resolvePlayerStatusTone(row.status)}>
                              {playerStatusLabelT(row.status, t)}
                            </AdminStatusBadge>
                          </div>
                        ) : canFreezePlayers ? (
                          <div className="flex justify-center">
                            <ConfirmableSwitch
                              checked={row.status === 0}
                              confirmBusy={confirmBusy}
                              disabled={freezeBusyId === row.id}
                              aria-label={t("status")}
                              onCheckedChange={(checked) => {
                                const name = row.username ?? row.site_player_id;
                                if (checked) {
                                  requestConfirm({
                                    title: t("confirmUnfreezeTitle"),
                                    description: t("confirmUnfreezeDescription", { name }),
                                    onConfirm: () => toggleFreeze(row, false),
                                  });
                                  return;
                                }
                                requestConfirm({
                                  title: t("confirmFreezeTitle"),
                                  description: t("confirmFreezeDescription", { name }),
                                  onConfirm: () => toggleFreeze(row, true),
                                });
                              }}
                            />
                          </div>
                        ) : (
                          <AdminStatusBadge status={row.status} tone={resolvePlayerStatusTone(row.status)}>
                            {playerStatusLabelT(row.status, t)}
                          </AdminStatusBadge>
                        )}
                      </TableCell>
                      <AdminAgentCell row={row} />
                      <TableCell>
                        <span className="font-mono text-xs">{row.site_player_id}</span>
                      </TableCell>
                      <TableCell>{row.username ?? "—"}</TableCell>
                      <TableCell>{row.nickname ?? "—"}</TableCell>
                      <TableCell className="max-w-[16rem]">
                        {riskTags.length > 0 ? (
                          <div className="flex flex-nowrap items-center gap-1 overflow-x-auto whitespace-nowrap" title={riskTags.join(", ")}>
                            {riskTags.map((tag) => (
                              <span
                                key={`${row.id}-${tag}`}
                                className="inline-flex shrink-0 items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-[11px] font-medium leading-4 text-amber-900"
                              >
                                {tag}
                              </span>
                            ))}
                          </div>
                        ) : (
                          <span className="text-xs text-muted-foreground">—</span>
                        )}
                      </TableCell>
                      <TableCell>{row.default_currency}</TableCell>
                      <TableCell className="whitespace-nowrap text-xs text-muted-foreground">
                        {row.last_login_at ? formatDt(row.last_login_at) : "—"}
                      </TableCell>
                      <TableCell className="sticky right-0 z-10 bg-card text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">
                        <AdminRowActionsMenu
                          actions={[
                            {
                              key: "detail",
                              label: t("viewDetail"),
                              icon: Eye,
                              href: adminPlayerDetailPath(row.id),
                            },
                            ...(canManagePlayers
                              ? [
                                  {
                                    key: "edit",
                                    label: t("edit"),
                                    icon: Pencil,
                                    onClick: () => openEditAccount(row),
                                  },
                                  {
                                    key: "delete",
                                    label: t("delete"),
                                    icon: Trash2,
                                    destructive: true,
                                    onClick: () => setDeleteTarget(row),
                                  },
                                ]
                              : []),
                          ]}
                        />
                      </TableCell>
                    </TableRow>
                    );
                  })
                )}
              </TableBody>
            </Table>
          </div>
          <AdminListPaginationFooter
            selectId="admin-players-per-page"
            total={total}
            page={page}
            lastPage={lastPage}
            perPage={perPage}
            loading={loading}
            onPerPageChange={(n) => {
              setPerPage(n);
              setPage(1);
            }}
            onPageChange={setPage}
          />
        </CardContent>
      </Card>

      <Dialog open={accountOpen} onOpenChange={handleAccountDialogOpenChange}>
        <DialogContent showCloseButton className="max-h-[90vh] max-w-lg gap-4 overflow-y-auto sm:max-w-xl">
          <DialogHeader>
            <DialogTitle>{accountMode === "create" ? t("createDialogTitle") : t("editDialogTitle")}</DialogTitle>
            <DialogDescription>
              {accountMode === "create" ? t("createDialogDesc") : t("editDialogDesc")}
            </DialogDescription>
          </DialogHeader>
          <div className="space-y-3">
            {accountMode === "create" && (
              <>
                <div className="space-y-1.5">
                  <Label htmlFor="player-funding-mode">{t("createFundingMode")}</Label>
                  <Select
                    value={formFundingMode}
                    onValueChange={(value) => {
                      const mode = value === "credit" ? "credit" : "wallet";
                      setFormFundingMode(mode);
                      if (mode === "credit") {
                        setFormPassword("");
                        setFormCreditLimit("");
                      }
                    }}
                  >
                    <SelectTrigger id="player-funding-mode">
                      <SelectValue>
                        {formFundingMode === "credit"
                          ? t("createFundingCredit")
                          : t("createFundingWallet")}
                      </SelectValue>
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="wallet">{t("createFundingWallet")}</SelectItem>
                      <SelectItem value="credit">{t("createFundingCredit")}</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div className="space-y-1.5">
                  <Label htmlFor="player-site-code">{t("siteCode")}</Label>
                  {boundAgent && boundAgent.site_code ? (
                    <Input id="player-site-code" value={boundAgent.site_code} disabled readOnly />
                  ) : canChooseSite && siteOptions.length > 0 ? (
                    <Select
                      value={formSiteCode}
                      onValueChange={(code) => {
                        setFormSiteCode(code ?? "");
                        setFormAgentNodeId(undefined);
                      }}
                    >
                      <SelectTrigger id="player-site-code">
                        <SelectValue placeholder={t("siteCodePlaceholder")}>
                          {siteOptions.find((s) => s.code === formSiteCode)?.name ?? formSiteCode}
                        </SelectValue>
                      </SelectTrigger>
                      <SelectContent>
                        {siteOptions.map((site) => (
                          <SelectItem key={site.id} value={site.code}>
                            {site.name} ({site.code})
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  ) : (
                    <Input
                      id="player-site-code"
                      value={formSiteCode}
                      placeholder={t("siteCodePlaceholder")}
                      onChange={(e) => setFormSiteCode(e.target.value)}
                    />
                  )}
                </div>
                {boundAgent ? (
                  <p className="text-xs text-muted-foreground">
                    {t("createAgentAutoHint", {
                      name: boundAgent.name,
                      code: boundAgent.code,
                    })}
                  </p>
                ) : canPickAgentOnCreate ? (
                  <div className="space-y-1.5">
                    <Label htmlFor="player-agent-node">{t("createAgentNode")}</Label>
                    <Select
                      value={
                        formAgentNodeId != null && formAgentNodeId > 0
                          ? String(formAgentNodeId)
                          : ""
                      }
                      onValueChange={(v) => setFormAgentNodeId(Number(v))}
                      disabled={createAgentLoading || createAgentOptions.length === 0}
                    >
                      <SelectTrigger id="player-agent-node">
                        <SelectValue placeholder={t("createAgentNodePlaceholder")}>
                          {createAgentOptions.find((o) => o.id === formAgentNodeId)?.label ??
                            t("createAgentNodePlaceholder")}
                        </SelectValue>
                      </SelectTrigger>
                      <SelectContent>
                        {createAgentOptions.map((opt) => (
                          <SelectItem key={opt.id} value={String(opt.id)}>
                            {opt.label}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                ) : null}
                <div className="space-y-1.5">
                  <Label htmlFor="player-site-id">{t("sitePlayerIdLabel")}</Label>
                  <Input
                    id="player-site-id"
                    value={formSitePlayerId}
                    placeholder={
                      formFundingMode === "credit"
                        ? t("createSitePlayerIdOptional")
                        : t("sitePlayerIdPlaceholder")
                    }
                    onChange={(e) => setFormSitePlayerId(e.target.value)}
                  />
                </div>
                {formFundingMode === "credit" ? (
                  <>
                    <div className="space-y-1.5">
                      <Label htmlFor="player-username">{t("username")}</Label>
                      <Input
                        id="player-username"
                        value={formUsername}
                        autoComplete="off"
                        placeholder={t("createUsernameRequired")}
                        onChange={(e) => setFormUsername(e.target.value)}
                      />
                    </div>
                    <div className="space-y-1.5">
                      <Label htmlFor="player-password">{t("createLoginPassword")}</Label>
                      <Input
                        id="player-password"
                        type="password"
                        value={formPassword}
                        autoComplete="new-password"
                        placeholder={t("createLoginPasswordPlaceholder")}
                        onChange={(e) => setFormPassword(e.target.value)}
                      />
                    </div>
                    <PlayerCreditLimitField
                      id="player-credit-limit"
                      value={formCreditLimit}
                      onValueChange={setFormCreditLimit}
                      parentAvailableCredit={parentAvailableCredit}
                    />
                  </>
                ) : (
                  <div className="space-y-1.5">
                    <Label htmlFor="player-username">{t("username")}</Label>
                    <Input
                      id="player-username"
                      value={formUsername}
                      placeholder={t("usernamePlaceholderOptional")}
                      onChange={(e) => setFormUsername(e.target.value)}
                    />
                  </div>
                )}
              </>
            )}
            {accountMode === "edit" ? (
              <div className="space-y-1.5">
                <Label htmlFor="player-username">{t("username")}</Label>
                <Input
                  id="player-username"
                  value={formUsername}
                  disabled
                  onChange={(e) => setFormUsername(e.target.value)}
                />
              </div>
            ) : null}
            <div className="space-y-1.5">
              <Label htmlFor="player-nickname">{t("nickname")}</Label>
              <Input
                id="player-nickname"
                value={formNickname}
                placeholder={t("nicknamePlaceholderOptional")}
                onChange={(e) => setFormNickname(e.target.value)}
              />
            </div>
            {accountMode === "create" && (
              <>
                <div className="space-y-1.5">
                  <Label htmlFor="player-currency">{t("defaultCurrency")}</Label>
                  <Input
                    id="player-currency"
                    value={formDefaultCurrency}
                    placeholder="NPR"
                    onChange={(e) => setFormDefaultCurrency(e.target.value.toUpperCase())}
                  />
                </div>
                <div className="space-y-1.5">
                  <Label htmlFor="player-status">{t("status")}</Label>
                  <Select
                    value={String(formStatus)}
                    onValueChange={(v) => setFormStatus(Number(v))}
                  >
                    <SelectTrigger id="player-status">
                      <SelectValue>{playerStatusLabelT(formStatus, t)}</SelectValue>
                    </SelectTrigger>
                    <SelectContent>
                      {PLAYER_STATUS_OPTIONS.map((o) => (
                        <SelectItem key={o.value} value={String(o.value)}>
                          {t(o.label)}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}
            {accountMode === "edit" && (
              <>
                <div className="rounded-lg border bg-muted/30 px-3 py-2.5 text-sm">
                  <div className="grid gap-2 sm:grid-cols-2">
                    <div>
                      <p className="text-xs text-muted-foreground">
                        {t("fundingMode", { defaultValue: "资金模式" })}
                      </p>
                      <p className="mt-1 font-medium">
                        {editingPlayer ? <PlayerFundingModeBadge row={editingPlayer} /> : "—"}
                      </p>
                    </div>
                    <div>
                      <p className="text-xs text-muted-foreground">
                        {t("authSource", { defaultValue: "登录来源" })}
                      </p>
                      <p className="mt-1 font-medium">
                        {editingPlayer?.auth_source === "main_site_sso"
                          ? t("authMainSite", { defaultValue: "主站 SSO" })
                          : editingPlayer?.auth_source === "lottery_native"
                            ? t("authNative", { defaultValue: "彩票端" })
                            : editingPlayer?.auth_source ?? "—"}
                      </p>
                    </div>
                  </div>
                </div>
                <div className="space-y-1.5">
                  <Label htmlFor="player-edit-risk-tags">
                    {t("riskTags", { defaultValue: "风控标签" })}
                  </Label>
                  <Input
                    id="player-edit-risk-tags"
                    value={formRiskTags}
                    placeholder={t("riskTagsPlaceholder", { defaultValue: "如：高频、大额、需复核；多个标签用逗号分隔" })}
                    onChange={(e) => setFormRiskTags(e.target.value)}
                  />
                </div>
                {editingUsesCredit ? (
                  accountDetailLoading ? (
                    <p className="text-xs text-muted-foreground">{t("editDetailLoading")}</p>
                  ) : (
                    <PlayerCreditLimitField
                      id="player-edit-credit-limit"
                      value={formCreditLimit}
                      onValueChange={setFormCreditLimit}
                      parentAvailableCredit={parentAvailableCredit}
                      baselineCreditLimit={editingPlayer?.credit_limit ?? 0}
                    />
                  )
                ) : null}
                <div className="space-y-1.5">
                  <Label htmlFor="player-edit-status">{t("status")}</Label>
                  <Select
                    value={String(formStatus)}
                    onValueChange={(v) => setFormStatus(Number(v))}
                  >
                    <SelectTrigger id="player-edit-status">
                      <SelectValue>{playerStatusLabelT(formStatus, t)}</SelectValue>
                    </SelectTrigger>
                    <SelectContent>
                      {PLAYER_STATUS_OPTIONS.map((o) => (
                        <SelectItem key={o.value} value={String(o.value)}>
                          {t(o.label)}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}
          </div>
          <div className="flex justify-end gap-2">
            <Button
              type="button"
              variant="outline"
              onClick={() => handleAccountDialogOpenChange(false)}
            >
              {t("cancel")}
            </Button>
            <Button
              type="button"
              disabled={accountSaving || accountDetailLoading}
              onClick={() => void submitAccount()}
            >
              {accountSaving ? t("saving") : t("save")}
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      <Dialog open={deleteTarget !== null} onOpenChange={(open) => !open && setDeleteTarget(null)}>
        <DialogContent showCloseButton className="max-w-sm">
          <DialogHeader>
            <DialogTitle>{t("confirmDelete")}</DialogTitle>
            <DialogDescription>
              {deleteTarget
                ? t("confirmDeleteDesc", {
                    name: deleteTarget.username ?? deleteTarget.site_player_id,
                  })
                : null}
            </DialogDescription>
          </DialogHeader>
          <div className="flex justify-end gap-2">
            <Button type="button" variant="outline" onClick={() => setDeleteTarget(null)}>
              {t("cancel")}
            </Button>
            <Button type="button" variant="destructive" disabled={deleteBusy} onClick={() => void confirmDelete()}>
              {deleteBusy ? t("actions.submitting", { ns: "common" }) : t("delete")}
            </Button>
          </div>
        </DialogContent>
      </Dialog>
      <ConfirmDialog />
    </div>
  );
}
