import { apiRequest } from "@/shared/api/request";
import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints";
import type { QueryParams } from "@/shared/api/request";

export interface GamePlatformDto {
    appCode?: string;
    platformCode: string;
    platformName: string;
    status: string;
    apiBaseUrl: string;
    // 服务端按 adapterType 选择真实厂商协议；AMG 使用 amg_v1，RSA 私钥仍通过 callbackSecret 单独提交。
    adapterType: string;
    // callbackSecret 由后台列表返回，用于配置页直接核对和轮换厂商 key。
    callbackSecret?: string;
    callbackSecretSet: boolean;
    callbackIpWhitelist: string[];
    // JSON 是服务端传输格式；配置页会按 adapterType 显示结构化字段，并保留未来新增的未知 key。
    adapterConfigJson: string;
    sortOrder: number;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface GameCatalogDto {
    appCode?: string;
    gameId: string;
    platformCode: string;
    providerGameId: string;
    gameName: string;
    category: string;
    iconUrl: string;
    coverUrl: string;
    launchUrl?: string;
    launchMode: string;
    orientation: string;
    safeHeight: number;
    minCoin: number;
    status: string;
    sortOrder: number;
    tags: string[];
    createdAtMs?: number;
    updatedAtMs?: number;
    whitelistEnabled: boolean;
}

export interface GameWhitelistUserDto {
    userId: string;
    displayUserId?: string;
    username?: string;
    avatar?: string;
    status?: string;
    createdByAdminId?: number;
    createdAtMs?: number;
}

export interface GameCatalogPage {
    items: GameCatalogDto[];
    nextCursor?: string;
    pageSize?: number;
    serverTimeMs?: number;
    total?: number;
    totalPages?: number;
}

export interface GamePlatformPage {
    items: GamePlatformDto[];
    serverTimeMs?: number;
}

export interface GameSyncPayload {
    dryRun?: boolean;
    status?: string;
    category?: string;
    launchMode?: string;
    minCoin?: number;
    tags?: string[];
    gameListType?: number;
    providerGameIds?: string[];
}

export interface GameSyncResult {
    platformCode: string;
    synced: number;
    dryRun: boolean;
    items: GameCatalogDto[];
}

export interface DiceStakeOptionDto {
    stakeCoin: number;
    enabled: boolean;
    sortOrder: number;
}

export interface DiceConfigDto {
    appCode?: string;
    gameId: string;
    status: string;
    stakeOptions: DiceStakeOptionDto[];
    feeBps: number;
    poolBps: number;
    minPlayers: number;
    maxPlayers: number;
    robotEnabled: boolean;
    robotMatchWaitMs: number;
    poolBalanceCoin: number;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface SelfGameNewUserPolicyDto {
    appCode?: string;
    gameId: string;
    enabled: boolean;
    protectionRounds: number;
    protectionHours: number;
    maxStakeCoin: number;
    lifetimeSubsidyQuotaCoin: number;
    day1SubsidyQuotaCoin: number;
    singleRoundSubsidyCapCoin: number;
    strategyLevel: string;
    loseStreakProtectionEnabled: boolean;
    loseStreakTrigger: number;
    normalPhaseTargetWinRatePercent: number;
    blackPoolRobotForceWinEnabled: boolean;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface SelfGameStakePoolDto {
    appCode?: string;
    gameId: string;
    stakeCoin: number;
    balanceCoin: number;
    reservedCoin: number;
    availableCoin: number;
    targetBalanceCoin: number;
    safeBalanceCoin: number;
    warningBalanceCoin: number;
    dangerBalanceCoin: number;
    status: string;
    poolLevel: string;
    requiredPoolCoin: number;
    humanWinCapacity: number;
    botMatchEnabled: boolean;
    blackReason?: string;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface DiceRobotDto {
    appCode?: string;
    gameId: string;
    userId: string;
    userIdNumber?: number;
    shortId?: string;
    nickname?: string;
    avatar?: string;
    status: string;
    createdByAdminId?: number;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface DiceRobotPage {
    items: DiceRobotDto[];
    nextCursor?: string;
    pageSize?: number;
    serverTimeMs?: number;
}

export interface RoomRpsStakeGiftDto {
    giftId: string;
    giftIdNumber?: number;
    giftName?: string;
    giftIconUrl?: string;
    giftPriceCoin?: number;
    enabled: boolean;
    sortOrder: number;
}

export interface RoomRpsConfigDto {
    appCode?: string;
    gameId: string;
    status: string;
    challengeTimeoutMs: number;
    revealCountdownMs: number;
    stakeGifts: RoomRpsStakeGiftDto[];
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface RoomRpsPlayerDto {
    userId: string;
    userIdNumber?: number;
    gesture: string;
    result: string;
    balanceAfter?: number;
    joinedAtMs?: number;
}

export interface RoomRpsChallengeDto {
    appCode?: string;
    challengeId: string;
    roomId: string;
    regionId?: number;
    status: string;
    stakeGiftId: string;
    stakeGiftIdNumber?: number;
    stakeCoin: number;
    initiator: RoomRpsPlayerDto;
    challenger: RoomRpsPlayerDto;
    winnerUserId: string;
    winnerUserIdNumber?: number;
    settlementStatus: string;
    failureReason?: string;
    timeoutAtMs?: number;
    revealAtMs?: number;
    createdAtMs?: number;
    matchedAtMs?: number;
    settledAtMs?: number;
    updatedAtMs?: number;
}

export interface RoomRpsChallengePage {
    items: RoomRpsChallengeDto[];
    nextCursor?: string;
    pageSize?: number;
    serverTimeMs?: number;
}

export type DiceConfigPayload = Omit<DiceConfigDto, "appCode" | "createdAtMs" | "updatedAtMs" | "poolBalanceCoin">;
export type SelfGameNewUserPolicyPayload = Omit<SelfGameNewUserPolicyDto, "appCode" | "createdAtMs" | "updatedAtMs">;

export interface RoomRpsConfigPayload {
    status: string;
    challengeTimeoutMs: number;
    revealCountdownMs: number;
    stakeGifts: Array<{
        giftId: string;
        enabled: boolean;
        sortOrder: number;
    }>;
}

export interface DicePoolAdjustPayload {
    amountCoin: number;
    direction: "in" | "out";
    reason: string;
}

export type SelfGameStakePoolPayload = Pick<
    SelfGameStakePoolDto,
    "targetBalanceCoin" | "safeBalanceCoin" | "warningBalanceCoin" | "dangerBalanceCoin" | "status"
>;

export interface DiceGenerateRobotsPayload {
    count: number;
    nicknameLanguage?: "english" | "arabic";
    avatarUrls?: string[];
    country?: string;
    gender?: string;
}

export type GamePlatformPayload = Omit<
    GamePlatformDto,
    "appCode" | "createdAtMs" | "updatedAtMs" | "callbackSecretSet"
>;
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs" | "whitelistEnabled">;

export interface GameCatalogQuery {
    platformCode?: string;
    status?: string;
    pageSize?: number;
    cursor?: string;
}

export interface RoomRpsChallengeQuery {
    roomId?: string;
    status?: string;
    initiatorUserId?: string;
    challengerUserId?: string;
    startTimeMs?: number;
    endTimeMs?: number;
    pageSize?: number;
    cursor?: string;
}

export function listGamePlatforms(status = ""): Promise<GamePlatformPage> {
    const endpoint = API_ENDPOINTS.listPlatforms;
    return apiRequest<GamePlatformPage>(apiEndpointPath(API_OPERATIONS.listPlatforms), {
        method: endpoint.method,
        query: { status },
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizePlatform),
    }));
}

export function upsertGamePlatform(payload: GamePlatformPayload, platformCode = ""): Promise<GamePlatformDto> {
    const operation = platformCode ? API_OPERATIONS.updatePlatform : API_OPERATIONS.createPlatform;
    const endpoint = API_ENDPOINTS[operation];
    return apiRequest<GamePlatformDto, GamePlatformPayload>(
        apiEndpointPath(operation, platformCode ? { platform_code: platformCode } : {}),
        {
            body: payload,
            method: endpoint.method,
        },
    ).then(normalizePlatform);
}

export function listGameCatalog(query: GameCatalogQuery = {}): Promise<GameCatalogPage> {
    const endpoint = API_ENDPOINTS.listCatalog;
    return apiRequest<GameCatalogPage>(apiEndpointPath(API_OPERATIONS.listCatalog), {
        method: endpoint.method,
        query: query as QueryParams,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeCatalog),
    }));
}

export function upsertGameCatalog(payload: GameCatalogPayload, gameId = ""): Promise<GameCatalogDto> {
    const operation = gameId ? API_OPERATIONS.updateCatalog : API_OPERATIONS.createCatalog;
    const endpoint = API_ENDPOINTS[operation];
    return apiRequest<GameCatalogDto, GameCatalogPayload>(
        apiEndpointPath(operation, gameId ? { game_id: gameId } : {}),
        {
            body: payload,
            method: endpoint.method,
        },
    ).then(normalizeCatalog);
}

export function setGameStatus(gameId: string, status: string): Promise<GameCatalogDto> {
    const endpoint = API_ENDPOINTS.setGameStatus;
    return apiRequest<GameCatalogDto, { status: string }>(
        apiEndpointPath(API_OPERATIONS.setGameStatus, { game_id: gameId }),
        {
            body: { status },
            method: endpoint.method,
        },
    ).then(normalizeCatalog);
}

export function deleteGameCatalog(gameId: string): Promise<void> {
    const endpoint = API_ENDPOINTS.deleteCatalog;
    return apiRequest<void>(apiEndpointPath(API_OPERATIONS.deleteCatalog, { game_id: gameId }), {
        method: endpoint.method,
    });
}

export function setGameWhitelistEnabled(gameId: string, enabled: boolean): Promise<GameCatalogDto> {
    const endpoint = API_ENDPOINTS.setGameWhitelistEnabled;
    return apiRequest<GameCatalogDto, { enabled: boolean }>(
        apiEndpointPath(API_OPERATIONS.setGameWhitelistEnabled, { game_id: gameId }),
        { body: { enabled }, method: endpoint.method },
    ).then(normalizeCatalog);
}

export function listGameWhitelistUsers(
    gameId: string,
): Promise<{ items: GameWhitelistUserDto[]; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.listGameWhitelistUsers;
    return apiRequest<{ items: GameWhitelistUserDto[]; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.listGameWhitelistUsers, { game_id: gameId }),
        { method: endpoint.method, query: { pageSize: 200 } },
    ).then((result) => ({ ...result, items: (result.items || []).map(normalizeGameWhitelistUser) }));
}

export function addGameWhitelistUser(gameId: string, userIdentifier: string): Promise<GameWhitelistUserDto> {
    const endpoint = API_ENDPOINTS.addGameWhitelistUser;
    return apiRequest<GameWhitelistUserDto, { userId: string }>(
        apiEndpointPath(API_OPERATIONS.addGameWhitelistUser, { game_id: gameId }),
        // 兼容已发布契约字段 userId；服务端会把内部 ID、默认短号或当前靓号统一解析成 owner user_id。
        { body: { userId: userIdentifier }, method: endpoint.method },
    ).then(normalizeGameWhitelistUser);
}

export function deleteGameWhitelistUser(gameId: string, userId: string): Promise<void> {
    const endpoint = API_ENDPOINTS.deleteGameWhitelistUser;
    return apiRequest<void>(
        apiEndpointPath(API_OPERATIONS.deleteGameWhitelistUser, { game_id: gameId, user_id: userId }),
        { method: endpoint.method },
    );
}

export function syncGamePlatformCatalog(platformCode: string, payload: GameSyncPayload = {}): Promise<GameSyncResult> {
    return apiRequest<GameSyncResult, GameSyncPayload>(
        `/v1/admin/game/platforms/${encodeURIComponent(platformCode)}/sync-games`,
        {
            body: payload,
            method: "POST",
        },
    ).then((result) => ({
        ...result,
        items: (result.items || []).map(normalizeCatalog),
        synced: numberValue(result.synced),
        dryRun: Boolean(result.dryRun),
    }));
}

export function listSelfGames(): Promise<{ items: DiceConfigDto[]; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.listSelfGames;
    return apiRequest<{ items: DiceConfigDto[]; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.listSelfGames),
        {
            method: endpoint.method,
        },
    ).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeDiceConfig),
    }));
}

export function updateDiceConfig(gameId: string, payload: DiceConfigPayload): Promise<DiceConfigDto> {
    const endpoint = API_ENDPOINTS.updateDiceConfig;
    return apiRequest<DiceConfigDto, DiceConfigPayload>(
        apiEndpointPath(API_OPERATIONS.updateDiceConfig, { game_id: gameId }),
        { body: payload, method: endpoint.method },
    ).then(normalizeDiceConfig);
}

export function getSelfGameNewUserPolicy(gameId: string): Promise<SelfGameNewUserPolicyDto> {
    return apiRequest<SelfGameNewUserPolicyDto>(
        `/v1/admin/game/self-games/${encodeURIComponent(gameId)}/new-user-policy`,
        { method: "GET" },
    ).then(normalizeSelfGameNewUserPolicy);
}

export function updateSelfGameNewUserPolicy(
    gameId: string,
    payload: SelfGameNewUserPolicyPayload,
): Promise<SelfGameNewUserPolicyDto> {
    return apiRequest<SelfGameNewUserPolicyDto, SelfGameNewUserPolicyPayload>(
        `/v1/admin/game/self-games/${encodeURIComponent(gameId)}/new-user-policy`,
        { body: payload, method: "PATCH" },
    ).then(normalizeSelfGameNewUserPolicy);
}

export function listSelfGameStakePools(
    gameId: string,
): Promise<{ items: SelfGameStakePoolDto[]; serverTimeMs?: number }> {
    return apiRequest<{ items: SelfGameStakePoolDto[]; serverTimeMs?: number }>(
        `/v1/admin/game/self-games/${encodeURIComponent(gameId)}/stake-pools`,
        { method: "GET" },
    ).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeSelfGameStakePool),
    }));
}

export function updateSelfGameStakePool(
    gameId: string,
    stakeCoin: number,
    payload: SelfGameStakePoolPayload,
): Promise<SelfGameStakePoolDto> {
    return apiRequest<SelfGameStakePoolDto, SelfGameStakePoolPayload>(
        `/v1/admin/game/self-games/${encodeURIComponent(gameId)}/stake-pools/${encodeURIComponent(String(stakeCoin))}`,
        { body: payload, method: "PATCH" },
    ).then(normalizeSelfGameStakePool);
}

export function adjustSelfGameStakePool(
    gameId: string,
    stakeCoin: number,
    payload: DicePoolAdjustPayload,
): Promise<{ config: DiceConfigDto; adjustment: unknown }> {
    return apiRequest<{ config: DiceConfigDto; adjustment: unknown }, DicePoolAdjustPayload>(
        `/v1/admin/game/self-games/${encodeURIComponent(gameId)}/stake-pools/${encodeURIComponent(String(stakeCoin))}/adjust`,
        { body: payload, method: "POST" },
    ).then((result) => ({ ...result, config: normalizeDiceConfig(result.config || {}) }));
}

export function listDiceRobots(query: { gameId?: string; status?: string; pageSize?: number; cursor?: string } = {}) {
    const endpoint = API_ENDPOINTS.listDiceRobots;
    return apiRequest<DiceRobotPage>(apiEndpointPath(API_OPERATIONS.listDiceRobots), {
        method: endpoint.method,
        query: query as QueryParams,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeDiceRobot),
    }));
}

export function generateDiceRobots(payload: DiceGenerateRobotsPayload) {
    const endpoint = API_ENDPOINTS.generateDiceRobots;
    return apiRequest<{ created: number; items: DiceRobotDto[] }, DiceGenerateRobotsPayload>(
        apiEndpointPath(API_OPERATIONS.generateDiceRobots),
        { body: payload, method: endpoint.method },
    ).then((result) => ({ ...result, items: (result.items || []).map(normalizeDiceRobot) }));
}

export function setDiceRobotStatus(userId: string, status: string, gameId = "") {
    const endpoint = API_ENDPOINTS.setDiceRobotStatus;
    return apiRequest<DiceRobotDto, { status: string }>(
        apiEndpointPath(API_OPERATIONS.setDiceRobotStatus, { user_id: userId }),
        { body: { status }, method: endpoint.method, query: gameId ? { gameId } : undefined },
    ).then(normalizeDiceRobot);
}

export function deleteDiceRobot(userId: string, gameId = ""): Promise<{ deleted: boolean }> {
    const endpoint = API_ENDPOINTS.deleteDiceRobot;
    return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deleteDiceRobot, { user_id: userId }), {
        method: endpoint.method,
        query: gameId ? { gameId } : undefined,
    });
}

export function getRoomRpsConfig(): Promise<{ config: RoomRpsConfigDto; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.getRoomRpsConfig;
    return apiRequest<{ config: RoomRpsConfigDto; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.getRoomRpsConfig),
        { method: endpoint.method },
    ).then((result) => ({ ...result, config: normalizeRoomRpsConfig(result.config || {}) }));
}

export function updateRoomRpsConfig(
    payload: RoomRpsConfigPayload,
): Promise<{ config: RoomRpsConfigDto; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.updateRoomRpsConfig;
    return apiRequest<{ config: RoomRpsConfigDto; serverTimeMs?: number }, RoomRpsConfigPayload>(
        apiEndpointPath(API_OPERATIONS.updateRoomRpsConfig),
        { body: payload, method: endpoint.method },
    ).then((result) => ({ ...result, config: normalizeRoomRpsConfig(result.config || {}) }));
}

export function listRoomRpsChallenges(query: RoomRpsChallengeQuery = {}): Promise<RoomRpsChallengePage> {
    const endpoint = API_ENDPOINTS.listRoomRpsChallenges;
    return apiRequest<RoomRpsChallengePage>(apiEndpointPath(API_OPERATIONS.listRoomRpsChallenges), {
        method: endpoint.method,
        query: query as QueryParams,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeRoomRpsChallenge),
    }));
}

export function getRoomRpsChallenge(
    challengeId: string,
): Promise<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.getRoomRpsChallenge;
    return apiRequest<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.getRoomRpsChallenge, { challenge_id: challengeId }),
        { method: endpoint.method },
    ).then((result) => ({ ...result, challenge: normalizeRoomRpsChallenge(result.challenge || {}) }));
}

export function retryRoomRpsSettlement(
    challengeId: string,
): Promise<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.retryRoomRpsSettlement;
    return apiRequest<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.retryRoomRpsSettlement, { challenge_id: challengeId }),
        { method: endpoint.method },
    ).then((result) => ({ ...result, challenge: normalizeRoomRpsChallenge(result.challenge || {}) }));
}

export function expireRoomRpsChallenge(
    challengeId: string,
): Promise<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }> {
    const endpoint = API_ENDPOINTS.expireRoomRpsChallenge;
    return apiRequest<{ challenge: RoomRpsChallengeDto; serverTimeMs?: number }>(
        apiEndpointPath(API_OPERATIONS.expireRoomRpsChallenge, { challenge_id: challengeId }),
        { method: endpoint.method },
    ).then((result) => ({ ...result, challenge: normalizeRoomRpsChallenge(result.challenge || {}) }));
}

function normalizePlatform(platform: Partial<GamePlatformDto>): GamePlatformDto {
    return {
        appCode: stringValue(platform.appCode),
        platformCode: stringValue(platform.platformCode),
        platformName: stringValue(platform.platformName),
        status: stringValue(platform.status || "active"),
        apiBaseUrl: stringValue(platform.apiBaseUrl),
        adapterType: stringValue(platform.adapterType || "yomi_v4"),
        callbackSecret: stringValue(platform.callbackSecret),
        callbackSecretSet: Boolean(platform.callbackSecretSet),
        callbackIpWhitelist: Array.isArray(platform.callbackIpWhitelist)
            ? platform.callbackIpWhitelist.map(String).filter(Boolean)
            : [],
        adapterConfigJson: stringValue(platform.adapterConfigJson || "{}"),
        sortOrder: numberValue(platform.sortOrder),
        createdAtMs: numberValue(platform.createdAtMs),
        updatedAtMs: numberValue(platform.updatedAtMs),
    };
}

function normalizeCatalog(game: Partial<GameCatalogDto>): GameCatalogDto {
    return {
        appCode: stringValue(game.appCode),
        gameId: stringValue(game.gameId),
        platformCode: stringValue(game.platformCode),
        providerGameId: stringValue(game.providerGameId),
        gameName: stringValue(game.gameName),
        category: stringValue(game.category),
        iconUrl: stringValue(game.iconUrl),
        coverUrl: stringValue(game.coverUrl),
        launchUrl: stringValue(game.launchUrl),
        launchMode: normalizeLaunchMode(game.launchMode),
        orientation: stringValue(game.orientation || "portrait"),
        safeHeight: numberValue(game.safeHeight),
        minCoin: numberValue(game.minCoin),
        status: stringValue(game.status || "active"),
        sortOrder: numberValue(game.sortOrder),
        tags: Array.isArray(game.tags) ? game.tags.map(String).filter(Boolean) : [],
        createdAtMs: numberValue(game.createdAtMs),
        updatedAtMs: numberValue(game.updatedAtMs),
        whitelistEnabled: game.whitelistEnabled === true,
    };
}

function normalizeGameWhitelistUser(user: Partial<GameWhitelistUserDto>): GameWhitelistUserDto {
    return {
        userId: stringValue(user.userId),
        displayUserId: stringValue(user.displayUserId),
        username: stringValue(user.username),
        avatar: stringValue(user.avatar),
        status: stringValue(user.status),
        createdByAdminId: numberValue(user.createdByAdminId),
        createdAtMs: numberValue(user.createdAtMs),
    };
}

function normalizeDiceConfig(config: Partial<DiceConfigDto>): DiceConfigDto {
    return {
        appCode: stringValue(config.appCode),
        gameId: stringValue(config.gameId || "dice"),
        status: stringValue(config.status || "active"),
        stakeOptions: Array.isArray(config.stakeOptions)
            ? config.stakeOptions.map((item) => ({
                  stakeCoin: numberValue(item.stakeCoin),
                  enabled: item.enabled !== false,
                  sortOrder: numberValue(item.sortOrder),
              }))
            : [],
        feeBps: numberValue(config.feeBps),
        poolBps: numberValue(config.poolBps),
        minPlayers: numberValue(config.minPlayers || 2),
        maxPlayers: numberValue(config.maxPlayers || 2),
        robotEnabled: config.robotEnabled !== false,
        robotMatchWaitMs: numberValue(config.robotMatchWaitMs || 3000),
        poolBalanceCoin: numberValue(config.poolBalanceCoin),
        createdAtMs: numberValue(config.createdAtMs),
        updatedAtMs: numberValue(config.updatedAtMs),
    };
}

function normalizeSelfGameNewUserPolicy(policy: Partial<SelfGameNewUserPolicyDto>): SelfGameNewUserPolicyDto {
    return {
        appCode: stringValue(policy.appCode),
        gameId: stringValue(policy.gameId || "dice"),
        enabled: policy.enabled !== false,
        protectionRounds: numberValue(policy.protectionRounds || 30),
        protectionHours: numberValue(policy.protectionHours || 168),
        maxStakeCoin: numberValue(policy.maxStakeCoin || 5000),
        lifetimeSubsidyQuotaCoin: numberValue(policy.lifetimeSubsidyQuotaCoin || 30000),
        day1SubsidyQuotaCoin: numberValue(policy.day1SubsidyQuotaCoin || 15000),
        singleRoundSubsidyCapCoin: numberValue(policy.singleRoundSubsidyCapCoin || 5000),
        strategyLevel: stringValue(policy.strategyLevel || "soft_boost"),
        loseStreakProtectionEnabled: policy.loseStreakProtectionEnabled !== false,
        loseStreakTrigger: numberValue(policy.loseStreakTrigger || 2),
        normalPhaseTargetWinRatePercent: numberValue(policy.normalPhaseTargetWinRatePercent || 53),
        blackPoolRobotForceWinEnabled: policy.blackPoolRobotForceWinEnabled === true,
        createdAtMs: numberValue(policy.createdAtMs),
        updatedAtMs: numberValue(policy.updatedAtMs),
    };
}

function normalizeSelfGameStakePool(pool: Partial<SelfGameStakePoolDto>): SelfGameStakePoolDto {
    return {
        appCode: stringValue(pool.appCode),
        gameId: stringValue(pool.gameId || "dice"),
        stakeCoin: numberValue(pool.stakeCoin),
        balanceCoin: numberValue(pool.balanceCoin),
        reservedCoin: numberValue(pool.reservedCoin),
        availableCoin: numberValue(pool.availableCoin),
        targetBalanceCoin: numberValue(pool.targetBalanceCoin),
        safeBalanceCoin: numberValue(pool.safeBalanceCoin),
        warningBalanceCoin: numberValue(pool.warningBalanceCoin),
        dangerBalanceCoin: numberValue(pool.dangerBalanceCoin),
        status: stringValue(pool.status || "active"),
        poolLevel: stringValue(pool.poolLevel || "black"),
        requiredPoolCoin: numberValue(pool.requiredPoolCoin),
        humanWinCapacity: numberValue(pool.humanWinCapacity),
        botMatchEnabled: pool.botMatchEnabled === true,
        blackReason: stringValue(pool.blackReason),
        createdAtMs: numberValue(pool.createdAtMs),
        updatedAtMs: numberValue(pool.updatedAtMs),
    };
}

function normalizeDiceRobot(robot: Partial<DiceRobotDto>): DiceRobotDto {
    return {
        appCode: stringValue(robot.appCode),
        gameId: stringValue(robot.gameId || "dice"),
        userId: stringValue(robot.userId),
        userIdNumber: numberValue(robot.userIdNumber),
        shortId: stringValue(robot.shortId),
        nickname: stringValue(robot.nickname),
        avatar: stringValue(robot.avatar),
        status: stringValue(robot.status || "active"),
        createdByAdminId: numberValue(robot.createdByAdminId),
        createdAtMs: numberValue(robot.createdAtMs),
        updatedAtMs: numberValue(robot.updatedAtMs),
    };
}

function normalizeRoomRpsConfig(config: Partial<RoomRpsConfigDto>): RoomRpsConfigDto {
    return {
        appCode: stringValue(config.appCode),
        gameId: stringValue(config.gameId || "room_rps"),
        status: stringValue(config.status || "active"),
        challengeTimeoutMs: numberValue(config.challengeTimeoutMs || 600000),
        revealCountdownMs: numberValue(config.revealCountdownMs || 3000),
        stakeGifts: Array.isArray(config.stakeGifts) ? config.stakeGifts.map(normalizeRoomRpsStakeGift) : [],
        createdAtMs: numberValue(config.createdAtMs),
        updatedAtMs: numberValue(config.updatedAtMs),
    };
}

function normalizeRoomRpsStakeGift(gift: Partial<RoomRpsStakeGiftDto>): RoomRpsStakeGiftDto {
    return {
        giftId: stringValue(gift.giftId),
        giftIdNumber: numberValue(gift.giftIdNumber),
        giftName: stringValue(gift.giftName),
        giftIconUrl: stringValue(gift.giftIconUrl),
        giftPriceCoin: numberValue(gift.giftPriceCoin),
        enabled: gift.enabled !== false,
        sortOrder: numberValue(gift.sortOrder),
    };
}

function normalizeRoomRpsChallenge(challenge: Partial<RoomRpsChallengeDto>): RoomRpsChallengeDto {
    return {
        appCode: stringValue(challenge.appCode),
        challengeId: stringValue(challenge.challengeId),
        roomId: stringValue(challenge.roomId),
        regionId: numberValue(challenge.regionId),
        status: stringValue(challenge.status || "pending"),
        stakeGiftId: stringValue(challenge.stakeGiftId),
        stakeGiftIdNumber: numberValue(challenge.stakeGiftIdNumber),
        stakeCoin: numberValue(challenge.stakeCoin),
        initiator: normalizeRoomRpsPlayer(challenge.initiator || {}),
        challenger: normalizeRoomRpsPlayer(challenge.challenger || {}),
        winnerUserId: stringValue(challenge.winnerUserId),
        winnerUserIdNumber: numberValue(challenge.winnerUserIdNumber),
        settlementStatus: stringValue(challenge.settlementStatus),
        failureReason: stringValue(challenge.failureReason),
        timeoutAtMs: numberValue(challenge.timeoutAtMs),
        revealAtMs: numberValue(challenge.revealAtMs),
        createdAtMs: numberValue(challenge.createdAtMs),
        matchedAtMs: numberValue(challenge.matchedAtMs),
        settledAtMs: numberValue(challenge.settledAtMs),
        updatedAtMs: numberValue(challenge.updatedAtMs),
    };
}

function normalizeRoomRpsPlayer(player: Partial<RoomRpsPlayerDto>): RoomRpsPlayerDto {
    return {
        userId: stringValue(player.userId),
        userIdNumber: numberValue(player.userIdNumber),
        gesture: stringValue(player.gesture),
        result: stringValue(player.result),
        balanceAfter: numberValue(player.balanceAfter),
        joinedAtMs: numberValue(player.joinedAtMs),
    };
}

function stringValue(value: unknown) {
    return typeof value === "string" ? value : value === undefined || value === null ? "" : String(value);
}

function numberValue(value: unknown) {
    const number = Number(value || 0);
    return Number.isFinite(number) ? number : 0;
}

function normalizeLaunchMode(value: unknown) {
    const mode = stringValue(value);
    return mode === "half_screen" || mode === "three_quarter_screen" || mode === "full_screen" ? mode : "full_screen";
}
