import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
    addGameWhitelistUser,
    deleteGameCatalog,
    deleteGameWhitelistUser,
    listGameCatalog,
    listGamePlatforms,
    listGameWhitelistUsers,
    setGameStatus,
    setGameWhitelistEnabled,
    syncGamePlatformCatalog,
    upsertGameCatalog,
    upsertGamePlatform,
} from "@/features/games/api";
import { useGameAbilities } from "@/features/games/permissions.js";
import { useAdminQuery } from "@/shared/hooks/useAdminQuery.js";
import { useConfirm } from "@/shared/ui/ConfirmProvider.jsx";
import { useToast } from "@/shared/ui/ToastProvider.jsx";
import {
    adapterDefinition,
    normalizeAdapterCallbackSecret,
    normalizeAdapterConfigJson,
    writableBridgeAdapterTypes,
} from "@/features/games/adapterConfigModel.js";

const defaultGameForm = {
    gameId: "",
    platformCode: "",
    providerGameId: "",
    gameName: "",
    category: "casual",
    iconUrl: "",
    coverUrl: "",
    gameUrl: "",
    launchMode: "full_screen",
    orientation: "portrait",
    safeHeight: 0,
    minCoin: 0,
    status: "active",
    sortOrder: 0,
    tagsText: "",
};

const defaultPlatformForm = {
    platformCode: "",
    platformName: "",
    status: "active",
    apiBaseUrl: "",
    // 新建平台必须选择真实厂商协议，避免把线上配置误落成 demo。
    adapterType: "yomi_v4",
    initialAdapterType: "yomi_v4",
    // 已保存密钥只回显“已配置”，不会重新进入表单状态；提交空值仍代表“不覆盖旧值”。
    callbackSecret: "",
    callbackSecretSet: false,
    callbackIpWhitelistText: "",
    // 厂商扩展配置以 JSON 透传，服务端按 adapter_type 自己解析需要的字段。
    adapterConfigJson: "{}",
    adapterDrafts: {},
    sortOrder: 0,
};

const defaultBridgeScriptForm = {
    bridgeScriptUrl: "",
    bridgeScriptVersion: "",
};

const emptyCatalog = { items: [], pageSize: 50 };
const emptyPlatforms = { items: [] };
const defaultPageSize = 50;

export function useGamesPage() {
    const abilities = useGameAbilities();
    const confirm = useConfirm();
    const { showToast } = useToast();
    const [platformCode, setPlatformCode] = useState("");
    const [status, setStatus] = useState("");
    const [pageSize, setPageSize] = useState(defaultPageSize);
    const [catalogItems, setCatalogItems] = useState([]);
    const [catalogMeta, setCatalogMeta] = useState(emptyCatalog);
    const [catalogError, setCatalogError] = useState("");
    const [catalogLoading, setCatalogLoading] = useState(true);
    const [catalogLoadingMore, setCatalogLoadingMore] = useState(false);
    const [activeAction, setActiveAction] = useState("");
    const [editingGameId, setEditingGameId] = useState("");
    const [editingPlatformCode, setEditingPlatformCode] = useState("");
    const [gameForm, setGameForm] = useState(defaultGameForm);
    const [platformForm, setPlatformForm] = useState(defaultPlatformForm);
    const [bridgeScriptForm, setBridgeScriptForm] = useState(defaultBridgeScriptForm);
    const [loadingAction, setLoadingAction] = useState("");
    const [syncPlatform, setSyncPlatform] = useState(null);
    const [syncCandidates, setSyncCandidates] = useState([]);
    const [selectedSyncGameIds, setSelectedSyncGameIds] = useState([]);
    const [whitelistGame, setWhitelistGame] = useState(null);
    const [whitelistItems, setWhitelistItems] = useState([]);
    const [whitelistUserId, setWhitelistUserId] = useState("");
    const [whitelistLoading, setWhitelistLoading] = useState(false);
    const catalogRequestRef = useRef(0);

    const platformsQueryFn = useCallback(() => listGamePlatforms(), []);
    const {
        data: platformData = emptyPlatforms,
        error: platformError,
        loading: platformsLoading,
        reload: reloadPlatforms,
    } = useAdminQuery(platformsQueryFn, {
        errorMessage: "加载游戏平台失败",
        initialData: emptyPlatforms,
        queryKey: ["games", "platforms"],
    });

    const loadCatalog = useCallback(
        async (cursor = "", append = false) => {
            const requestId = catalogRequestRef.current + 1;
            catalogRequestRef.current = requestId;
            if (append) {
                setCatalogLoadingMore(true);
            } else {
                setCatalogLoading(true);
            }
            setCatalogError("");

            try {
                const result = await listGameCatalog({ cursor, platformCode, status, pageSize });
                if (catalogRequestRef.current !== requestId) {
                    return result;
                }
                setCatalogMeta(result);
                setCatalogItems((current) =>
                    append ? mergeCatalogItems(current, result.items || []) : result.items || [],
                );
                return result;
            } catch (err) {
                if (catalogRequestRef.current === requestId) {
                    setCatalogError(err.message || "加载游戏列表失败");
                }
                return null;
            } finally {
                if (catalogRequestRef.current === requestId) {
                    setCatalogLoading(false);
                    setCatalogLoadingMore(false);
                }
            }
        },
        [pageSize, platformCode, status],
    );

    useEffect(() => {
        loadCatalog("", false);
    }, [loadCatalog]);

    const platformOptions = useMemo(
        () => (platformData.items || []).map((platform) => [platform.platformCode, platform.platformName]),
        [platformData.items],
    );
    const resolveGameURL = useCallback(
        (game) => resolveGameURLFromPlatforms(platformData.items || [], game),
        [platformData.items],
    );

    useEffect(() => {
        if (!gameForm.platformCode && platformOptions.length > 0 && activeAction === "create-game") {
            setGameForm((current) => ({ ...current, platformCode: platformOptions[0][0] }));
        }
    }, [activeAction, gameForm.platformCode, platformOptions]);

    const resetCatalogPage = () => {
        setCatalogItems([]);
        setCatalogMeta(emptyCatalog);
    };

    const changePlatformCode = (value) => {
        setPlatformCode(value);
        resetCatalogPage();
    };

    const changeStatus = (value) => {
        setStatus(value);
        resetCatalogPage();
    };

    const changePageSize = (value) => {
        setPageSize(value);
        resetCatalogPage();
    };

    const loadNextPage = () => {
        if (!catalogMeta.nextCursor || catalogLoading || catalogLoadingMore) {
            return null;
        }
        return loadCatalog(catalogMeta.nextCursor, true);
    };

    const reloadCatalog = () => {
        return loadCatalog("", false);
    };

    const resetFilters = () => {
        setPlatformCode("");
        setStatus("");
        setPageSize(defaultPageSize);
        resetCatalogPage();
    };

    const openCreateGame = () => {
        setEditingGameId("");
        setGameForm({ ...defaultGameForm, platformCode: platformOptions[0]?.[0] || "" });
        setActiveAction("create-game");
    };

    const openEditGame = (game) => {
        setEditingGameId(game.gameId);
        setGameForm(gameToForm(game, platformData.items || []));
        setActiveAction("edit-game");
    };

    const loadWhitelist = async (game) => {
        if (!game?.gameId) {
            return;
        }
        setWhitelistLoading(true);
        try {
            const result = await listGameWhitelistUsers(game.gameId);
            setWhitelistItems(result.items || []);
        } catch (err) {
            showToast(err.message || "加载游戏白名单失败", "error");
        } finally {
            setWhitelistLoading(false);
        }
    };

    const openWhitelist = (game) => {
        setWhitelistGame(game);
        setWhitelistItems([]);
        setWhitelistUserId("");
        setActiveAction("game-whitelist");
        loadWhitelist(game);
    };

    const openCreatePlatform = () => {
        setEditingPlatformCode("");
        setPlatformForm(defaultPlatformForm);
        setActiveAction("create-platform");
    };

    const openManagePlatforms = () => {
        setActiveAction("manage-platforms");
    };

    const openBridgeScript = () => {
        setBridgeScriptForm(bridgeScriptFormFromPlatforms(platformData.items || []));
        setActiveAction("bridge-script");
    };

    const openEditPlatform = (platform) => {
        setEditingPlatformCode(platform.platformCode);
        setPlatformForm(platformToForm(platform));
        setActiveAction("edit-platform");
    };

    const closeAction = () => {
        if (activeAction === "sync-games") {
            setSyncPlatform(null);
            setSyncCandidates([]);
            setSelectedSyncGameIds([]);
        }
        if (activeAction === "bridge-script") {
            setBridgeScriptForm(defaultBridgeScriptForm);
        }
        if (activeAction === "game-whitelist") {
            setWhitelistGame(null);
            setWhitelistItems([]);
            setWhitelistUserId("");
        }
        setActiveAction("");
        setEditingGameId("");
        setEditingPlatformCode("");
    };

    const submitGame = async (event) => {
        event.preventDefault();
        const payload = gamePayload(gameForm);
        setLoadingAction(activeAction);
        try {
            await upsertGameCatalog(payload, editingGameId);
            await saveGameUrl(platformData.items || [], payload, gameForm.gameUrl);
            await reloadPlatforms();
            await reloadCatalog();
            showToast(editingGameId ? "游戏已更新" : "游戏已创建", "success");
            closeAction();
        } catch (err) {
            showToast(err.message || "保存游戏失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const submitPlatform = async (event) => {
        event.preventDefault();
        let payload;
        try {
            payload = platformPayload(platformForm, Boolean(editingPlatformCode));
        } catch (err) {
            showToast(err.message || "适配器配置 JSON 不正确", "error");
            return;
        }
        setLoadingAction(activeAction);
        try {
            await upsertGamePlatform(payload, editingPlatformCode);
            await reloadPlatforms();
            await reloadCatalog();
            showToast(editingPlatformCode ? "平台已更新" : "平台已创建", "success");
            closeAction();
        } catch (err) {
            showToast(err.message || "保存平台失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const submitBridgeScript = async (event) => {
        event.preventDefault();
        const platforms = platformData.items || [];
        if (platforms.length === 0) {
            showToast("暂无游戏平台可写入", "error");
            return;
        }
        const bridgeScriptUrl = bridgeScriptForm.bridgeScriptUrl.trim();
        const bridgeScriptVersion = bridgeScriptForm.bridgeScriptVersion.trim();
        if (!bridgeScriptUrl || !bridgeScriptVersion) {
            showToast("请填写 bridge_script_url 和 bridge_script_version", "error");
            return;
        }
        const writablePlatforms = platforms.filter(isWritableBridgePlatform);
        const skippedPlatforms = platforms.filter((platform) => !isWritableBridgePlatform(platform));
        if (writablePlatforms.length === 0) {
            showToast("没有可写入的有效游戏平台", "error");
            return;
        }
        setLoadingAction("bridge-script");
        try {
            await Promise.all(
                writablePlatforms.map((platform) => {
                    const config = parseJsonObject(platform.adapterConfigJson);
                    config.bridge_script_url = bridgeScriptUrl;
                    config.bridge_script_version = bridgeScriptVersion;
                    delete config.bridgeScriptUrl;
                    delete config.bridgeScriptVersion;
                    delete config.bridge_script_sha256;
                    delete config.bridgeScriptSha256;
                    delete config.bridge_script_hash;
                    delete config.script_sha256;
                    delete config.scriptSha256;
                    return upsertGamePlatform(
                        platformUpsertPayload(platform, JSON.stringify(config, null, 2)),
                        platform.platformCode,
                    );
                }),
            );
            await reloadPlatforms();
            await reloadCatalog();
            const skippedMessage =
                skippedPlatforms.length > 0
                    ? `，跳过 ${skippedPlatforms.length} 个无效适配器平台：${skippedPlatforms
                          .map((platform) => platform.platformCode || platform.platformName || platform.adapterType)
                          .slice(0, 3)
                          .join(", ")}`
                    : "";
            showToast(`JS 桥接配置已同步 ${writablePlatforms.length} 个平台${skippedMessage}`, "success");
            closeAction();
        } catch (err) {
            showToast(err.message || "保存 JS 桥接配置失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const changeGameStatus = async (game, checked) => {
        const nextStatus = checked ? "active" : "maintenance";
        setLoadingAction(`status-${game.gameId}`);
        try {
            await setGameStatus(game.gameId, nextStatus);
            await reloadCatalog();
            showToast("游戏状态已更新", "success");
        } catch (err) {
            showToast(err.message || "状态更新失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const deleteGame = async (game) => {
        const ok = await confirm({
            confirmText: "删除",
            message: `删除后 App 游戏列表不再展示「${game.gameName || game.gameId}」，历史订单和启动记录会保留用于审计。`,
            title: "删除游戏",
            tone: "danger",
        });
        if (!ok) {
            return;
        }
        setLoadingAction(`delete-${game.gameId}`);
        try {
            await deleteGameCatalog(game.gameId);
            await reloadCatalog();
            showToast("游戏已删除", "success");
        } catch (err) {
            showToast(err.message || "删除游戏失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const changeWhitelistEnabled = async (checked) => {
        if (!whitelistGame) {
            return;
        }
        setLoadingAction(`whitelist-enabled-${whitelistGame.gameId}`);
        try {
            const updated = await setGameWhitelistEnabled(whitelistGame.gameId, checked);
            setWhitelistGame((current) =>
                current ? { ...current, whitelistEnabled: updated.whitelistEnabled } : current,
            );
            setCatalogItems((current) =>
                current.map((game) =>
                    game.gameId === whitelistGame.gameId
                        ? { ...game, whitelistEnabled: updated.whitelistEnabled }
                        : game,
                ),
            );
            showToast(checked ? "游戏白名单已开启" : "游戏白名单已关闭", "success");
        } catch (err) {
            showToast(err.message || "白名单状态更新失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const addWhitelistUser = async (event) => {
        event.preventDefault();
        const userIdentifier = whitelistUserId.trim();
        if (!whitelistGame || !userIdentifier) {
            showToast("请输入用户 ID、短号或靓号", "error");
            return;
        }
        setLoadingAction(`whitelist-add-${whitelistGame.gameId}`);
        try {
            const matchedUser = await addGameWhitelistUser(whitelistGame.gameId, userIdentifier);
            setWhitelistUserId("");
            await loadWhitelist(whitelistGame);
            showToast(`已匹配用户 ${matchedUser.displayUserId || matchedUser.userId} 并加入白名单`, "success");
        } catch (err) {
            showToast(err.message || "添加白名单用户失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const removeWhitelistUser = async (item) => {
        if (!whitelistGame) {
            return;
        }
        const ok = await confirm({
            confirmText: "移除",
            message: `移除用户 ${item.displayUserId || item.userId} 后，该用户将看不到且无法启动此游戏。`,
            title: "移除白名单用户",
            tone: "danger",
        });
        if (!ok) {
            return;
        }
        setLoadingAction(`whitelist-delete-${item.userId}`);
        try {
            await deleteGameWhitelistUser(whitelistGame.gameId, item.userId);
            setWhitelistItems((current) => current.filter((user) => user.userId !== item.userId));
            showToast("用户已移出白名单", "success");
        } catch (err) {
            showToast(err.message || "移除白名单用户失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const fetchPlatformGames = async (platform) => {
        setLoadingAction(`fetch-games-${platform.platformCode}`);
        try {
            const result = await syncGamePlatformCatalog(platform.platformCode, {
                dryRun: true,
                status: "disabled",
            });
            setSyncPlatform(platform);
            setSyncCandidates(result.items || []);
            setSelectedSyncGameIds([]);
            setActiveAction("sync-games");
            showToast(`已获取 ${result.items?.length || 0} 个游戏`, "success");
        } catch (err) {
            showToast(err.message || "获取厂商游戏列表失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const syncCandidateIds = useMemo(
        () => syncCandidates.map((game) => game.providerGameId).filter(Boolean),
        [syncCandidates],
    );
    const allSyncGamesSelected = syncCandidateIds.length > 0 && selectedSyncGameIds.length === syncCandidateIds.length;

    const toggleAllSyncGames = () => {
        setSelectedSyncGameIds(allSyncGamesSelected ? [] : syncCandidateIds);
    };

    const toggleSyncGame = (providerGameId) => {
        setSelectedSyncGameIds((current) =>
            current.includes(providerGameId)
                ? current.filter((item) => item !== providerGameId)
                : [...current, providerGameId],
        );
    };

    const importSelectedSyncGames = async (event) => {
        event.preventDefault();
        if (!syncPlatform || selectedSyncGameIds.length === 0) {
            showToast("请选择要添加的游戏", "error");
            return;
        }
        setLoadingAction("import-sync-games");
        try {
            const result = await syncGamePlatformCatalog(syncPlatform.platformCode, {
                providerGameIds: selectedSyncGameIds,
                status: "disabled",
            });
            await reloadPlatforms();
            await reloadCatalog();
            showToast(`已添加 ${result.synced} 个游戏，默认下架`, "success");
            closeAction();
        } catch (err) {
            showToast(err.message || "添加游戏失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    const importSingleSyncGame = async (game) => {
        if (!syncPlatform || !game.providerGameId) {
            return;
        }
        setLoadingAction(`import-sync-game-${game.providerGameId}`);
        try {
            await syncGamePlatformCatalog(syncPlatform.platformCode, {
                providerGameIds: [game.providerGameId],
                status: "disabled",
            });
            await reloadPlatforms();
            await reloadCatalog();
            setSelectedSyncGameIds((current) => current.filter((item) => item !== game.providerGameId));
            setSyncCandidates((current) => current.filter((item) => item.providerGameId !== game.providerGameId));
            showToast("游戏已添加，默认下架", "success");
        } catch (err) {
            showToast(err.message || "添加游戏失败", "error");
        } finally {
            setLoadingAction("");
        }
    };

    return {
        abilities,
        activeAction,
        allSyncGamesSelected,
        bridgeScriptForm,
        addWhitelistUser,
        changeGameStatus,
        changeWhitelistEnabled,
        deleteGame,
        closeAction,
        data: {
            ...catalogMeta,
            items: catalogItems,
            loadedCount: catalogItems.length,
            loadedPages: Math.max(1, Math.ceil(catalogItems.length / pageSize)),
        },
        error: catalogError || platformError,
        gameForm,
        loading: (catalogLoading && catalogItems.length === 0) || platformsLoading,
        loadingAction,
        loadingMore: catalogLoadingMore || (catalogLoading && catalogItems.length > 0),
        page: Math.max(1, Math.ceil(catalogItems.length / pageSize)),
        fetchPlatformGames,
        importSelectedSyncGames,
        importSingleSyncGame,
        openCreateGame,
        openCreatePlatform,
        openBridgeScript,
        openEditGame,
        openEditPlatform,
        openWhitelist,
        openManagePlatforms,
        pageSize,
        platformCode,
        platformData,
        platformForm,
        platformOptions,
        loadNextPage,
        reload: reloadCatalog,
        resetFilters,
        removeWhitelistUser,
        resolveGameURL,
        changePageSize,
        setGameForm,
        setBridgeScriptForm,
        setPlatformCode: changePlatformCode,
        setPlatformForm,
        setStatus: changeStatus,
        status,
        submitBridgeScript,
        submitGame,
        submitPlatform,
        syncCandidates,
        syncPlatform,
        selectedSyncGameIds,
        toggleAllSyncGames,
        toggleSyncGame,
        whitelistGame,
        whitelistItems,
        whitelistLoading,
        whitelistUserId,
        setWhitelistUserId,
    };
}

function mergeCatalogItems(current, nextItems) {
    const seen = new Set();
    return [...current, ...nextItems].filter((item) => {
        const key = item.gameId || `${item.platformCode}:${item.providerGameId}`;
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}

function bridgeScriptFormFromPlatform(platform) {
    if (!platform) {
        return defaultBridgeScriptForm;
    }
    const config = parseJsonObject(platform.adapterConfigJson);
    return {
        bridgeScriptUrl: readConfigString(config.bridge_script_url ?? config.bridgeScriptUrl),
        bridgeScriptVersion: readConfigString(config.bridge_script_version ?? config.bridgeScriptVersion),
    };
}

function bridgeScriptFormFromPlatforms(platforms) {
    for (const platform of platforms) {
        const form = bridgeScriptFormFromPlatform(platform);
        if (form.bridgeScriptUrl || form.bridgeScriptVersion) {
            return form;
        }
    }
    return defaultBridgeScriptForm;
}

function readConfigString(value) {
    if (value === null || value === undefined) {
        return "";
    }
    return String(value).trim();
}

function isWritableBridgePlatform(platform) {
    return writableBridgeAdapterTypes.has(
        String(platform?.adapterType || "")
            .trim()
            .toLowerCase(),
    );
}

function platformUpsertPayload(platform, adapterConfigJson) {
    return {
        platformCode: platform.platformCode,
        platformName: platform.platformName,
        status: platform.status,
        apiBaseUrl: platform.apiBaseUrl,
        adapterType: platform.adapterType,
        callbackSecret: "",
        callbackIpWhitelist: platform.callbackIpWhitelist || [],
        adapterConfigJson,
        sortOrder: Number(platform.sortOrder || 0),
    };
}

async function saveGameUrl(platforms, game, gameUrl) {
    const platform = platforms.find((item) => item.platformCode === game.platformCode);
    if (!platform) {
        return;
    }
    const nextURL = String(gameUrl || "").trim();
    const currentURL = resolveGameURLFromPlatforms(platforms, game);
    if (nextURL === currentURL) {
        return;
    }

    const config = parseJsonObject(platform.adapterConfigJson);
    const gameURLs =
        config.game_urls && typeof config.game_urls === "object" && !Array.isArray(config.game_urls)
            ? { ...config.game_urls }
            : {};
    const keys = gameUrlKeys(game);
    for (const key of keys) {
        delete gameURLs[key];
    }
    if (nextURL) {
        gameURLs[keys[0]] = nextURL;
    }
    if (Object.keys(gameURLs).length > 0) {
        config.game_urls = gameURLs;
    } else {
        delete config.game_urls;
    }

    await upsertGamePlatform(platformUpsertPayload(platform, JSON.stringify(config, null, 2)), platform.platformCode);
}

function gameToForm(game, platforms = []) {
    return {
        gameId: game.gameId || "",
        platformCode: game.platformCode || "",
        providerGameId: game.providerGameId || "",
        gameName: game.gameName || "",
        category: game.category || "casual",
        iconUrl: game.iconUrl || "",
        coverUrl: game.coverUrl || "",
        gameUrl: resolveGameURLFromPlatforms(platforms, game),
        launchMode: normalizeLaunchMode(game.launchMode),
        orientation: game.orientation || "portrait",
        safeHeight: Number(game.safeHeight || 0),
        minCoin: Number(game.minCoin || 0),
        status: game.status || "active",
        sortOrder: Number(game.sortOrder || 0),
        tagsText: (game.tags || []).join(", "),
    };
}

function resolveGameURLFromPlatforms(platforms, game) {
    const platform = platforms.find((item) => item.platformCode === game.platformCode);
    if (!platform) {
        return "";
    }
    const config = parseJsonObject(platform.adapterConfigJson);
    const gameURLs = config.game_urls && typeof config.game_urls === "object" ? config.game_urls : {};
    for (const key of gameUrlKeys(game)) {
        const value = gameURLs[key];
        if (typeof value === "string" && value.trim()) {
            return value.trim();
        }
    }
    return "";
}

function gameUrlKeys(game) {
    const keys = [game.providerGameId, game.gameId, String(game.gameId || "").toLowerCase()]
        .map((value) => String(value || "").trim())
        .filter(Boolean);
    return [...new Set(keys)];
}

function parseJsonObject(raw) {
    try {
        const value = JSON.parse(raw || "{}");
        return value && typeof value === "object" && !Array.isArray(value) ? value : {};
    } catch {
        return {};
    }
}

function platformToForm(platform) {
    return {
        platformCode: platform.platformCode || "",
        platformName: platform.platformName || "",
        status: platform.status || "active",
        apiBaseUrl: platform.apiBaseUrl || "",
        adapterType: platform.adapterType || "yomi_v4",
        initialAdapterType: platform.adapterType || "yomi_v4",
        // 运营要求密钥明文可核对；后端有返回时直接进入编辑框，没有返回时仍保留“已配置”状态语义。
        callbackSecret: platform.callbackSecret || "",
        callbackSecretSet: Boolean(platform.callbackSecretSet),
        callbackIpWhitelistText: (platform.callbackIpWhitelist || []).join("\n"),
        adapterConfigJson: platform.adapterConfigJson || "{}",
        adapterDrafts: {},
        sortOrder: Number(platform.sortOrder || 0),
    };
}

function gamePayload(form) {
    return {
        gameId: form.gameId.trim(),
        platformCode: form.platformCode.trim(),
        providerGameId: form.providerGameId.trim(),
        gameName: form.gameName.trim(),
        category: form.category.trim(),
        iconUrl: form.iconUrl.trim(),
        coverUrl: form.coverUrl.trim(),
        launchMode: normalizeLaunchMode(form.launchMode),
        orientation: form.orientation.trim() || "portrait",
        safeHeight: Number(form.safeHeight || 0),
        minCoin: Number(form.minCoin || 0),
        status: form.status.trim() || "active",
        sortOrder: Number(form.sortOrder || 0),
        tags: form.tagsText
            .split(",")
            .map((tag) => tag.trim())
            .filter(Boolean),
    };
}

function normalizeLaunchMode(value) {
    const mode = String(value || "").trim();
    if (mode === "half_screen" || mode === "three_quarter_screen" || mode === "full_screen") {
        return mode;
    }
    return "full_screen";
}

function platformPayload(form, editing) {
    const adapterType = form.adapterType.trim() || "yomi_v4";
    const definition = adapterDefinition(adapterType);
    const status = form.status.trim() || "active";
    const callbackSecret = normalizeAdapterCallbackSecret(form, editing);
    // required 属性只能拦截空字符串，纯空格提交后会被 trim 成空并触发后端“保留旧密钥”；提交层必须再次校验。
    return {
        platformCode: form.platformCode.trim(),
        platformName: form.platformName.trim(),
        status,
        apiBaseUrl: definition.apiUrl === false ? "" : form.apiBaseUrl.trim(),
        adapterType,
        // 空密钥提交给后端后会保留旧值；非空才覆盖，用于安全轮换 key/AppSecret。
        callbackSecret,
        // 支持换行或逗号分隔，方便直接粘贴厂商给的多 IP 白名单。
        callbackIpWhitelist: definition.ipWhitelist
            ? form.callbackIpWhitelistText
                  .split(/[\n,]/)
                  .map((item) => item.trim())
                  .filter(Boolean)
            : [],
        // 可视字段只 patch 当前 key；提交时再统一类型与历史别名，未知字段、游戏目录和桥接配置原样保留。
        adapterConfigJson: normalizeAdapterConfigJson(form.adapterConfigJson, adapterType),
        sortOrder: Number(form.sortOrder || 0),
    };
}
