/** * Freebit SIM Provider - Mapper */ import type { SimDetails, SimUsage, SimTopUpHistory, SimType, SimStatus } from "../../contract.js"; import { simDetailsSchema, simUsageSchema, simTopUpHistorySchema } from "../../schema.js"; import { type FreebitAccountDetailsRaw, type FreebitTrafficInfoRaw, type FreebitQuotaHistoryRaw, type FreebitAuthResponseRaw, type FreebitTopUpRaw, type FreebitAddSpecRaw, type FreebitPlanChangeRaw, type FreebitCancelPlanRaw, type FreebitCancelAccountRaw, type FreebitEsimReissueRaw, type FreebitEsimAddAccountRaw, freebitAccountDetailsRawSchema, freebitTrafficInfoRawSchema, freebitQuotaHistoryRawSchema, freebitAuthResponseRawSchema, freebitTopUpRawSchema, freebitAddSpecRawSchema, freebitPlanChangeRawSchema, freebitCancelPlanRawSchema, freebitCancelAccountRawSchema, freebitEsimReissueRawSchema, freebitEsimAddAccountRawSchema, } from "./raw.types.js"; import { normalizeAccount } from "./utils.js"; function asString(value: unknown): string { if (typeof value === "string") return value; if (typeof value === "number") return String(value); return ""; } function asNumber(value: unknown): number { if (typeof value === "number") return value; if (typeof value === "string") { const parsed = parseFloat(value); return isNaN(parsed) ? 0 : parsed; } return 0; } function parseBooleanFlag(value: unknown): boolean { if (typeof value === "boolean") return value; if (typeof value === "number") return value === 10; if (typeof value === "string") return value === "10" || value.toLowerCase() === "true"; return false; } function mapSimStatus(status: string | undefined): SimStatus { if (!status) return "pending"; const normalized = status.toLowerCase(); if (normalized.includes("active") || normalized === "10") return "active"; if (normalized.includes("suspend")) return "suspended"; if (normalized.includes("cancel") || normalized.includes("terminate")) return "cancelled"; return "pending"; } function deriveSimType(sizeValue: unknown, eid?: string | number | null): SimType { const simSizeStr = typeof sizeValue === "number" ? String(sizeValue) : sizeValue; const raw = typeof simSizeStr === "string" ? simSizeStr.toLowerCase() : undefined; const eidStr = typeof eid === "number" ? String(eid) : eid; if (eidStr && eidStr.length > 0) { return "esim"; } switch (raw) { case "nano": return "nano"; case "micro": return "micro"; case "esim": return "esim"; default: return "standard"; } } export function transformFreebitAccountDetails(raw: unknown): SimDetails { const response = freebitAccountDetailsRawSchema.parse(raw); const account = response.responseDatas.at(0); if (!account) { throw new Error("Freebit account details missing response data"); } const sanitizedAccount = asString(account.account); const legacySize = "size" in account ? (account as { size?: unknown }).size : undefined; const simSizeValue = account.simSize ?? legacySize; const eidValue = account.eid; const simType = deriveSimType( typeof simSizeValue === "number" ? String(simSizeValue) : simSizeValue, typeof eidValue === "number" ? String(eidValue) : eidValue ); const voiceMailEnabled = parseBooleanFlag(account.voicemail ?? account.voiceMail); const callWaitingEnabled = parseBooleanFlag(account.callwaiting ?? account.callWaiting); const internationalRoamingEnabled = parseBooleanFlag(account.worldwing ?? account.worldWing); const simDetails: SimDetails = { account: sanitizedAccount, status: mapSimStatus(account.status), planCode: asString(account.planCode), planName: asString(account.planName), simType, iccid: asString(account.iccid), eid: asString(eidValue), msisdn: asString(account.msisdn), imsi: asString(account.imsi), remainingQuotaMb: asNumber(account.quota), remainingQuotaKb: asNumber(account.quotaKb), voiceMailEnabled, callWaitingEnabled, internationalRoamingEnabled, networkType: asString(account.contractLine), activatedAt: asString(account.startDate) || undefined, expiresAt: asString(account.expireDate) || undefined, }; return simDetailsSchema.parse(simDetails); } export function transformFreebitTrafficInfo(raw: unknown): SimUsage { const response = freebitTrafficInfoRawSchema.parse(raw); const simUsage: SimUsage = { account: asString(response.account), todayUsageMb: response.traffic?.today ? asNumber(response.traffic.today) / 1024 : 0, todayUsageKb: response.traffic?.today ? asNumber(response.traffic.today) : 0, monthlyUsageMb: undefined, monthlyUsageKb: undefined, recentDaysUsage: [], isBlacklisted: parseBooleanFlag(response.traffic?.blackList), lastUpdated: new Date().toISOString(), }; return simUsageSchema.parse(simUsage); } export function transformFreebitQuotaHistory( raw: unknown, accountOverride?: string ): SimTopUpHistory { const response = freebitQuotaHistoryRawSchema.parse(raw); const history: SimTopUpHistory = { account: accountOverride ?? asString(response.account), totalAdditions: asNumber(response.total), additionCount: asNumber(response.count), history: (response.quotaHistory || []).map(detail => ({ quotaKb: asNumber(detail.addQuotaKb), quotaMb: asNumber(detail.addQuotaKb) / 1024, addedDate: detail.addDate || "", expiryDate: detail.expireDate || "", campaignCode: detail.campaignCode || "", })), }; return simTopUpHistorySchema.parse(history); } export function transformFreebitTopUpResponse(raw: unknown) { return freebitTopUpRawSchema.parse(raw); } export type FreebitTopUpResponse = ReturnType; export function transformFreebitAddSpecResponse(raw: unknown) { return freebitAddSpecRawSchema.parse(raw); } export type FreebitAddSpecResponse = ReturnType; export function transformFreebitPlanChangeResponse(raw: unknown) { return freebitPlanChangeRawSchema.parse(raw); } export type FreebitPlanChangeResponse = ReturnType; export function transformFreebitCancelPlanResponse(raw: unknown) { return freebitCancelPlanRawSchema.parse(raw); } export type FreebitCancelPlanResponse = ReturnType; export function transformFreebitCancelAccountResponse(raw: unknown) { return freebitCancelAccountRawSchema.parse(raw); } export type FreebitCancelAccountResponse = ReturnType; export function transformFreebitEsimReissueResponse(raw: unknown) { return freebitEsimReissueRawSchema.parse(raw); } export type FreebitEsimReissueResponse = ReturnType; export function transformFreebitEsimAddAccountResponse(raw: unknown) { return freebitEsimAddAccountRawSchema.parse(raw); } export type FreebitEsimAddAccountResponse = ReturnType< typeof transformFreebitEsimAddAccountResponse >; export function transformFreebitEsimActivationResponse(raw: unknown) { return freebitEsimAddAccountRawSchema.parse(raw); } export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw { return freebitAuthResponseRawSchema.parse(raw); }