import type { ChatAction } from "@/data/schemas/chat"; import type { PaymentOrderStatusResponse } from "@/data/schemas/payment"; import type { CharacterRoutes } from "@/router/routes"; import { appendRouteSearchParams, ROUTE_BUILDERS } from "@/router/routes"; export interface ChatActionNavigationContext { characterRoutes: CharacterRoutes; characterSlug: string; profileUrl: string; feedbackUrl: string; coinsRulesUrl: string; getOrderStatus: (orderId: string) => Promise; } /** Resolve only application-owned, allowlisted destinations for a chat action. */ export async function resolveChatActionTarget( action: ChatAction, context: ChatActionNavigationContext, ): Promise { switch (action.type) { case "giftOffer": return appendRouteSearchParams(context.characterRoutes.tip, { chatActionId: action.actionId, }); case "privateAlbumOffer": return context.characterRoutes.privateZone; case "openProfile": case "openWallet": return context.profileUrl; case "openCoinsRules": return context.coinsRulesUrl; case "activateVip": return ROUTE_BUILDERS.subscription("vip", { sourceCharacterSlug: context.characterSlug, returnTo: "chat", chatActionId: action.actionId, }); case "topUp": return ROUTE_BUILDERS.subscription("topup", { sourceCharacterSlug: context.characterSlug, returnTo: "chat", chatActionId: action.actionId, }); case "openFeedback": return context.feedbackUrl; case "resumePayment": { if (!action.orderId) return context.feedbackUrl; const order = await context.getOrderStatus(action.orderId); if (order.status === "paid") return context.profileUrl; if (order.status !== "pending") return context.feedbackUrl; if (order.orderType === "tip") return context.characterRoutes.tip; return ROUTE_BUILDERS.subscription( order.orderType === "vip" || order.orderType.startsWith("vip_") ? "vip" : "topup", { sourceCharacterSlug: context.characterSlug, returnTo: "chat", resumeOrderId: action.orderId, }, ); } } }