"use client";

import { 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 {
  ConfigVersionToolbarMeta,
  ConfigVersionToolbarMetaEmphasis,
} from "@/modules/config/config-version-toolbar-meta";
import { ConfigSection } from "@/modules/config/config-section";
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 {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
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 { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useConfirmAction } from "@/hooks/use-confirm-action";
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 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 { request: requestConfirm, ConfirmDialog, busy: confirmBusy } = useConfirmAction();
  const profile = useAdminProfile();
  const canManage = adminHasAnyPermission(profile?.permissions, [PRD_PLAY_SWITCH_MANAGE]);
  const formatDt = useAdminDateTimeFormatter();
  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 detailRequestSeq = useRef(0);

  const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
  const [rulePlayCode, setRulePlayCode] = useState<string | null>(null);
  const [ruleDraftZh, setRuleDraftZh] = useState("");
  const [ruleDraftEn, setRuleDraftEn] = useState("");
  const [ruleDraftNe, setRuleDraftNe] = useState("");

  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 : t("errors.loadFailed", { ns: "common" });
      setError(msg);
      setList([]);
    } finally {
      setLoadingList(false);
    }
  }, [t]);

  useEffect(() => {
    queueMicrotask(() => {
      void refreshList();
    });
  }, [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 : t("errors.loadFailed", { ns: "common" }));
      setDetail(null);
      setDraftRows([]);
    } finally {
      if (detailRequestSeq.current === requestSeq) {
        setLoadingDetail(false);
      }
    }
  }, [t]);

  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].sort(
        (a, b) => a.display_order - b.display_order || a.play_code.localeCompare(b.play_code),
      ),
    [draftRows],
  );

  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 openRuleEditor(play_code: string) {
    const item = draftRows.find((row) => row.play_code === play_code);
    setRulePlayCode(play_code);
    setRuleDraftZh(item?.rule_text_zh ?? "");
    setRuleDraftEn(item?.rule_text_en ?? "");
    setRuleDraftNe(item?.rule_text_ne ?? "");
    setRuleDialogOpen(true);
  }

  function saveRuleDraft() {
    if (!rulePlayCode) {
      return;
    }
    updateConfigRow(rulePlayCode, {
      rule_text_zh: ruleDraftZh.trim() || null,
      rule_text_en: ruleDraftEn.trim() || null,
      rule_text_ne: ruleDraftNe.trim() || null,
    });
    setRuleDialogOpen(false);
    setRulePlayCode(null);
    toast.message(t("play.ruleSavedLocal", { ns: "config" }));
  }

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

  const activeHead = list.find((x) => x.status === "active");

  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}
              sheetTitle={`${t("nav.items.plays", { ns: "config" })} ${t("versionSwitcher.sheetTitle", { ns: "config" })}`}
              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(),
                })
              }
            />
          }
          footer={
            detail ? (
              <ConfigVersionToolbarMeta emphasis={!isDraft}>
                {activeHead ? (
                  <span>
                    {t("play.activeVersion", { ns: "config", version: activeHead.version_no })}
                    {activeHead.effective_at ? ` · ${formatDt(activeHead.effective_at)}` : ""}
                  </span>
                ) : null}
                {!isDraft ? (
                  <ConfigVersionToolbarMetaEmphasis>
                    {t("play.readOnlyHint", { ns: "config" })}
                  </ConfigVersionToolbarMetaEmphasis>
                ) : activeHead ? (
                  <span>{t("versionToolbar.draftEditing", { ns: "config" })}</span>
                ) : null}
              </ConfigVersionToolbarMeta>
            ) : null
          }
        />
      }
    >
      {detail ? (
        <ConfigSection
          title={t("play.batchSwitchesTitle", { ns: "config" })}
          description={!isDraft ? t("play.readOnlyDraftHint", { ns: "config" }) : undefined}
        >
          <ConfigChipGroup>
            {batchSwitchStates.map((group) => {
              const groupOn = group.allEnabled;
              const isPartial =
                group.total > 0 && group.enabledCount > 0 && group.enabledCount < group.total;
              return (
                <div
                  key={group.key}
                  className="flex items-center justify-between gap-4 rounded-xl border border-border/60 bg-card px-4 py-3"
                >
                  <div className="min-w-0">
                    <p className="text-sm font-medium text-foreground">{group.label}</p>
                    <p className="text-sm text-muted-foreground">
                      {group.total > 0
                        ? isPartial
                          ? t("play.batchPartialEnabled", {
                              ns: "config",
                              enabledCount: group.enabledCount,
                              total: group.total,
                            })
                          : t("play.batchEnabledCount", {
                              ns: "config",
                              enabledCount: group.enabledCount,
                              total: group.total,
                            })
                        : t("play.noPlayTypes", { ns: "config" })}
                    </p>
                  </div>
                  <div className="flex shrink-0 items-center justify-center">
                    <Checkbox
                      checked={groupOn}
                      indeterminate={isPartial}
                      disabled={!isDraft || 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),
                        });
                      }}
                    />
                  </div>
                </div>
              );
            })}
          </ConfigChipGroup>
        </ConfigSection>
      ) : null}

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

      {loadingDetail ? (
        <p className="text-sm text-muted-foreground">{t("states.loading", { ns: "common" })}</p>
      ) : (
        <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>
                  <TableHead className="w-[140px] text-center">{t("play.table.actions", { ns: "config" })}</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {orderedRows.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 ?? "—"}</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}
                          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 font-mono tabular-nums"
                          disabled={saving}
                          value={formatAdminMinorDecimal(row.min_bet_amount, amountCurrencyCode)}
                          onChange={(e) =>
                            updateConfigRow(row.play_code, {
                              min_bet_amount:
                                parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
                            })
                          }
                        />
                      ) : (
                        <ConfigReadonlyValue mono className="justify-center">
                          {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 font-mono tabular-nums"
                          disabled={saving}
                          value={formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
                          onChange={(e) =>
                            updateConfigRow(row.play_code, {
                              max_bet_amount:
                                parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
                            })
                          }
                        />
                      ) : (
                        <ConfigReadonlyValue mono className="justify-center">
                          {formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
                        </ConfigReadonlyValue>
                      )}
                    </TableCell>
                    <TableCell className="text-center">
                      {isDraft ? (
                        <Button
                          type="button"
                          variant="outline"
                          disabled={saving}
                          onClick={() => openRuleEditor(row.play_code)}
                        >
                          {t("play.actions.ruleText", { ns: "config" })}
                        </Button>
                      ) : (
                        <AdminStatusBadge status="disabled" className="mx-auto w-fit">
                          {t("play.states.readOnly", { ns: "config" })}
                        </AdminStatusBadge>
                      )}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
        </Table>
      )}

      <Dialog open={ruleDialogOpen} onOpenChange={setRuleDialogOpen}>
        <DialogContent showCloseButton className="sm:max-w-lg">
          <DialogHeader>
            <DialogTitle>{t("play.ruleDialog.title", { ns: "config" })}</DialogTitle>
            <DialogDescription>
              {t("play.ruleDialog.description", { ns: "config", playCode: rulePlayCode ?? "—" })}
            </DialogDescription>
          </DialogHeader>
          <Tabs defaultValue="zh" className="w-full">
            <TabsList className="w-full">
              <TabsTrigger value="zh">{t("play.locales.zh", { ns: "config" })}</TabsTrigger>
              <TabsTrigger value="en">{t("play.locales.en", { ns: "config" })}</TabsTrigger>
              <TabsTrigger value="ne">{t("play.locales.ne", { ns: "config" })}</TabsTrigger>
            </TabsList>
            <TabsContent value="zh" className="mt-3">
              <textarea
                id="rule-zh"
                className="border-input bg-background ring-ring/24 focus-visible:ring-[3px] min-h-[140px] w-full rounded-lg border px-3 py-2 text-sm outline-none"
                value={ruleDraftZh}
                onChange={(e) => setRuleDraftZh(e.target.value)}
              />
            </TabsContent>
            <TabsContent value="en" className="mt-3">
              <textarea
                id="rule-en"
                className="border-input bg-background ring-ring/24 focus-visible:ring-[3px] min-h-[140px] w-full rounded-lg border px-3 py-2 text-sm outline-none"
                value={ruleDraftEn}
                onChange={(e) => setRuleDraftEn(e.target.value)}
              />
            </TabsContent>
            <TabsContent value="ne" className="mt-3">
              <textarea
                id="rule-ne"
                className="border-input bg-background ring-ring/24 focus-visible:ring-[3px] min-h-[140px] w-full rounded-lg border px-3 py-2 text-sm outline-none"
                value={ruleDraftNe}
                onChange={(e) => setRuleDraftNe(e.target.value)}
              />
            </TabsContent>
          </Tabs>
          <DialogFooter>
            <Button type="button" variant="outline" onClick={() => setRuleDialogOpen(false)}>
              {t("actions.cancel", { ns: "adminUsers" })}
            </Button>
            <Button type="button" onClick={saveRuleDraft}>
              {t("play.ruleDialog.apply", { ns: "config" })}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <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>
  );
}
