import type { Vendor, VendorDeactivationImpact, VendorFacetCompany, VendorFacetJobBucket, VendorFacetLocation, VendorFacets, VendorListItem, VendorOpenWorkOrderSummary, VendorPortalToken, VendorPreferredContact, } from "@/domain/vendors/types/vendor"; const PREFERRED_CONTACT_VALUES = new Set(["Phone", "Email", "Text"]); function asRecord(raw: unknown): Record { return typeof raw === "object" && raw !== null ? (raw as Record) : {}; } function readString(record: Record, ...keys: string[]): string { for (const key of keys) { const value = record[key]; if (typeof value === "string") return value; if (typeof value === "number") return String(value); } return ""; } function readNumber(record: Record, ...keys: string[]): number { for (const key of keys) { const value = record[key]; if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string") { const parsed = Number(value); if (Number.isFinite(parsed)) return parsed; } } return 0; } function readId(record: Record): string | number | null { const value = record.id ?? record.Id; if (typeof value === "string") return value.trim() === "" ? null : value; if (typeof value === "number" && Number.isFinite(value)) return value; return null; } function readCompanyId(record: Record): string | number | null { const value = record.companyId ?? record.CompanyId; if (typeof value === "string") return value.trim() === "" ? null : value; if (typeof value === "number" && Number.isFinite(value)) return value; return null; } function readNumericCompanyId(record: Record): number | null { const value = readCompanyId(record); if (typeof value === "number") return value; if (typeof value !== "string" || value.trim() === "") return null; const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : null; } function readPreferredContact( record: Record, ...keys: string[] ): VendorPreferredContact { for (const key of keys) { const value = record[key]; if (typeof value === "string") { const normalized = value.trim(); if (PREFERRED_CONTACT_VALUES.has(normalized as VendorPreferredContact)) { return normalized as VendorPreferredContact; } const lowered = normalized.toLowerCase(); if (lowered === "phone" || lowered === "call") return "Phone"; if (lowered === "email" || lowered === "e-mail") return "Email"; if (lowered === "text" || lowered === "sms") return "Text"; } } return "Phone"; } function readPreferredContactOptional( record: Record, ): VendorPreferredContact | undefined { const raw = record.preferredContact ?? record.PreferredContact; if (typeof raw !== "string" || raw.trim() === "") return undefined; return readPreferredContact(record, "preferredContact", "PreferredContact"); } export function toCanonicalPhone(value: unknown): string { const original = String(value ?? ""); let digits = original.replace(/\D+/g, ""); if (digits.length === 11 && digits.startsWith("1")) { digits = digits.slice(1); } if (digits.length !== 10) return original; return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; } function readBool(record: Record, ...keys: string[]): boolean { for (const key of keys) { const value = record[key]; if (typeof value === "boolean") return value; } return true; } export function mapVendorListItem(raw: unknown): VendorListItem { const item = asRecord(raw); return { id: readId(item), companyId: readCompanyId(item), companyName: readString(item, "companyName", "CompanyName"), contactName: readString(item, "contactName", "ContactName"), email: readString(item, "email", "Email"), phone: readString(item, "phone", "Phone"), companyPhone: readString(item, "companyPhone", "CompanyPhone"), googleMapsUrl: readString(item, "googleMapsUrl", "GoogleMapsUrl"), notes: readString(item, "notes", "Notes"), totalJobs: readNumber(item, "totalJobs", "TotalJobs"), address: readString(item, "address", "Address"), city: readString(item, "city", "City"), state: readString(item, "state", "State"), tradeSpecialties: readString(item, "tradeSpecialties", "TradeSpecialties"), isActive: readBool(item, "isActive", "IsActive"), preferredContact: readPreferredContact(item, "preferredContact", "PreferredContact"), }; } export function mapVendorListItems(rawItems: unknown[]): VendorListItem[] { return rawItems.map(mapVendorListItem); } export function mapVendor(raw: unknown): Vendor { const item = asRecord(raw); return { id: readId(item) || undefined, companyId: readCompanyId(item), companyName: readString(item, "companyName", "CompanyName"), contactName: readString(item, "contactName", "ContactName"), email: readString(item, "email", "Email"), phone: readString(item, "phone", "Phone"), companyPhone: readString(item, "companyPhone", "CompanyPhone"), googleMapsUrl: readString(item, "googleMapsUrl", "GoogleMapsUrl"), notes: readString(item, "notes", "Notes"), totalJobs: readNumber(item, "totalJobs", "TotalJobs"), address: readString(item, "address", "Address"), city: readString(item, "city", "City"), state: readString(item, "state", "State"), zip: readString(item, "zip", "Zip", "zipCode", "ZipCode"), tradeSpecialties: readString(item, "tradeSpecialties", "TradeSpecialties"), isActive: readBool(item, "isActive", "IsActive"), preferredContact: readPreferredContact(item, "preferredContact", "PreferredContact"), }; } export function mapVendorToBackend(vendor: unknown): Record { const item = asRecord(vendor); const companyName = readString(item, "companyName", "CompanyName"); const payload: Record = { companyId: readNumericCompanyId(item), companyName, contactName: readString(item, "contactName", "ContactName"), email: readString(item, "email", "Email"), phone: toCanonicalPhone(item.phone ?? item.Phone), companyPhone: toCanonicalPhone(item.companyPhone ?? item.CompanyPhone), googleMapsUrl: readString(item, "googleMapsUrl", "GoogleMapsUrl"), notes: readString(item, "notes", "Notes"), address: readString(item, "address", "Address"), city: readString(item, "city", "City"), state: readString(item, "state", "State"), zip: readString(item, "zip", "Zip", "zipCode", "ZipCode"), tradeSpecialties: readString(item, "tradeSpecialties", "TradeSpecialties"), isActive: readBool(item, "isActive", "IsActive"), }; const preferredContact = readPreferredContactOptional(item); if (preferredContact) { payload.preferredContact = preferredContact; } const id = item.id ?? item.Id; if (typeof id === "string" || typeof id === "number") { payload.id = id; } return payload; } export function mapVendorUpdateToBackend(vendor: unknown): Record { const item = asRecord(vendor); const payload: Record = {}; const fieldMappings = [ ["companyName", "CompanyName"], ["contactName", "ContactName"], ["email", "Email"], ["googleMapsUrl", "GoogleMapsUrl"], ["notes", "Notes"], ["address", "Address"], ["city", "City"], ["state", "State"], ["zip", "Zip"], ["tradeSpecialties", "TradeSpecialties"], ["isActive", "IsActive"], ["preferredContact", "PreferredContact"], ] as const; fieldMappings.forEach(([target, source]) => { if (Object.prototype.hasOwnProperty.call(item, target)) payload[target] = item[target]; if (Object.prototype.hasOwnProperty.call(item, source)) payload[target] = item[source]; }); if ( Object.prototype.hasOwnProperty.call(item, "companyId") || Object.prototype.hasOwnProperty.call(item, "CompanyId") ) { payload.companyId = readNumericCompanyId(item); } if ( Object.prototype.hasOwnProperty.call(item, "phone") || Object.prototype.hasOwnProperty.call(item, "Phone") ) { payload.phone = toCanonicalPhone(item.phone ?? item.Phone); } if ( Object.prototype.hasOwnProperty.call(item, "companyPhone") || Object.prototype.hasOwnProperty.call(item, "CompanyPhone") ) { payload.companyPhone = toCanonicalPhone(item.companyPhone ?? item.CompanyPhone); } return payload; } function mapFacetCompany(raw: unknown): VendorFacetCompany { const item = asRecord(raw); return { companyId: readCompanyId(item), name: readString(item, "name", "Name"), companyPhone: readString(item, "companyPhone", "CompanyPhone"), email: readString(item, "email", "Email"), address: readString(item, "address", "Address"), city: readString(item, "city", "City"), state: readString(item, "state", "State"), zip: readString(item, "zip", "Zip", "zipCode", "ZipCode"), googleMapsUrl: readString(item, "googleMapsUrl", "GoogleMapsUrl"), }; } function mapFacetLocation(raw: unknown): VendorFacetLocation { const item = asRecord(raw); const city = readString(item, "city", "City"); const state = readString(item, "state", "State"); const label = readString(item, "label", "Label"); return { city, state, label: label || [city, state].filter(Boolean).join(", "), }; } function mapFacetJobBucket(raw: unknown): VendorFacetJobBucket { const item = asRecord(raw); return { id: readString(item, "id", "Id"), label: readString(item, "label", "Label"), }; } export function mapVendorFacets(raw: unknown): VendorFacets { const item = asRecord(raw); const companiesRaw = item.companies ?? item.Companies; const tradesRaw = item.trades ?? item.Trades; const locationsRaw = item.locations ?? item.Locations; const jobBucketsRaw = item.jobBuckets ?? item.JobBuckets; return { companies: Array.isArray(companiesRaw) ? companiesRaw.map(mapFacetCompany) : [], trades: Array.isArray(tradesRaw) ? tradesRaw .map((entry) => { if (typeof entry === "string") return entry; return readString(asRecord(entry), "name", "Name", "value", "Value", "label", "Label"); }) .filter(Boolean) : [], locations: Array.isArray(locationsRaw) ? locationsRaw.map(mapFacetLocation) : [], jobBuckets: Array.isArray(jobBucketsRaw) ? jobBucketsRaw.map(mapFacetJobBucket) : [], }; } export function mapVendorPortalToken(raw: unknown): VendorPortalToken | null { const item = asRecord(raw); const portalUrl = readString(item, "portalUrl", "PortalUrl"); if (!portalUrl) return null; return { portalUrl, issuedAt: readString(item, "issuedAt", "IssuedAt"), expiresAt: readString(item, "expiresAt", "ExpiresAt"), lastUsedAt: readString(item, "lastUsedAt", "LastUsedAt") || undefined, }; } function mapOpenWorkOrder(raw: unknown): VendorOpenWorkOrderSummary { const item = asRecord(raw); const workOrderId = item.workOrderId ?? item.WorkOrderId; const dispatchId = item.dispatchId ?? item.DispatchId; return { workOrderId: typeof workOrderId === "string" || typeof workOrderId === "number" ? workOrderId : "", workOrderNumber: readString(item, "workOrderNumber", "WorkOrderNumber"), workOrderTitle: readString(item, "workOrderTitle", "WorkOrderTitle"), status: readString(item, "status", "Status"), scheduledDate: readString(item, "scheduledDate", "ScheduledDate") || undefined, dispatchId: typeof dispatchId === "string" || typeof dispatchId === "number" ? dispatchId : undefined, }; } export function mapVendorDeactivationImpact(raw: unknown): VendorDeactivationImpact { const item = asRecord(raw); const openWorkOrdersRaw = item.openWorkOrders ?? item.OpenWorkOrders; const rawId = item.vendorId ?? item.VendorId ?? item.id ?? item.Id; const vendorId: string | number = typeof rawId === "string" || typeof rawId === "number" ? rawId : ""; return { vendorId, canDeactivate: readBool(item, "canDeactivate", "CanDeactivate"), openWorkOrders: Array.isArray(openWorkOrdersRaw) ? openWorkOrdersRaw.map(mapOpenWorkOrder) : [], }; }