/** * Freebit SIM Provider - Mapper */ import type { SimDetails, SimUsage, SimTopUpHistory, SimType, SimStatus } from "../../contract.js"; import { simDetailsSchema, simUsageSchema, simTopUpHistorySchema } from "../../schema.js"; import { type FreebitAuthResponseRaw, freebitAccountDetailsRawSchema, freebitTrafficInfoRawSchema, freebitQuotaHistoryRawSchema, freebitAuthResponseRawSchema, freebitTopUpRawSchema, freebitAddSpecRawSchema, freebitPlanChangeRawSchema, freebitCancelPlanRawSchema, freebitCancelAccountRawSchema, freebitEsimReissueRawSchema, freebitEsimAddAccountRawSchema, } from "./raw.types.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 = Number.parseFloat(value); return Number.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); }