"use client";

import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

import {
  deletePlayConfigVersion,
  getAllConfigVersions,
  getPlayConfigVersion,
  getPlayConfigVersions,
  postPlayConfigVersion,
  publishPlayConfigVersion,
  putPlayConfigItems,
} from "@/api/admin-config";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { ConfigChipGroup } from "@/modules/config/config-chip-group";
import { ConfigDocPage, ConfigDocToolbar } from "@/modules/config/config-doc-page";

import { ConfirmableSwitch } from "@/components/admin/confirmable-switch";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
import { ConfigVersionActions } from "@/modules/config/config-version-actions";
import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher";

import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { formatAdminMinorDecimal, parseAdminMajorToMinor } from "@/lib/money";
import { PRD_PLAY_SWITCH_MANAGE } from "@/lib/admin-prd";
import { useAdminProfile } from "@/stores/admin-session";
import { LotteryApiBizError } from "@/types/api/errors";
import type {
  ConfigVersionSummary,
  PlayConfigItemRow,
  PlayConfigVersionDetail,
} from "@/types/api/admin-config";

type PlayConfigSaveItemPayload = {
  play_code: string;
  category: string;
  dimension: number | null;
  bet_mode: string | null;
  display_name: string;
  is_enabled: boolean;
  min_bet_amount: number;
  max_bet_amount: number;
  display_order: number;
  supports_multi_number: boolean;
  reserved_rule_json: unknown;
  rule_text_zh: string | null;
  rule_text_en: string | null;
  rule_text_ne: string | null;
  extra_config_json: unknown;
};

type PlayBatchSwitchGroup = {
  key: string;
  match: (row: PlayConfigItemRow) => boolean;
};

const TRADITIONAL_PLAY_CODES = new Set([
  "big", "small", "pos_4a", "pos_4b", "pos_4c", "pos_4d", "pos_4e", "four_any", "four_top", "four_lower",
  "pos_3a", "pos_3lower", "pos_3b", "pos_3c", "pos_3d", "pos_3e",
  "pos_2a", "pos_2b", "pos_2c", "pos_2d", "pos_2e", "pos_2any",
]);

const PLAY_BATCH_SWITCH_GROUPS: PlayBatchSwitchGroup[] = [
  {
    key: "d2",
    match: (row) => row.dimension === 2,
  },
  {
    key: "d3",
    match: (row) => row.dimension === 3,
  },
  {
    key: "d4",
    match: (row) => row.dimension === 4,
  },
  {
    key: "big-small",
    match: (row) => row.play_code === "big" || row.play_code === "small",
  },
  {
    key: "position",
    match: (row) => row.category === "position",
  },
  {
    key: "box",
    match: (row) => row.category === "box",
  },
  {
    key: "jackpot",
    match: (row) => row.category === "jackpot" || row.play_code.includes("jackpot"),
  },
];

/** Save payload for play-config drafts. Persist the current draft snapshot directly. */
function buildPlayConfigSavePayload(
  draftRows: PlayConfigItemRow[],
): PlayConfigSaveItemPayload[] {
  return [...draftRows]
    .sort((a, b) => a.display_order - b.display_order || a.play_code.localeCompare(b.play_code))
    .map((row) => ({
      play_code: row.play_code,
      category: row.category ?? "",
      dimension: row.dimension,
      bet_mode: row.bet_mode,
      display_name: row.display_name ?? row.play_code,
      is_enabled: row.is_enabled,
      min_bet_amount: row.min_bet_amount,
      max_bet_amount: row.max_bet_amount,
      display_order: row.display_order,
      supports_multi_number: row.supports_multi_number,
      reserved_rule_json: row.reserved_rule_json,
      rule_text_zh: row.rule_text_zh,
      rule_text_en: row.rule_text_en,
      rule_text_ne: row.rule_text_ne,
      extra_config_json: row.extra_config_json,
    }));
}

export function PlayConfigDocScreen() {
  const { t } = useTranslation(["config", "adminUsers", "common"]);
  const tRef = useTranslationRef(["config", "common"]);
  const { request: requestConfirm, ConfirmDialog, busy: confirmBusy } = useConfirmAction();
  const profile = useAdminProfile();
  const canManage = adminHasAnyPermission(profile?.permissions, [PRD_PLAY_SWITCH_MANAGE]);
  const [list, setList] = useState<ConfigVersionSummary[]>([]);
  const [selectedId, setSelectedId] = useState("");
  const [detail, setDetail] = useState<PlayConfigVersionDetail | null>(null);
  const [draftRows, setDraftRows] = useState<PlayConfigItemRow[]>([]);
  const [loadingList, setLoadingList] = useState(true);
  const [loadingDetail, setLoadingDetail] = useState(false);
  const [saving, setSaving] = useState(false);
  const [creatingDraftId, setCreatingDraftId] = useState<string | null>(null);
  const [rollbackOpen, setRollbackOpen] = useState(false);
  const [rollbackTarget, setRollbackTarget] = useState<ConfigVersionSummary | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [keyword, setKeyword] = useState("");
  const [statusFilter, setStatusFilter] = useState<"all" | "enabled" | "disabled">("all");
  const [categoryFilter, setCategoryFilter] = useState("all");
  const detailRequestSeq = useRef(0);

  const refreshList = useCallback(async () => {
    setLoadingList(true);
    setError(null);
    try {
      const d = await getAllConfigVersions(getPlayConfigVersions);
      setList(d.items);
      setCreatingDraftId((draftId) =>
        draftId !== null && d.items.some((x) => String(x.id) === draftId) ? null : draftId,
      );
    } catch (e) {
      const msg =
        e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" });
      setError(msg);
      setList([]);
    } finally {
      setLoadingList(false);
    }
  }, [tRef]);

  useAsyncEffect(() => {
    void refreshList();
  }, []);

  const loadDetail = useCallback(async (id: number) => {
    const requestSeq = detailRequestSeq.current + 1;
    detailRequestSeq.current = requestSeq;
    setLoadingDetail(true);
    setDetail(null);
    setDraftRows([]);
    try {
      const d = await getPlayConfigVersion(id);
      if (detailRequestSeq.current !== requestSeq) {
        return;
      }
      setDetail(d);
      setDraftRows(d.items.map((it) => ({ ...it })));
    } catch (e) {
      if (detailRequestSeq.current !== requestSeq) {
        return;
      }
      toast.error(
        e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
      );
      setDetail(null);
      setDraftRows([]);
    } finally {
      if (detailRequestSeq.current === requestSeq) {
        setLoadingDetail(false);
      }
    }
  }, [tRef]);

  useEffect(() => {
    if (list.length === 0) {
      if (selectedId !== "") {
        queueMicrotask(() => {
          setSelectedId("");
          setDetail(null);
          setDraftRows([]);
        });
      }
      return;
    }
    if (selectedId !== "" && list.some((x) => String(x.id) === selectedId)) {
      return;
    }
    if (creatingDraftId !== null && selectedId === creatingDraftId) {
      return;
    }
    queueMicrotask(() => {
      const active = list.find((x) => x.status === "active");
      const drafts = list.filter((x) => x.status === "draft").sort((a, b) => b.id - a.id);
      const pick = active ?? drafts[0] ?? [...list].sort((a, b) => b.id - a.id)[0];
      if (pick) {
        setSelectedId(String(pick.id));
      }
    });
  }, [list, selectedId, creatingDraftId]);

  useEffect(() => {
    if (selectedId === "") {
      return;
    }
    const id = Number(selectedId);
    if (!Number.isFinite(id)) {
      return;
    }
    queueMicrotask(() => {
      void loadDetail(id);
    });
  }, [selectedId, loadDetail]);

  const selectedVersionSummary = useMemo(
    () => list.find((x) => String(x.id) === selectedId) ?? null,
    [list, selectedId],
  );
  const isSelectedDetail = detail !== null && String(detail.id) === selectedId;
  const selectedStatus = isSelectedDetail ? detail.status : selectedVersionSummary?.status;
  const isDraft = selectedStatus === "draft";
  const amountCurrencyCode = "NPR";

  const orderedRows = useMemo(
    () =>
      draftRows.filter((row) => TRADITIONAL_PLAY_CODES.has(row.play_code)).sort(
        (a, b) => a.display_order - b.display_order || a.play_code.localeCompare(b.play_code),
      ),
    [draftRows],
  );

  const categoryOptions = useMemo(() => {
    const seen = new Set<string>();
    return orderedRows
      .map((row) => row.category?.trim() || "")
      .filter((value) => {
        if (!value || seen.has(value)) {
          return false;
        }
        seen.add(value);
        return true;
      });
  }, [orderedRows]);

  const filteredRows = useMemo(() => {
    const normalizedKeyword = keyword.trim().toLowerCase();

    return orderedRows.filter((row) => {
      const normalizedCategory = row.category?.trim() || "uncategorized";
      const matchesKeyword =
        normalizedKeyword === "" ||
        row.play_code.toLowerCase().includes(normalizedKeyword) ||
        (row.display_name ?? "").toLowerCase().includes(normalizedKeyword) ||
        (row.category ?? "").toLowerCase().includes(normalizedKeyword);
      const matchesStatus =
        statusFilter === "all" ||
        (statusFilter === "enabled" && row.is_enabled) ||
        (statusFilter === "disabled" && !row.is_enabled);
      const matchesCategory = categoryFilter === "all" || normalizedCategory === categoryFilter;

      return matchesKeyword && matchesStatus && matchesCategory;
    });
  }, [categoryFilter, keyword, orderedRows, statusFilter]);

  const groupedRows = useMemo(() => {
    const groups = new Map<string, PlayConfigItemRow[]>();
    for (const row of filteredRows) {
      const groupKey = row.category?.trim() || "uncategorized";
      const current = groups.get(groupKey);
      if (current) {
        current.push(row);
      } else {
        groups.set(groupKey, [row]);
      }
    }
    return Array.from(groups.entries());
  }, [filteredRows]);

  function categoryLabel(categoryKey: string): string {
    if (categoryKey === "uncategorized") {
      return t("play.filters.uncategorized", { ns: "config" });
    }
    const mapped = t(`play.categories.${categoryKey}`, { ns: "config" });
    return mapped === `play.categories.${categoryKey}` ? categoryKey : mapped;
  }

  function updateConfigRow(playCode: string, patch: Partial<PlayConfigItemRow>) {
    setDraftRows((prev) => prev.map((r) => (r.play_code === playCode ? { ...r, ...patch } : r)));
  }

  function applyBatchSwitch(group: PlayBatchSwitchGroup, enabled: boolean) {
    setDraftRows((prev) =>
      prev.map((row) => (group.match(row) ? { ...row, is_enabled: enabled } : row)),
    );
  }

  const batchSwitchStates = useMemo(
    () =>
      PLAY_BATCH_SWITCH_GROUPS.map((group) => {
        const rows = draftRows.filter(group.match);
        const enabledCount = rows.filter((row) => row.is_enabled).length;
        return {
          ...group,
          label: t(`play.batchGroups.${group.key}`, { ns: "config", defaultValue: group.key }),
          total: rows.length,
          enabledCount,
          allEnabled: rows.length > 0 && enabledCount === rows.length,
        };
      }),
    [draftRows, t],
  );

  async function handleSaveDraft() {
    if (!detail || !isDraft) {
      return;
    }
    const payload = buildPlayConfigSavePayload(draftRows);
    for (const r of payload) {
      if (r.min_bet_amount > r.max_bet_amount) {
        toast.error(t("play.validation.minMaxInvalid", { ns: "config", playCode: r.play_code }));
        return;
      }
    }
    setSaving(true);
    try {
      const d = await putPlayConfigItems(detail.id, payload);
      setDetail(d);
      setDraftRows(d.items.map((it) => ({ ...it })));
      toast.success(t("versionActions.saveDraft", { ns: "config" }));
      void refreshList();
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("versionActions.saveFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  async function handlePublish() {
    if (!detail || !isDraft) {
      return;
    }
    setSaving(true);
    try {
      const d = await publishPlayConfigVersion(detail.id);
      setDetail(d);
      setDraftRows(d.items.map((it) => ({ ...it })));
      toast.success(t("versionActions.publishCurrent", { ns: "config" }));
      void refreshList();
      setSelectedId(String(d.id));
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("play.publishFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  async function handleNewDraft() {
    setSaving(true);
    try {
      const active = list.find((x) => x.status === "active");
      const d = await postPlayConfigVersion({
        reason: `draft ${new Date().toISOString()}`,
        clone_from_version_id: active?.id ?? null,
      });
      toast.success(t("play.createDraftSuccess", { ns: "config", version: d.version_no }));
      setCreatingDraftId(String(d.id));
      setSelectedId(String(d.id));
      setDetail(d);
      setDraftRows(d.items.map((it) => ({ ...it })));
      void refreshList();
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("play.createDraftFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  function renderDisplayNameReadonly(row: PlayConfigItemRow) {
    const name = row.display_name?.trim();
    return <span>{name || row.play_code}</span>;
  }

  async function handleDeleteVersion(row: ConfigVersionSummary) {
    try {
      await deletePlayConfigVersion(row.id);
      toast.success(t("versionSwitcher.delete", { ns: "config" }));
      await refreshList();
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("play.deleteFailed", { ns: "config" }));
      throw e;
    }
  }

  function requestRollback(row: ConfigVersionSummary) {
    setRollbackTarget(row);
    setRollbackOpen(true);
  }

  async function handleRollback() {
    if (!rollbackTarget) {
      return;
    }
    setSaving(true);
    try {
      const d = await postPlayConfigVersion({
        reason: `rollback from v${rollbackTarget.version_no}`,
        clone_from_version_id: rollbackTarget.id,
      });
      toast.success(
        t("versionActions.rollbackSuccess", {
          ns: "config",
          fromVersion: rollbackTarget.version_no,
          version: d.version_no,
        }),
      );
      await refreshList();
      setSelectedId(String(d.id));
      setDetail(d);
      setDraftRows(d.items.map((it) => ({ ...it })));
      setRollbackOpen(false);
      setRollbackTarget(null);
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("versionActions.rollbackFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  return (
    <ConfigDocPage
      title={t("nav.items.plays", { ns: "config" })}
      toolbar={
        <ConfigDocToolbar
          switcher={
            <ConfigVersionSwitcher
              versions={list}
              selectedId={selectedId}
              onSelectedIdChange={setSelectedId}
              loading={loadingList}

              onDeleteVersion={handleDeleteVersion}
              onRollbackVersion={requestRollback}
              rollbackBusy={saving}
            />
          }
          actions={
            <ConfigVersionActions
              isDraft={isDraft}
              canManage={canManage}
              loadingList={loadingList}
              loadingDetail={loadingDetail}
              saving={saving}
              onRefresh={() => void refreshList()}
              onNewDraft={() => void handleNewDraft()}
              onSaveDraft={() => void handleSaveDraft()}
              onPublish={() =>
                requestConfirm({
                  title: t("play.publishDialog.title", { ns: "config" }),
                  description: t("play.publishDialog.description", { ns: "config" }),
                  confirmLabel: t("play.publishDialog.confirm", { ns: "config" }),
                  confirmVariant: "destructive",
                  onConfirm: () => handlePublish(),
                })
              }
            />
          }
        />
      }
    >
      {detail ? (
        <div className="space-y-3">
          <div className="flex flex-wrap items-center gap-2">
            <Input
              value={keyword}
              onChange={(e) => setKeyword(e.target.value)}
              placeholder={t("play.filters.keywordPlaceholder", { ns: "config" })}
              className="h-8 w-full min-w-[12rem] max-w-xs"
            />
            <Select value={categoryFilter} onValueChange={(value) => setCategoryFilter(value ?? "all")}>
              <SelectTrigger className="h-8 w-[8.5rem]">
                <SelectValue>
                  {categoryFilter === "all"
                    ? t("play.filters.allCategories", { ns: "config" })
                    : categoryLabel(categoryFilter)}
                </SelectValue>
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">{t("play.filters.allCategories", { ns: "config" })}</SelectItem>
                {categoryOptions.map((category) => (
                  <SelectItem key={category} value={category}>
                    {categoryLabel(category)}
                  </SelectItem>
                ))}
                <SelectItem value="uncategorized">
                  {t("play.filters.uncategorized", { ns: "config" })}
                </SelectItem>
              </SelectContent>
            </Select>
            <Select
              value={statusFilter}
              onValueChange={(value) => setStatusFilter(value as "all" | "enabled" | "disabled")}
            >
              <SelectTrigger className="h-8 w-[6.5rem]">
                <SelectValue>
                  {statusFilter === "all"
                    ? t("play.filters.allStatuses", { ns: "config" })
                    : statusFilter === "enabled"
                      ? t("play.states.enabled", { ns: "config" })
                      : t("play.states.disabled", { ns: "config" })}
                </SelectValue>
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">{t("play.filters.allStatuses", { ns: "config" })}</SelectItem>
                <SelectItem value="enabled">{t("play.states.enabled", { ns: "config" })}</SelectItem>
                <SelectItem value="disabled">{t("play.states.disabled", { ns: "config" })}</SelectItem>
              </SelectContent>
            </Select>
            {(keyword || categoryFilter !== "all" || statusFilter !== "all") ? (
              <Button
                type="button"
                size="sm"
                variant="ghost"
                className="h-8 px-2"
                onClick={() => {
                  setKeyword("");
                  setCategoryFilter("all");
                  setStatusFilter("all");
                }}
              >
                {t("play.filters.reset", { ns: "config" })}
              </Button>
            ) : null}
          </div>
          {isDraft ? (
            <ConfigChipGroup label={t("play.batchSwitchesTitle", { ns: "config" })}>
              {batchSwitchStates.map((group) => {
                const groupOn = group.allEnabled;
                const isPartial =
                  group.total > 0 && group.enabledCount > 0 && group.enabledCount < group.total;
                return (
                  <label
                    key={group.key}
                    className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-border/60 px-2.5 py-1.5 text-sm"
                  >
                    <span>{group.label}</span>
                    <Checkbox
                      checked={groupOn}
                      indeterminate={isPartial}
                      disabled={saving || group.total === 0 || confirmBusy}
                      aria-label={t("play.aria.batchGroupSwitch", {
                        ns: "config",
                        group: group.label,
                      })}
                      onCheckedChange={(checked) => {
                        const enable = checked === true;
                        const action = enable
                          ? t("play.batchSwitchEnable", { ns: "config" })
                          : t("play.batchSwitchDisable", { ns: "config" });
                        requestConfirm({
                          title: t("play.batchSwitchConfirmTitle", { ns: "config", action }),
                          description: t("play.batchSwitchConfirmDescription", {
                            ns: "config",
                            action,
                            group: group.label,
                            count: group.total,
                          }),
                          confirmVariant: enable ? "default" : "destructive",
                          onConfirm: () => applyBatchSwitch(group, enable),
                        });
                      }}
                    />
                  </label>
                );
              })}
            </ConfigChipGroup>
          ) : null}
        </div>
      ) : null}

      {error ? <p className="text-sm text-destructive">{error}</p> : null}

      {loadingDetail ? (
        <AdminLoadingState minHeight="6rem" className="py-6" />
      ) : (
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead className="text-center">{t("play.table.playCode", { ns: "config" })}</TableHead>
              <TableHead className="w-[100px] text-center">{t("play.table.category", { ns: "config" })}</TableHead>
              <TableHead className="w-[88px] text-center">{t("play.table.status", { ns: "config" })}</TableHead>
              <TableHead className="w-36 text-center">{t("play.table.displayName", { ns: "config" })}</TableHead>
              <TableHead className="w-24 text-center">{t("play.table.order", { ns: "config" })}</TableHead>
              <TableHead className="w-[110px] text-center">{t("play.table.minBet", { ns: "config" })}</TableHead>
              <TableHead className="w-[110px] text-center">{t("play.table.maxBet", { ns: "config" })}</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {groupedRows.length === 0 ? (
              <TableRow>
                <TableCell colSpan={7} className="py-8 text-center text-sm text-muted-foreground">
                  {t("play.filters.empty", { ns: "config" })}
                </TableCell>
              </TableRow>
            ) : null}
            {groupedRows.map(([groupKey, rows]) => (
              <Fragment key={groupKey}>
                <TableRow className="bg-muted/30">
                  <TableCell colSpan={7} className="py-2 text-sm font-medium text-foreground">
                    {categoryLabel(groupKey)}
                    <span className="ml-2 text-xs font-normal text-muted-foreground">
                      {t("play.filters.groupCount", { ns: "config", count: rows.length })}
                    </span>
                  </TableCell>
                </TableRow>
                {rows.map((row) => (
                  <TableRow key={row.play_code}>
                    <TableCell className="text-center font-mono text-sm">{row.play_code}</TableCell>
                    <TableCell className="text-center text-muted-foreground text-sm">
                      {row.category ? categoryLabel(row.category) : "—"}
                    </TableCell>
                    <TableCell className="text-center">
                      {isDraft ? (
                        <div className="flex justify-center">
                          <ConfirmableSwitch
                            checked={row.is_enabled}
                            confirmBusy={confirmBusy}
                            disabled={saving}
                            aria-label={t("play.aria.enablePlay", { ns: "config", playCode: row.play_code })}
                            onCheckedChange={(enabled) => {
                              const action = enabled
                                ? t("play.toggleEnable", { ns: "config" })
                                : t("play.toggleDisable", { ns: "config" });
                              requestConfirm({
                                title: t("play.toggleConfirmTitle", {
                                  ns: "config",
                                  action,
                                  playCode: row.play_code,
                                }),
                                description: t("play.toggleConfirmDescription", { ns: "config" }),
                                confirmVariant: enabled ? "default" : "destructive",
                                onConfirm: () => {
                                  updateConfigRow(row.play_code, { is_enabled: enabled });
                                },
                              });
                            }}
                          />
                        </div>
                      ) : (
                        <div className="flex justify-center">
                          <AdminStatusBadge status={row.is_enabled ? "enabled" : "disabled"}>
                            {row.is_enabled
                              ? t("play.states.enabled", { ns: "config" })
                              : t("play.states.disabled", { ns: "config" })}
                          </AdminStatusBadge>
                        </div>
                      )}
                    </TableCell>
                    <TableCell className="w-36 text-center">
                      {isDraft ? (
                        <Input
                          type="text"
                          className="mx-auto h-8 w-full max-w-[9rem] text-center text-sm"
                          disabled={saving}
                          value={row.display_name ?? ""}
                          placeholder={row.play_code}
                          onChange={(e) =>
                            updateConfigRow(row.play_code, { display_name: e.target.value })
                          }
                          onBlur={(e) => {
                            const trimmed = e.target.value.trim();
                            updateConfigRow(row.play_code, {
                              display_name: trimmed || row.play_code,
                            });
                          }}
                        />
                      ) : (
                        <ConfigReadonlyValue className="justify-center">
                          {renderDisplayNameReadonly(row)}
                        </ConfigReadonlyValue>
                      )}
                    </TableCell>
                    <TableCell className="w-24 text-center">
                      {isDraft ? (
                        <Input
                          type="text"
                          inputMode="numeric"
                          className="mx-auto h-8 w-16 font-mono tabular-nums text-center"
                          value={row.display_order}
                          disabled={saving}
                          placeholder={t("play.placeholders.displayOrder", { ns: "config" })}
                          onChange={(e) => {
                            const n = Number.parseInt(e.target.value, 10);
                            if (Number.isFinite(n)) {
                              updateConfigRow(row.play_code, { display_order: n });
                            }
                          }}
                        />
                      ) : (
                        <ConfigReadonlyValue mono className="justify-center">
                          {row.display_order}
                        </ConfigReadonlyValue>
                      )}
                    </TableCell>
                    <TableCell className="text-center">
                      {isDraft ? (
                        <Input
                          type="text"
                          inputMode="decimal"
                          className="mx-auto h-8 w-24 text-center text-sm font-semibold"
                          disabled={saving}
                          value={formatAdminMinorDecimal(row.min_bet_amount, amountCurrencyCode)}
                          placeholder={t("play.placeholders.minBetAmount", { ns: "config" })}
                          onChange={(e) =>
                            updateConfigRow(row.play_code, {
                              min_bet_amount:
                                parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
                            })
                          }
                        />
                      ) : (
                        <ConfigReadonlyValue className="justify-center text-sm font-semibold">
                          {formatAdminMinorDecimal(row.min_bet_amount, amountCurrencyCode)}
                        </ConfigReadonlyValue>
                      )}
                    </TableCell>
                    <TableCell className="text-center">
                      {isDraft ? (
                        <Input
                          type="text"
                          inputMode="decimal"
                          className="mx-auto h-8 w-24 text-center text-sm font-semibold"
                          disabled={saving}
                          value={formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
                          placeholder={t("play.placeholders.maxBetAmount", { ns: "config" })}
                          onChange={(e) =>
                            updateConfigRow(row.play_code, {
                              max_bet_amount:
                                parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
                            })
                          }
                        />
                      ) : (
                        <ConfigReadonlyValue className="justify-center text-sm font-semibold">
                          {formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
                        </ConfigReadonlyValue>
                      )}
                    </TableCell>
                  </TableRow>
                ))}
              </Fragment>
            ))}
          </TableBody>
        </Table>
      )}

      <Dialog open={rollbackOpen} onOpenChange={setRollbackOpen}>
        <DialogContent showCloseButton className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>{t("versionActions.rollbackDialog.title", { ns: "config" })}</DialogTitle>
            <DialogDescription>
              {t("versionActions.rollbackDialog.description", {
                ns: "config",
                version: rollbackTarget?.version_no ?? "—",
              })}
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button type="button" variant="outline" onClick={() => setRollbackOpen(false)}>
              {t("actions.cancel", { ns: "adminUsers" })}
            </Button>
            <Button type="button" onClick={() => void handleRollback()} disabled={!rollbackTarget || saving}>
              {t("versionActions.rollbackDialog.confirm", { ns: "config" })}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <ConfirmDialog />
    </ConfigDocPage>
  );
}
