"use client";

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

import {
  deleteOddsVersion,
  getAdminPlayTypes,
  getAllConfigVersions,
  getOddsVersion,
  getOddsVersions,
  postOddsVersion,
  publishOddsVersion,
  putOddsItems,
} from "@/api/admin-config";
import { ConfigDocPage, ConfigDocToolbar } from "@/modules/config/config-doc-page";
import {
  ConfigVersionToolbarMeta,
  ConfigVersionToolbarMetaEmphasis,
} from "@/modules/config/config-version-toolbar-meta";
import { updateAdminSetting } from "@/api/admin-settings";
import { useAsyncEffect } from "@/hooks/use-async-effect";
import { useTranslationRef } from "@/hooks/use-translation-ref";
import {
  loadApplyRebateToPayoutSetting,
  setCachedApplyRebateToPayoutSetting,
} from "@/lib/admin-settlement-settings-cache";
import { ensureAdminPlayTypesLoaded } from "@/lib/admin-play-types";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
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 { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { pickDefaultConfigVersionId } from "@/lib/config-version-auto-pick";
import type { OddsConfigWorkspace } from "@/modules/config/use-odds-config-workspace";
import { PRD_REBATE_MANAGE, PRD_WALLET_RECONCILE_MANAGE } from "@/lib/admin-prd";
import { useAdminProfile } from "@/stores/admin-session";
import { LotteryApiBizError } from "@/types/api/errors";
import type {
  AdminPlayTypeRow,
  ConfigVersionSummary,
  OddsItemRow,
  OddsVersionDetail,
} from "@/types/api/admin-config";

import { ConfigWorkflowSection } from "@/modules/config/config-workflow-section";
import {
  inferRebatePercentFromDimension,
  rateToPercentUi,
} from "@/modules/config/doc/odds-rebate-rates";
import { PRIZE_SCOPE_ORDER } from "@/modules/config/doc/prize-scopes";

const APPLY_REBATE_TO_PAYOUT_KEY = "settlement.apply_rebate_to_payout";

function dimensionDistinctPrimaryScopePercents(
  dim: 2 | 3 | 4,
  rows: OddsItemRow[],
  typeList: AdminPlayTypeRow[],
): Set<string> {
  const codes = typeList
    .filter((t) => (t.dimension ?? 2) === dim)
    .map((t) => t.play_code)
    .sort((a, b) => a.localeCompare(b));
  const scope = PRIZE_SCOPE_ORDER[0];
  const percents = new Set<string>();
  for (const code of codes) {
    const hit = rows.find((r) => r.play_code === code && r.prize_scope === scope);
    if (hit) {
      percents.add(rateToPercentUi(String(hit.rebate_rate)));
    }
  }
  return percents;
}

type RebateConfigDocScreenProps = {
  embedded?: boolean;
  /** 合并页第 3 步卡片 */
  mergedSection?: boolean;
  workspace?: OddsConfigWorkspace;
  versionId?: string;
  onVersionIdChange?: (id: string) => void;
};

export function RebateConfigDocScreen({
  embedded = false,
  mergedSection = false,
  workspace,
  versionId: controlledVersionId,
  onVersionIdChange,
}: RebateConfigDocScreenProps) {
  const { t } = useTranslation(["config", "common"]);
  const tRef = useTranslationRef(["config", "common"]);
  const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
  const profile = useAdminProfile();
  const canManage = adminHasAnyPermission(profile?.permissions, [PRD_REBATE_MANAGE]);
  const canEditWinEnjoy = adminHasAnyPermission(profile?.permissions, [
    PRD_REBATE_MANAGE,
    PRD_WALLET_RECONCILE_MANAGE,
  ]);
  const [applyRebateToPayout, setApplyRebateToPayout] = useState(false);
  const [winEnjoyLoading, setWinEnjoyLoading] = useState(true);
  const [winEnjoySaving, setWinEnjoySaving] = useState(false);
  const formatDt = useAdminDateTimeFormatter();
  const [types, setTypes] = useState<AdminPlayTypeRow[]>([]);
  const [listRows, setListRows] = useState<ConfigVersionSummary[]>([]);

  const [internalSelectedId, setInternalSelectedId] = useState("");
  const selectedId = workspace?.selectedId ?? controlledVersionId ?? internalSelectedId;
  const setSelectedId = workspace?.setSelectedId ?? onVersionIdChange ?? setInternalSelectedId;
  const [detail, setDetail] = useState<OddsVersionDetail | null>(null);
  const [draftRows, setDraftRows] = useState<OddsItemRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [loadingDetail, setLoadingDetail] = useState(false);
  const [saving, setSaving] = useState(false);

  const resolvedTypes = workspace?.types ?? types;
  const resolvedList = workspace?.list ?? listRows;
  const resolvedDetail = workspace?.detail ?? detail;
  const resolvedDraftRows = workspace?.draftRows ?? draftRows;
  const resolvedLoading = workspace ? workspace.loadingList || workspace.loadingTypes : loading;
  const resolvedLoadingDetail = workspace?.loadingDetail ?? loadingDetail;

  const [p2, setP2] = useState("0");
  const [p3, setP3] = useState("0");
  const [p4, setP4] = useState("0");
  const [rollbackOpen, setRollbackOpen] = useState(false);
  const [rollbackTarget, setRollbackTarget] = useState<ConfigVersionSummary | null>(null);

  const refreshTypes = useCallback(async () => {
    try {
      setTypes(await ensureAdminPlayTypesLoaded(getAdminPlayTypes));
    } catch (e) {
      toast.error(
        e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
      );
      setTypes([]);
    }
  }, [tRef]);

  const refreshList = useCallback(async () => {
    try {
      const d = await getAllConfigVersions(getOddsVersions);
      setListRows(d.items);
    } catch (e) {
      toast.error(
        e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
      );
      setListRows([]);
    }
  }, [tRef]);

  useAsyncEffect(() => {
    if (workspace) {
      return;
    }
    void (async () => {
      setLoading(true);
      await Promise.all([refreshTypes(), refreshList()]);
      setLoading(false);
    })();
  }, [workspace]);

  useAsyncEffect(() => {
    void (async () => {
      setWinEnjoyLoading(true);
      try {
        setApplyRebateToPayout(await loadApplyRebateToPayoutSetting());
      } catch (e) {
        toast.error(
          e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
        );
      } finally {
        setWinEnjoyLoading(false);
      }
    })();
  }, []);

  useEffect(() => {
    if (!workspace) {
      return;
    }
    setP2(inferRebatePercentFromDimension(2, workspace.draftRows, workspace.types));
    setP3(inferRebatePercentFromDimension(3, workspace.draftRows, workspace.types));
    setP4(inferRebatePercentFromDimension(4, workspace.draftRows, workspace.types));
  }, [workspace?.draftRows, workspace?.types, workspace]);

  async function handleWinEnjoyChange(checked: boolean): Promise<void> {
    if (!canEditWinEnjoy) {
      return;
    }
    setWinEnjoySaving(true);
    try {
      await updateAdminSetting(APPLY_REBATE_TO_PAYOUT_KEY, checked);
      setCachedApplyRebateToPayoutSetting(checked);
      setApplyRebateToPayout(checked);
      toast.success(t("rebate.winEnjoy.saveSuccess", { ns: "config" }));
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.winEnjoy.saveFailed", { ns: "config" }));
    } finally {
      setWinEnjoySaving(false);
    }
  }

  const loadDetail = useCallback(async (id: number) => {
    setLoadingDetail(true);
    try {
      const typeList = await ensureAdminPlayTypesLoaded(getAdminPlayTypes);
      setTypes(typeList);
      const d = await getOddsVersion(id);
      const rows = d.items.map((it) => ({ ...it }));
      setDetail(d);
      setDraftRows(rows);
      setP2(inferRebatePercentFromDimension(2, rows, typeList));
      setP3(inferRebatePercentFromDimension(3, rows, typeList));
      setP4(inferRebatePercentFromDimension(4, rows, typeList));
    } catch (e) {
      toast.error(
        e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }),
      );
      setDetail(null);
      setDraftRows([]);
    } finally {
      setLoadingDetail(false);
    }
  }, [tRef]);

  useEffect(() => {
    if (workspace) {
      return;
    }
    if (listRows.length === 0) {
      if (selectedId !== "") {
        queueMicrotask(() => {
          setSelectedId("");
          setDetail(null);
          setDraftRows([]);
        });
      }
      return;
    }
    if (selectedId !== "" && listRows.some((x) => String(x.id) === selectedId)) {
      return;
    }
    queueMicrotask(() => {
      const pickId = pickDefaultConfigVersionId(listRows);
      if (pickId) {
        setSelectedId(pickId);
      }
    });
  }, [listRows, selectedId, setSelectedId, workspace]);

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

  const typesByCode = useMemo(() => {
    const m = new Map<string, AdminPlayTypeRow>();
    for (const row of resolvedTypes) {
      m.set(row.play_code, row);
    }
    return m;
  }, [resolvedTypes]);

  const rebateBulkPercentsMixed = useMemo(() => {
    if (resolvedTypes.length === 0 || resolvedDraftRows.length === 0) {
      return false;
    }
    for (const dim of [2, 3, 4] as const) {
      if (dimensionDistinctPrimaryScopePercents(dim, resolvedDraftRows, resolvedTypes).size > 1) {
        return true;
      }
    }
    return false;
  }, [resolvedTypes, resolvedDraftRows]);

  const selectedVersionSummary = useMemo(
    () => resolvedList.find((x) => String(x.id) === selectedId) ?? null,
    [resolvedList, selectedId],
  );
  const isSelectedDetail = resolvedDetail !== null && String(resolvedDetail.id) === selectedId;
  const selectedStatus = isSelectedDetail ? resolvedDetail.status : selectedVersionSummary?.status;
  const isDraft = selectedStatus === "draft";
  const canEditDraft = isDraft && canManage;

  function applyDimensionPercentsToRows(rows: OddsItemRow[]): OddsItemRow[] {
    const r2 = Number.parseFloat(p2);
    const r3 = Number.parseFloat(p3);
    const r4 = Number.parseFloat(p4);
    const rate2 = Number.isFinite(r2) ? r2 / 100 : 0;
    const rate3 = Number.isFinite(r3) ? r3 / 100 : 0;
    const rate4 = Number.isFinite(r4) ? r4 / 100 : 0;
    return rows.map((row) => {
      const t = typesByCode.get(row.play_code);
      const dim = (t?.dimension ?? 2) as 2 | 3 | 4;
      const rate = dim === 4 ? rate4 : dim === 3 ? rate3 : rate2;
      return { ...row, rebate_rate: String(rate) };
    });
  }

  async function handleSave() {
    if (!resolvedDetail || !canEditDraft) {
      return;
    }
    setSaving(true);
    try {
      const nextRows = applyDimensionPercentsToRows(resolvedDraftRows);
      const payload = nextRows.map((r) => ({
        play_code: r.play_code,
        prize_scope: r.prize_scope,
        odds_value: r.odds_value,
        rebate_rate: Number.parseFloat(String(r.rebate_rate)) || 0,
        commission_rate: Number.parseFloat(String(r.commission_rate)) || 0,
        currency_code: r.currency_code,
        extra_config_json: r.extra_config_json,
      }));
      const d = await putOddsItems(resolvedDetail.id, payload);
      const rows = d.items.map((it) => ({ ...it }));
      if (workspace) {
        workspace.applyDetail(d);
      } else {
        setDetail(d);
        setDraftRows(rows);
      }
      setP2(inferRebatePercentFromDimension(2, rows, resolvedTypes));
      setP3(inferRebatePercentFromDimension(3, rows, resolvedTypes));
      setP4(inferRebatePercentFromDimension(4, rows, resolvedTypes));
      toast.success(t("versionActions.saveDraft", { ns: "config" }));
      void (workspace?.refreshList() ?? refreshList());
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("versionActions.saveFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  async function handlePublish() {
    if (!resolvedDetail || !canEditDraft) {
      return;
    }
    setSaving(true);
    try {
      const d = await publishOddsVersion(resolvedDetail.id);
      const rows = d.items.map((it) => ({ ...it }));
      if (workspace) {
        workspace.applyDetail(d);
      } else {
        setDetail(d);
        setDraftRows(rows);
      }
      setP2(inferRebatePercentFromDimension(2, rows, resolvedTypes));
      setP3(inferRebatePercentFromDimension(3, rows, resolvedTypes));
      setP4(inferRebatePercentFromDimension(4, rows, resolvedTypes));
      toast.success(t("rebate.publishSuccess", { ns: "config" }));
      void (workspace?.refreshList() ?? refreshList());
      setSelectedId(String(d.id));
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.publishFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

  async function handleNewDraft() {
    setSaving(true);
    try {
      const active = resolvedList.find((x) => x.status === "active");
      const d = await postOddsVersion({
        reason: `rebate draft ${new Date().toISOString()}`,
        clone_from_version_id: active?.id ?? null,
      });
      toast.success(t("rebate.createDraftSuccess", { ns: "config", version: d.version_no }));
      await (workspace?.refreshList() ?? refreshList());
      setSelectedId(String(d.id));
      const rows = d.items.map((it) => ({ ...it }));
      if (workspace) {
        workspace.applyDetail(d);
      } else {
        setDetail(d);
        setDraftRows(rows);
      }
      setP2(inferRebatePercentFromDimension(2, rows, resolvedTypes));
      setP3(inferRebatePercentFromDimension(3, rows, resolvedTypes));
      setP4(inferRebatePercentFromDimension(4, rows, resolvedTypes));
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.createDraftFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

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

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

  async function handleRollback() {
    if (!rollbackTarget) {
      return;
    }
    setSaving(true);
    try {
      const d = await postOddsVersion({
        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 (workspace?.refreshList() ?? refreshList());
      setSelectedId(String(d.id));
      const rows = d.items.map((it) => ({ ...it }));
      if (workspace) {
        workspace.applyDetail(d);
      } else {
        setDetail(d);
        setDraftRows(rows);
      }
      setP2(inferRebatePercentFromDimension(2, rows, resolvedTypes));
      setP3(inferRebatePercentFromDimension(3, rows, resolvedTypes));
      setP4(inferRebatePercentFromDimension(4, rows, resolvedTypes));
      setRollbackOpen(false);
      setRollbackTarget(null);
    } catch (e) {
      toast.error(e instanceof LotteryApiBizError ? e.message : t("versionActions.rollbackFailed", { ns: "config" }));
    } finally {
      setSaving(false);
    }
  }

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

  const toolbarBlock = embedded ? null : (
    <ConfigDocToolbar
      switcher={
        <ConfigVersionSwitcher
          versions={resolvedList}
          selectedId={selectedId}
          onSelectedIdChange={setSelectedId}
          loading={resolvedLoading}

          onDeleteVersion={handleDeleteVersion}
          onRollbackVersion={requestRollback}
          rollbackBusy={saving}
        />
      }
      actions={
        <ConfigVersionActions
          isDraft={isDraft}
          canManage={canManage}
          loadingList={resolvedLoading}
          loadingDetail={resolvedLoadingDetail}
          saving={saving}
          publishLabel={t("rebate.publishLabel", { ns: "config" })}
          onRefresh={() => void refreshList()}
          onNewDraft={() => void handleNewDraft()}
          onSaveDraft={() => void handleSave()}
          onPublish={() =>
            requestConfirm({
              title: t("rebate.publishDialog.title", { ns: "config" }),
              description: t("rebate.publishDialog.description", { ns: "config" }),
              confirmLabel: t("rebate.publishDialog.confirm", { ns: "config" }),
              confirmVariant: "destructive",
              onConfirm: () => handlePublish(),
            })
          }
        />
      }
      footer={
        embedded || !resolvedDetail ? null : (
          <ConfigVersionToolbarMeta emphasis={!isDraft}>
            <span>
              {t("rebate.editingVersion", {
                ns: "config",
                version: resolvedDetail.version_no,
                status:
                  resolvedDetail.status === "draft"
                    ? t("versionStatus.draft", { ns: "config" })
                    : resolvedDetail.status === "active"
                      ? t("versionStatus.active", { ns: "config" })
                      : t("versionStatus.archived", { ns: "config" }),
              })}
            </span>
            {!isDraft ? (
              <ConfigVersionToolbarMetaEmphasis>
                {t("rebate.readOnlyHint", { ns: "config" })}
              </ConfigVersionToolbarMetaEmphasis>
            ) : (
              <span>{t("versionToolbar.draftEditing", { ns: "config" })}</span>
            )}
          </ConfigVersionToolbarMeta>
        )
      }
    />
  );

  const fieldsBlock = (
    <>
      {rebateBulkPercentsMixed ? (
        <Alert className="border-amber-500/35 bg-amber-500/10 text-amber-950 dark:text-amber-100">
          <AlertDescription>{t("rebate.dimensionRatesMixedHint", { ns: "config" })}</AlertDescription>
        </Alert>
      ) : null}
      <div className="grid gap-5 sm:grid-cols-3">
          <div className="grid gap-2">
            <Label>{t("rebate.fields.d2", { ns: "config" })}</Label>
            {canEditDraft ? (
              <Input
                type="number"
                step="0.01"
                min={0}
                className="h-9 text-base font-semibold"
                disabled={saving}
                value={p2}
                placeholder={t("rebate.placeholders.d2", { ns: "config" })}
                onChange={(e) => setP2(e.target.value)}
              />
            ) : (
              <ConfigReadonlyValue className="text-base font-semibold">{p2}</ConfigReadonlyValue>
            )}
          </div>
          <div className="grid gap-2">
            <Label>{t("rebate.fields.d3", { ns: "config" })}</Label>
            {canEditDraft ? (
              <Input
                type="number"
                step="0.01"
                min={0}
                className="h-9 text-base font-semibold"
                disabled={saving}
                value={p3}
                placeholder={t("rebate.placeholders.d3", { ns: "config" })}
                onChange={(e) => setP3(e.target.value)}
              />
            ) : (
              <ConfigReadonlyValue className="text-base font-semibold">{p3}</ConfigReadonlyValue>
            )}
          </div>
          <div className="grid gap-2">
            <Label>{t("rebate.fields.d4", { ns: "config" })}</Label>
            {canEditDraft ? (
              <Input
                type="number"
                step="0.01"
                min={0}
                className="h-9 text-base font-semibold"
                disabled={saving}
                value={p4}
                placeholder={t("rebate.placeholders.d4", { ns: "config" })}
                onChange={(e) => setP4(e.target.value)}
              />
            ) : (
              <ConfigReadonlyValue className="text-base font-semibold">{p4}</ConfigReadonlyValue>
            )}
          </div>
        </div>

        <div className="rounded-xl border border-border/60 px-4 py-3">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <div className="min-w-0 space-y-1">
              <p className="text-sm font-medium">{t("rebate.winEnjoy.label", { ns: "config" })}</p>
              <p className="text-xs text-muted-foreground">{t("rebate.winEnjoy.description", { ns: "config" })}</p>
            </div>
            <Switch
              checked={applyRebateToPayout}
              disabled={winEnjoyLoading || winEnjoySaving || !canEditWinEnjoy}
              aria-label={t("rebate.winEnjoy.label", { ns: "config" })}
              onCheckedChange={(value) => void handleWinEnjoyChange(value)}
            />
          </div>
          <p className="mt-2 text-xs text-muted-foreground">{t("rebate.winEnjoy.hint", { ns: "config" })}</p>
        </div>

      {!embedded ? (
        <div className="grid gap-1 text-sm">
          <span className="text-muted-foreground">{t("rebate.effectiveTime", { ns: "config" })}</span>
          <span className="font-mono text-sm">
            {activeHead?.effective_at ? formatDt(activeHead.effective_at) : "—"}
          </span>
        </div>
      ) : null}

      {resolvedLoading || resolvedLoadingDetail ? (
        <AdminLoadingState minHeight="6rem" className="py-6" />
      ) : null}
    </>
  );

  const rollbackDialog = (
    <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>
  );

  if (embedded && mergedSection) {
    return (
      <>
        <ConfigWorkflowSection
          step={3}
          title={t("nav.items.rebate", { ns: "config" })}
          description={t("rebate.sectionHint", { ns: "config" })}
          contentClassName="space-y-5"
        >
          {fieldsBlock}
        </ConfigWorkflowSection>
        {rollbackDialog}
        <ConfirmDialog />
      </>
    );
  }

  if (embedded) {
    return (
      <div className="space-y-4">
        {fieldsBlock}
        {rollbackDialog}
        <ConfirmDialog />
      </div>
    );
  }

  return (
    <ConfigDocPage
      title={t("nav.items.rebate", { ns: "config" })}
      toolbar={toolbarBlock}
    >
      {fieldsBlock}
      {rollbackDialog}
      <ConfirmDialog />
    </ConfigDocPage>
  );
}
