/** * Freebit SIM Provider - Mapper */ import type { SimDetails, SimUsage, SimTopUpHistory, SimType, SimStatus } from "../../contract"; import { simDetailsSchema, simUsageSchema, simTopUpHistorySchema } from "../../schema"; import { type FreebitAccountDetailsRaw, type FreebitTrafficInfoRaw, type FreebitQuotaHistoryRaw, freebitAccountDetailsRawSchema, freebitTrafficInfoRawSchema, freebitQuotaHistoryRawSchema, } from "./raw.types"; 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 simSizeValue = account.simSize ?? (account as any).size; 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: asNumber(response.todayData) / 1024, todayUsageKb: asNumber(response.todayData), monthlyUsageMb: response.thisMonthData ? asNumber(response.thisMonthData) / 1024 : undefined, monthlyUsageKb: response.thisMonthData ? asNumber(response.thisMonthData) : undefined, recentDaysUsage: (response.daily || []).map(day => ({ date: day.usageDate || "", usageKb: asNumber(day.trafficKb), usageMb: asNumber(day.trafficKb) / 1024, })), isBlacklisted: parseBooleanFlag(response.blacklistFlg), lastUpdated: new Date().toISOString(), }; return simUsageSchema.parse(simUsage); } export function transformFreebitQuotaHistory(raw: unknown): SimTopUpHistory { const response = freebitQuotaHistoryRawSchema.parse(raw); const history: SimTopUpHistory = { account: asString(response.account), totalAdditions: asNumber(response.totalAddQuotaKb), additionCount: asNumber(response.addQuotaCount), history: (response.details || []).map(detail => ({ quotaKb: asNumber(detail.addQuotaKb), quotaMb: asNumber(detail.addQuotaKb) / 1024, addedDate: detail.addDate || "", expiryDate: detail.expireDate || "", campaignCode: detail.campaignCode || "", })), }; return simTopUpHistorySchema.parse(history); }