import { SIM_PLAN_CODES, SIM_PLAN_LABELS, SIM_STATUS } from "./contract.js"; import type { SimStatus, SimFeaturesUpdateRequest } from "./schema.js"; export function canManageActiveSim(status: SimStatus): boolean { return status === SIM_STATUS.ACTIVE; } export function canReissueEsim(status: SimStatus): boolean { return canManageActiveSim(status); } export function canCancelSim(status: SimStatus): boolean { return canManageActiveSim(status); } export function canTopUpSim(status: SimStatus): boolean { return canManageActiveSim(status); } export function formatSimPlanShort(planCode?: string): string { if (!planCode) return "—"; const normalized = planCode.trim(); const matchPrimary = normalized.match(/(?:^|[_-])(\d+(?:\.\d+)?)\s*G(?:B)?\b/i); if (matchPrimary?.[1]) { return `${matchPrimary[1]}G`; } const matchFallback = normalized.match(/(\d+(?:\.\d+)?)\s*G(?:B)?\b/i); if (matchFallback?.[1]) { return `${matchFallback[1]}G`; } return normalized; } export type SimPlanOption = { code: (typeof SIM_PLAN_CODES)[number]; label: string; shortLabel: string; }; export const SIM_PLAN_OPTIONS: SimPlanOption[] = SIM_PLAN_CODES.map(code => ({ code, label: SIM_PLAN_LABELS[code], shortLabel: formatSimPlanShort(code), })); export function getSimPlanLabel(planCode?: string): string { if (!planCode) return "Unknown plan"; if (planCode in SIM_PLAN_LABELS) { return SIM_PLAN_LABELS[planCode as keyof typeof SIM_PLAN_LABELS]; } return formatSimPlanShort(planCode); } export type SimFeatureToggleSnapshot = { voiceMailEnabled: boolean; callWaitingEnabled: boolean; internationalRoamingEnabled: boolean; networkType: "4G" | "5G"; }; export function buildSimFeaturesUpdatePayload( current: SimFeatureToggleSnapshot, next: SimFeatureToggleSnapshot ): SimFeaturesUpdateRequest | null { const payload: SimFeaturesUpdateRequest = {}; if (current.voiceMailEnabled !== next.voiceMailEnabled) { payload.voiceMailEnabled = next.voiceMailEnabled; } if (current.callWaitingEnabled !== next.callWaitingEnabled) { payload.callWaitingEnabled = next.callWaitingEnabled; } if (current.internationalRoamingEnabled !== next.internationalRoamingEnabled) { payload.internationalRoamingEnabled = next.internationalRoamingEnabled; } if (current.networkType !== next.networkType) { payload.networkType = next.networkType; } return Object.keys(payload).length > 0 ? payload : null; } /** * Plan code mapping from product SKU/name to Freebit plan code * Maps common data tiers: 5GB, 10GB, 25GB, 50GB */ const PLAN_CODE_MAPPING: Record = { "5": "PASI_5G", "10": "PASI_10G", "25": "PASI_25G", "50": "PASI_50G", }; /** * Maps a product SKU or name to the corresponding Freebit plan code. * * Extracts the data tier (e.g., "50" from "SIM Data+Voice 50GB" or "sim-50gb") * and maps it to the appropriate PASI plan code. * * @param productSku - The product SKU (e.g., "sim-data-voice-50gb") * @param productName - The product name (e.g., "SIM Data+Voice 50GB") * @returns The Freebit plan code (e.g., "PASI_50G") or null if not mappable */ export function mapProductToFreebitPlanCode( productSku?: string, productName?: string ): string | null { // Try to extract data tier from SKU or name const source = productSku || productName || ""; // Match patterns like "50GB", "50G", "50gb", or just "50" in context of GB const gbMatch = source.match(/(\d+)\s*G(?:B)?/i); if (gbMatch?.[1]) { const tier = gbMatch[1]; const planCode = PLAN_CODE_MAPPING[tier]; if (planCode) { return planCode; } } // Try matching standalone numbers in SKU patterns like "sim-50gb" const skuMatch = source.match(/[-_](\d+)[-_]?(?:gb|g)?/i); if (skuMatch?.[1]) { const tier = skuMatch[1]; const planCode = PLAN_CODE_MAPPING[tier]; if (planCode) { return planCode; } } return null; }