import { z } from "zod"; import type { Customer, CustomerAddress, CustomerStats } from "../../contract"; import { customerAddressSchema, customerEmailPreferencesSchema, customerSchema, customerStatsSchema, customerUserSchema, } from "../../schema"; import { whmcsClientSchema, whmcsClientResponseSchema, whmcsClientStatsSchema, type WhmcsClient, type WhmcsClientResponse, type WhmcsClientStats, whmcsCustomFieldSchema, whmcsUserSchema, } from "./raw.types"; const toBoolean = (value: unknown): boolean | undefined => { if (value === undefined || value === null) { return undefined; } if (typeof value === "boolean") { return value; } if (typeof value === "number") { return value === 1; } if (typeof value === "string") { const normalized = value.trim().toLowerCase(); return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on"; } return undefined; }; const toNumber = (value: unknown): number | undefined => { if (value === undefined || value === null) { return undefined; } if (typeof value === "number") { return value; } const parsed = Number.parseInt(String(value).replace(/[^0-9-]/g, ""), 10); return Number.isFinite(parsed) ? parsed : undefined; }; const toString = (value: unknown): string | undefined => { if (value === undefined || value === null) { return undefined; } return String(value); }; const normalizeCustomFields = (input: unknown): Record | undefined => { if (!input) { return undefined; } const parsed = whmcsCustomFieldSchema.array().safeParse(input).success ? (input as Array<{ id: number | string; value?: string | null; name?: string }>) : (() => { const wrapped = z.object({ customfield: z.union([whmcsCustomFieldSchema, whmcsCustomFieldSchema.array()]) }).safeParse(input); if (wrapped.success) { const value = wrapped.data.customfield; return Array.isArray(value) ? value : [value]; } return undefined; })(); if (!parsed) { return undefined; } const result: Record = {}; for (const field of parsed) { const key = field.name || String(field.id); if (key) { result[key] = field.value ?? ""; } } return Object.keys(result).length > 0 ? result : undefined; }; const normalizeUsers = (input: unknown): Customer["users"] => { if (!input) { return undefined; } const parsed = z .union([whmcsUserSchema, whmcsUserSchema.array()]) .safeParse(input); if (!parsed.success) { return undefined; } const usersArray = Array.isArray(parsed.data) ? parsed.data : [parsed.data]; const normalize = (value: z.infer) => customerUserSchema.parse(value); const users = usersArray.map(normalize); return users.length > 0 ? users : undefined; }; const normalizeAddress = (client: WhmcsClient): CustomerAddress | undefined => { const address = customerAddressSchema.parse({ address1: client.address1 ?? null, address2: client.address2 ?? null, city: client.city ?? null, state: client.fullstate ?? client.state ?? null, postcode: client.postcode ?? null, country: client.country ?? null, countryCode: client.countrycode ?? null, phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null, phoneCountryCode: client.phonecc ?? null, }); const hasValues = Object.values(address).some(value => value !== undefined && value !== null && value !== ""); return hasValues ? address : undefined; }; const normalizeEmailPreferences = (input: unknown) => customerEmailPreferencesSchema.parse(input ?? {}); const normalizeStats = (input: unknown): CustomerStats | undefined => { const parsed = whmcsClientStatsSchema.safeParse(input); if (!parsed.success || !parsed.data) { return undefined; } const stats = customerStatsSchema.parse(parsed.data); return stats; }; export const parseWhmcsClient = (raw: unknown): WhmcsClient => whmcsClientSchema.parse(raw); export const parseWhmcsClientResponse = (raw: unknown): WhmcsClientResponse => whmcsClientResponseSchema.parse(raw); export const transformWhmcsClient = (raw: unknown): Customer => { const client = parseWhmcsClient(raw); const customer: Customer = { id: Number(client.id), clientId: client.client_id ? Number(client.client_id) : client.userid ? Number(client.userid) : undefined, ownerUserId: client.owner_user_id ? Number(client.owner_user_id) : undefined, userId: client.userid ? Number(client.userid) : undefined, uuid: client.uuid ?? null, firstname: client.firstname ?? null, lastname: client.lastname ?? null, fullname: client.fullname ?? null, companyName: client.companyname ?? null, email: client.email, status: client.status ?? null, language: client.language ?? null, defaultGateway: client.defaultgateway ?? null, defaultPaymentMethodId: client.defaultpaymethodid ? Number(client.defaultpaymethodid) : undefined, currencyId: client.currency !== undefined ? toNumber(client.currency) : undefined, currencyCode: client.currency_code ?? null, taxId: client.tax_id ?? null, phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null, phoneCountryCode: client.phonecc ?? null, telephoneNumber: client.telephoneNumber ?? null, allowSingleSignOn: toBoolean(client.allowSingleSignOn) ?? null, emailVerified: toBoolean(client.email_verified) ?? null, marketingEmailsOptIn: toBoolean(client.isOptedInToMarketingEmails) ?? toBoolean(client.marketing_emails_opt_in) ?? null, notes: client.notes ?? null, createdAt: client.datecreated ?? null, lastLogin: client.lastlogin ?? null, address: normalizeAddress(client), emailPreferences: normalizeEmailPreferences(client.email_preferences), customFields: normalizeCustomFields(client.customfields), users: normalizeUsers(client.users?.user ?? client.users), raw: client, }; return customerSchema.parse(customer); }; export const transformWhmcsClientResponse = (raw: unknown): Customer => { const parsed = parseWhmcsClientResponse(raw); const customer = transformWhmcsClient(parsed.client); const stats = normalizeStats(parsed.stats); if (stats) { customer.stats = stats; } customer.raw = parsed; return customerSchema.parse(customer); }; export const transformWhmcsClientAddress = (raw: unknown): CustomerAddress | undefined => { const client = parseWhmcsClient(raw); return normalizeAddress(client); }; export const transformWhmcsClientStats = (raw: unknown): CustomerStats | undefined => normalizeStats(raw); export const prepareWhmcsClientAddressUpdate = ( address: Partial ): Record => { const update: Record = {}; if (address.address1 !== undefined) update.address1 = address.address1 ?? ""; if (address.address2 !== undefined) update.address2 = address.address2 ?? ""; if (address.city !== undefined) update.city = address.city ?? ""; if (address.state !== undefined) update.state = address.state ?? ""; if (address.postcode !== undefined) update.postcode = address.postcode ?? ""; if (address.country !== undefined) update.country = address.country ?? ""; if (address.countryCode !== undefined) update.countrycode = address.countryCode ?? ""; if (address.phoneNumber !== undefined) update.phonenumber = address.phoneNumber ?? ""; if (address.phoneCountryCode !== undefined) update.phonecc = address.phoneCountryCode ?? ""; return update; };