import { ProductResponse, ProductCardResponse } from '../services/api/catalogService'; import { Product } from '../types'; import { getCatalogPriceInfo, parseMoney } from '../lib/utils'; /** * Normalize a list/card API product into a full ProductResponse with numeric variant prices. */ export function mapProductCardToProductResponse( p: ProductCardResponse | Record ): ProductResponse { const price = parseMoney(p.price); const comparePrice = p.compare_price != null ? parseMoney(p.compare_price) : null; const colorsFromCard = Array.isArray((p as any).colors) ? (p as any).colors.filter(Boolean) : (p as any).color ? [(p as any).color] : []; const variants = Array.isArray((p as any).variants) && (p as any).variants.length > 0 ? (p as any).variants.map((v: any) => ({ ...v, price: parseMoney(v.price), compare_price: v.compare_price != null && v.compare_price !== '' ? parseMoney(v.compare_price) : null, cost_price: parseMoney(v.cost_price), attributes: Array.isArray(v.attributes) ? v.attributes : [], })) : [ { variant_id: p.first_variant_id || p.product_id, product_id: p.product_id, sku: (p as any).sku || '', barcode: null, price, compare_price: comparePrice && comparePrice > 0 ? comparePrice : null, cost_price: 0, low_stock_threshold: 5, status: 'active', available_stock: p.stock_count, attributes: colorsFromCard.map((color: string, index: number) => ({ id: `color-${index}`, variant_id: p.first_variant_id || p.product_id, attribute_id: `color-${index}`, attribute_value: color, attribute_name: 'Color', attribute_code: 'color', })), images: [], }, ]; return { product_id: p.product_id, category_id: p.category_id || '', brand_id: (p as any).brand_id || null, device_series_id: (p as any).device_series_id || null, device_model_id: (p as any).device_model_id || null, name: p.name, slug: p.slug, full_path: (p as any).full_path || `/products/${p.slug}`, description: (p as any).description ?? null, status: (p as any).status || 'active', created_at: (p as any).created_at || '', rating: (() => { const n = Number((p as any).rating ?? (p as any).average_rating ?? (p as any).avg_rating); return Number.isFinite(n) && n > 0 ? n : undefined; })(), badge: (p.badge as ProductResponse['badge']) || null, review_count: (p as any).review_count || 12, discount_percent: parseMoney((p as any).discount_percent) || undefined, color: (p as any).color || colorsFromCard[0] || null, colors: colorsFromCard, brand_name: (p as any).brand_name || null, images: (p as any).images?.length > 0 ? (p as any).images : p.thumbnail_url ? [ { image_id: '1', product_id: p.product_id, image_url: p.thumbnail_url, alt_text: p.name, sort_order: 0, is_banner: true, }, ] : [], variants, } as ProductResponse; } /** * Standardizes the mapping from database ProductResponse / ProductCardResponse (from API) * to the storefront's local Product interface. */ export function mapCatalogProductToProduct( p: ProductResponse | ProductCardResponse | Record ): Product { const normalized: ProductResponse = Array.isArray((p as any).variants) && (p as any).variants.length > 0 ? ({ ...mapProductCardToProductResponse(p), ...p, variants: (p as any).variants.map((v: any) => ({ ...v, price: parseMoney(v.price), compare_price: v.compare_price != null && v.compare_price !== '' ? parseMoney(v.compare_price) : null, })), } as ProductResponse) : mapProductCardToProductResponse(p); const { price, priceMax, oldPrice, discount } = getCatalogPriceInfo(normalized); const hasTimer = false; return { id: normalized.slug, product_id: normalized.product_id, first_variant_id: (p as any).first_variant_id || normalized.variants?.[0]?.variant_id || undefined, slug: normalized.slug, name: normalized.name, price, priceMax: priceMax && priceMax > price ? priceMax : undefined, oldPrice: oldPrice && oldPrice > price ? oldPrice : undefined, rating: normalized.rating || 5.0, reviewsCount: normalized.review_count || 12, image: (p as any).thumbnail_url || (normalized.images && normalized.images[0] ? normalized.images[0].image_url : ''), imageHover: normalized.images && normalized.images[1] ? normalized.images[1].image_url : (p as any).thumbnail_url || undefined, discount, category: normalized.category_id, brand: normalized.brand_id || (p as any).brand_name || 'iFixKart', // Deal countdown belongs only on the Deals Of The Day card — not catalog tiles hasTimer, timeRemaining: undefined, isHot: normalized.badge === 'HOT', isFeatured: normalized.badge === 'HOT' || (normalized.rating !== undefined && normalized.rating >= 4.8), isNewArrival: normalized.badge === 'NEW' || !normalized.badge, isRecentlyLaunched: normalized.badge === 'NEW', isTechGadget: true, totalStock: typeof (p as any).stock_count === 'number' ? (p as any).stock_count : 50, soldCount: 15, }; } /** * Deterministically selects the Hot Deal product from a list. * Priority: * 1. Product tagged "Hot Deal" (isHot === true) * 2. Product with highest discount percentage * 3. Fallback to first available product */ export function getHotDealProduct(products: Product[]): Product | null { if (!products || products.length === 0) return null; const hotTagged = products.find((p) => p.isHot); if (hotTagged) return hotTagged; const sortedByDiscount = [...products].sort( (a, b) => (b.discount || 0) - (a.discount || 0) ); if (sortedByDiscount[0] && (sortedByDiscount[0].discount || 0) > 0) { return sortedByDiscount[0]; } return products[0]; }