'use client'; import React, { useState, useEffect } from 'react'; import Link from 'next/link'; import { ArrowLeft, CheckCircle2, ShieldCheck, Truck, Zap, QrCode, Phone, MapPin, User, MessageCircle, ExternalLink, CreditCard, AlertCircle } from 'lucide-react'; import { City, PaymentMethod, Order, StoreSettings } from '@/types'; import { INITIAL_CITIES, INITIAL_PAYMENT_METHODS, INITIAL_PRODUCTS } from '@/lib/data-service'; import { formatCOP } from '@/lib/pricing-engine'; import { useBrand } from '@/context/BrandContext'; import { useAuth } from '@/context/AuthContext'; import { renderClientTemplate } from '@/lib/templates-client'; import confetti from 'canvas-confetti'; import { OptimizedImage } from '@/components/ui/OptimizedImage'; export default function CheckoutPage() { const { currentBrand } = useBrand(); const { user } = useAuth(); const [customerName, setCustomerName] = useState(''); const [customerPhone, setCustomerPhone] = useState(''); const [address, setAddress] = useState(''); const [neighborhood, setNeighborhood] = useState(''); const [notes, setNotes] = useState(''); const [orderSource, setOrderSource] = useState('web'); const [cities, setCities] = useState(INITIAL_CITIES); const [paymentMethods, setPaymentMethods] = useState(INITIAL_PAYMENT_METHODS); const [selectedCityId, setSelectedCityId] = useState('city-cali'); const [selectedPaymentId, setSelectedPaymentId] = useState('pay-deposit-10k'); const [storeSettings, setStoreSettings] = useState(null); const [checkoutItems, setCheckoutItems] = useState>([]); const handleUpdateItemSize = (index: number, newSize: string) => { setCheckoutItems((prev) => { const copy = [...prev]; if (copy[index]) { copy[index] = { ...copy[index], size: newSize }; } try { localStorage.setItem('dripzone_cart', JSON.stringify(copy)); } catch (_) {} return copy; }); }; const [isSubmitting, setIsSubmitting] = useState(false); const [orderCompleted, setOrderCompleted] = useState(null); const [userRequiresDeposit, setUserRequiresDeposit] = useState(true); const [userDepositReason, setUserDepositReason] = useState('Garantía de separación requerida para el primer pedido'); const [showOtpModal, setShowOtpModal] = useState(false); const [otpCode, setOtpCode] = useState(''); const [otpLoading, setOtpLoading] = useState(false); const [otpError, setOtpError] = useState(''); // Estados para pasarela Bold & QR Bre-B const [boldPayment, setBoldPayment] = useState<{ paymentLink: string; checkoutUrl: string; qrImageUrl: string; amount: number; environment?: string; } | null>(null); const [boldPaid, setBoldPaid] = useState(false); const [boldLoading, setBoldLoading] = useState(false); useEffect(() => { fetch('/api/cities') .then((r) => r.json()) .then((d) => d.success && setCities(d.cities)) .catch(() => {}); fetch('/api/payment-methods') .then((r) => r.json()) .then((d) => d.success && setPaymentMethods(d.paymentMethods)) .catch(() => {}); fetch('/api/settings') .then((r) => r.json()) .then((d) => d.success && setStoreSettings(d.settings)) .catch(() => {}); // Carga dinámica de productos desde URL (WhatsApp Catalog / Asesor) o Carrito local if (typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search); const urlProductId = params.get('productId') || params.get('id'); const urlSize = params.get('size'); const urlPrice = params.get('price') ? parseInt(params.get('price')!, 10) : undefined; const urlName = params.get('name'); const urlSource = params.get('source'); if (urlSource === 'whatsapp' || urlProductId) { setOrderSource('whatsapp'); } if (urlProductId) { fetch('/api/products?all=true') .then((r) => r.json()) .then((d) => { if (d.success && Array.isArray(d.products)) { const matched = d.products.find((p: any) => p.id === urlProductId); if (matched) { const item = { productId: matched.id, productName: matched.name, brand: matched.brand || 'Sneakers', size: urlSize || (matched.sizes?.[0] || '39'), quantity: 1, price: urlPrice || matched.retailPrice || 125000, image: matched.images?.[0] || matched.colors?.[0]?.image || '/splash.png', availableSizes: matched.sizes || ['37', '38', '39', '40', '41', '42', '43'], }; setCheckoutItems([item]); localStorage.setItem('dripzone_cart', JSON.stringify([item])); return; } } }) .catch(() => {}); } else if (urlName) { const item = { productId: `custom-${Date.now()}`, productName: decodeURIComponent(urlName), brand: 'DripZone', size: urlSize || '39', quantity: 1, price: urlPrice || 125000, image: params.get('image') ? decodeURIComponent(params.get('image')!) : '/splash.png', availableSizes: ['37', '38', '39', '40', '41', '42', '43'], }; setCheckoutItems([item]); return; } // Si no viene en URL, intentar cargar de localStorage try { const stored = localStorage.getItem('dripzone_cart'); if (stored) { const parsed = JSON.parse(stored); if (Array.isArray(parsed) && parsed.length > 0) { setCheckoutItems( parsed.map((item: any) => ({ productId: item.productId || item.product?.id || `item-${Date.now()}`, productName: item.productName || item.product?.name || 'Zapatillas DripZone', brand: item.brand || item.product?.brand || 'Sneakers', size: item.size || item.selectedSize || '39', quantity: item.quantity || 1, price: item.price || item.product?.retailPrice || 125000, image: item.image || item.product?.images?.[0] || '/splash.png', availableSizes: item.availableSizes || item.product?.sizes || ['37', '38', '39', '40', '41', '42', '43'], })) ); return; } } } catch (_) {} // Fallback a productos activos fetch('/api/products') .then((r) => r.json()) .then((d) => { if (d.success && d.products?.length > 0) { const first = d.products[0]; setCheckoutItems([ { productId: first.id, productName: first.name, brand: first.brand || 'Sneakers', size: first.sizes?.[0] || '39', quantity: 1, price: first.retailPrice || 125000, image: first.images?.[0] || first.colors?.[0]?.image || '/splash.png', availableSizes: first.sizes || ['37', '38', '39', '40', '41', '42', '43'], }, ]); } }) .catch(() => {}); } }, []); useEffect(() => { if (user) { setUserRequiresDeposit((user.totalOrders || 0) === 0 || user.requiresShippingDeposit === true); setUserDepositReason(user.depositReason || ''); } }, [user]); useEffect(() => { const clean = customerPhone.replace(/\D/g, ''); if (clean.length >= 10) { fetch(`/api/users?phone=${clean}`) .then((r) => r.json()) .then((data) => { if (data.success) { setUserRequiresDeposit(data.requiresShippingDeposit !== false); setUserDepositReason(data.depositReason || ''); } }) .catch(() => {}); } }, [customerPhone]); const activeCity = cities.find((c) => c.id === selectedCityId) || cities[0]; const activePayment = paymentMethods.find((p) => p.id === selectedPaymentId) || paymentMethods[0]; const subtotal = checkoutItems.reduce((acc, it) => acc + it.price * it.quantity, 0); const shippingCost = activeCity.shippingCost; const totalAmount = subtotal + shippingCost; // Lógica de Cobro: // - Si el método es Bold 100% en línea (PSE, Tarjetas, Nequi, etc. sin separación): cobra el totalAmount. // - Si el método requiere separación (ej. $10.000 COP) o el usuario la tiene obligatoria: cobra el anticipo ($10.000 COP) y el saldo queda contraentrega. // - Si es 100% contraentrega en efectivo (ej. Cali): depósito $0 y saldo total contraentrega. const is100PercentOnline = activePayment?.type === 'bold' && !activePayment?.requiresDeposit; const isSeparation = Boolean(activePayment?.requiresDeposit || userRequiresDeposit); const isMandatoryDeposit = isSeparation; const depositAmount = is100PercentOnline ? totalAmount : isSeparation ? (activePayment?.depositAmount || 10000) : 0; const balanceDue = Math.max(0, totalAmount - depositAmount); const officialWhatsapp = storeSettings?.whatsappNumber || '573106390296'; const boldLink = activePayment?.boldPaymentLink || storeSettings?.boldPaymentLink || 'https://checkout.bold.co/payment/LNK_AJPFKOPB0C'; const boldEnv = storeSettings?.boldEnvironment || activePayment?.environment || 'production'; const processOrderSubmission = async () => { setIsSubmitting(true); try { const orderPayload = { customerName, customerPhone, address: `${address} - Barrio: ${neighborhood || 'Centro'}`, cityId: activeCity.id, cityName: activeCity.name, items: checkoutItems, subtotal, shippingCost, totalAmount, depositAmount, balanceDue, paymentMethodId: activePayment.id, paymentMethodName: activePayment.name, notes: notes ? `${notes} (Origen: ${orderSource})` : `Origen: ${orderSource}`, source: orderSource, }; const res = await fetch('/api/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderPayload), }); const data = await res.json(); if (data.success) { setOrderCompleted(data.order); confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 }, }); // Si la orden requiere cobro en línea vía Bold (anticipo de separación o pago total 100%) if (depositAmount > 0) { setBoldLoading(true); fetch('/api/bold/create-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orderId: data.order.id, amount: depositAmount, type: is100PercentOnline ? 'full' : 'deposit', payerEmail: '', redirectionUrl: window.location.href, }), }) .then((r) => r.json()) .then((bData) => { if (bData.success) { setBoldPayment(bData); } }) .catch((err) => console.error('Error generando cobro Bold:', err)) .finally(() => setBoldLoading(false)); } } else { alert(data.error || 'Error al registrar pedido.'); } } catch (err) { console.error('Error creando orden:', err); alert('Error de conexión al registrar pedido.'); } finally { setIsSubmitting(false); } }; // Escuchador en tiempo real del estado de pago en Bold useEffect(() => { if (!boldPayment?.paymentLink || boldPaid || !orderCompleted?.id) return; const interval = setInterval(async () => { try { const res = await fetch(`/api/bold/status?linkId=${boldPayment.paymentLink}&orderId=${orderCompleted.id}`); const data = await res.json(); if (data.success && data.isPaid) { setBoldPaid(true); confetti({ particleCount: 150, spread: 90, origin: { y: 0.5 }, }); } } catch (_) {} }, 3000); return () => clearInterval(interval); }, [boldPayment, boldPaid, orderCompleted]); const handleSubmitOrder = async (e: React.FormEvent) => { e.preventDefault(); if (!customerName || !customerPhone || !address) { alert('Por favor completa tu nombre, celular y dirección para el despacho.'); return; } const cleanPhone = customerPhone.replace(/\D/g, ''); if (cleanPhone.length < 10) { alert('Por favor ingresa un número de celular/WhatsApp válido a 10 dígitos.'); return; } if (user?.phoneVerified && user.phone === cleanPhone) { await processOrderSubmission(); return; } // Solicitar OTP por WhatsApp setOtpLoading(true); setOtpError(''); try { const res = await fetch('/api/auth/send-otp', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: cleanPhone, name: customerName }), }); const data = await res.json(); if (data.success) { setShowOtpModal(true); } else { alert(data.error || 'No se pudo enviar el código OTP a WhatsApp.'); } } catch (err) { alert('Error al solicitar código OTP por WhatsApp.'); } finally { setOtpLoading(false); } }; const handleVerifyOtpAndPlaceOrder = async () => { if (otpCode.length < 6) { setOtpError('Ingresa el código de 6 dígitos enviado a tu WhatsApp.'); return; } setOtpLoading(true); setOtpError(''); try { const cleanPhone = customerPhone.replace(/\D/g, ''); const res = await fetch('/api/auth/verify-otp', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: cleanPhone, code: otpCode, name: customerName, city: activeCity.name, address: `${address} - Barrio: ${neighborhood || 'Centro'}`, }), }); const data = await res.json(); if (data.success) { setShowOtpModal(false); await processOrderSubmission(); } else { setOtpError(data.error || 'Código incorrecto o expirado.'); } } catch (err) { setOtpError('Error verificando código.'); } finally { setOtpLoading(false); } }; const handleOpenWhatsAppOrder = (order: Order) => { const itemsSummary = order.items .map((i) => `• ${i.productName} (Talla ${i.size}) x${i.quantity} = ${formatCOP(i.price * i.quantity)}`) .join('\n'); const paymentCondition = order.depositAmount > 0 ? `⚡ *Separación:* ${formatCOP(order.depositAmount)}\n🤝 *Saldo Contraentrega:* ${formatCOP(order.balanceDue)}` : '🤝 *Pago 100% Contraentrega.*'; const rawText = renderClientTemplate('order_checkout_wa', { tienda_nombre: currentBrand.name, orden_id: order.id, cliente_nombre: order.customerName, cliente_telefono: order.customerPhone, cliente_direccion: order.address, cliente_ciudad: order.cityName, articulos_resumen: itemsSummary, total: formatCOP(order.totalAmount), condicion_pago_resumen: paymentCondition, metodo_pago: order.paymentMethodName, saldo_contraentrega: formatCOP(order.balanceDue), }); window.open(`https://wa.me/${officialWhatsapp}?text=${encodeURIComponent(rawText)}`, '_blank'); }; if (orderCompleted) { return (
¡Pedido Registrado con Éxito!

Orden #{orderCompleted.id}

Hemos recibido tu solicitud para entrega en {orderCompleted.cityName} con {currentBrand.name}.

{/* Bold.co QR Bre-B and Separation Box */} {orderCompleted.depositAmount > 0 ? ( boldPaid ? (

¡Anticipo de $10.000 COP Confirmado con Éxito!

Tu pago fue procesado correctamente mediante Bold. Tu orden ha sido autorizada para alistamiento prioritario en bodega y despacho inmediato.

🤝 Saldo a Pagar Contraentrega: {formatCOP(Math.max(0, orderCompleted.totalAmount - (orderCompleted.depositAmount || 10000)))} COP
) : (
{orderCompleted.balanceDue === 0 ? `Paso 2: Pago en Línea 100% Seguro (${formatCOP(orderCompleted.totalAmount)})` : `Paso 2: Anticipo de Separación (${formatCOP(orderCompleted.depositAmount || 10000)})`}
{boldPayment?.environment === 'sandbox' || boldEnv === 'sandbox' ? ( SANDBOX (PRUEBAS) ) : ( PRODUCCIÓN (LIVE) )} QR Bre-B / Bold

Escanea el Código QR Bre-B o toca el botón para pagar en línea de forma 100% segura con Bold:

{boldPayment?.qrImageUrl ? (
Código QR Bre-B Bold

Total a Pagar: {formatCOP(orderCompleted.depositAmount || (orderCompleted.balanceDue === 0 ? orderCompleted.totalAmount : 10000))} COP

⚡ Compatible con Bre-B e Interoperabilidad Bancaria

) : boldLoading ? (
Generando Código QR Bre-B con Bold...
) : null}
🤝 Saldo restante contraentrega: {orderCompleted.balanceDue === 0 ? '$0 COP (Despacho Prioritario)' : `${formatCOP(orderCompleted.balanceDue || Math.max(0, orderCompleted.totalAmount - (orderCompleted.depositAmount || 10000)))} COP`}
Esperando confirmación en tiempo real...
) ) : (

¡Cliente Frecuente! Tu pedido quedó programado con 100% Pago Contraentrega al recibir en tu puerta.

)}
Volver a la Tienda
); } return (
{/* Header */}
Regresar a {currentBrand.name}
Compra Segura & Garantizada
{/* Banner de Pedido Originado en WhatsApp */} {orderSource === 'whatsapp' && (

¡Pedido iniciado desde WhatsApp!

Atención Comercial

Tu calzado y talla seleccionada están reservados. Completa tu dirección y confirma el anticipo de $10.000 en línea (PSE, Tarjetas Débito/Crédito, Nequi o Bre-B) para asegurar tu despacho hoy. El saldo restante lo pagas contraentrega (en efectivo o transferencia) al recibir en tu puerta.

O pedir en App Móvil
)}
{/* Left Column: Shipping & Payment Details (7 cols) */}
{/* 1. Datos del Cliente */}

1. Datos de Entrega

setCustomerName(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-sm text-white focus:border-purple-400 outline-hidden" />
setCustomerPhone(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-sm text-white focus:border-purple-400 outline-hidden" />
{/* City Selection */}
setAddress(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-sm text-white focus:border-purple-400 outline-hidden" />
setNeighborhood(e.target.value)} className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-sm text-white focus:border-purple-400 outline-hidden" />
{/* 2. Método de Pago */}

2. Selecciona tu Método de Pago

{boldEnv === 'sandbox' ? ( MODO PRUEBAS (SANDBOX) ) : ( PRODUCCIÓN (LIVE) )}
{boldEnv === 'sandbox' && (

Pasarela Bold en Modo Pruebas Activo

Estás en entorno Sandbox. Los pagos generados con Bold son de prueba y no debitarán dinero real de tus cuentas.

)}
{paymentMethods.map((method) => { const isSelected = selectedPaymentId === method.id; const isBold = method.type === 'bold' || Boolean(method.boldChannel); return (
setSelectedPaymentId(method.id)} className={`p-4 rounded-2xl border transition-all cursor-pointer space-y-1.5 ${ isSelected ? `border-purple-400 bg-purple-950/20 shadow-md` : 'border-neutral-800 hover:border-neutral-700 bg-neutral-950' }`} >
{isSelected &&
}
{method.name}
{/* IDENTIFICADOR DE ENTORNO EN CADA MÉTODO */} {isBold && ( boldEnv === 'sandbox' ? ( PRUEBAS SANDBOX ) : ( EN VIVO LIVE ) )} {method.badge ? ( {method.badge} ) : method.requiresDeposit ? ( Separa con {formatCOP(method.depositAmount || 10000)} ) : method.type === 'bold' ? ( 100% en Línea ) : null}

{method.description}

{isSelected && (method.accountInfo || (method.type === 'bold' && boldLink)) && (
{method.accountInfo &&

{method.accountInfo}

} {method.type === 'bold' && (method.boldPaymentLink || boldLink) && ( Pagar en Línea con Bold (PSE, Tarjetas, Nequi o Bre-B) )}
)}
); })}
{/* Right Column: Summary (5 cols) */}

Resumen del Pedido

{/* Items */}
{checkoutItems.map((item, i) => (

{item.productName}

Talla elegida: {item.size}

{formatCOP(item.price * item.quantity)}

{/* Selector de tallas interactivo para clientes desde WhatsApp */} {item.availableSizes && item.availableSizes.length > 0 && (
📏 Selecciona o Cambia tu Talla:
{item.availableSizes.map((sz) => ( ))}
)}
))}
{/* Breakdown */}
Subtotal: {formatCOP(subtotal)}
Envío ({activeCity.name}): {shippingCost === 0 ? Gratis : formatCOP(shippingCost)}
Total de la Orden: {formatCOP(totalAmount)}
{/* Discriminación de Separación y Saldo Contraentrega */}
{isMandatoryDeposit ? '⚡ Valor Separación (1er Pedido):' : '⭐ Separación (Cliente Frecuente):'} {isMandatoryDeposit ? formatCOP(depositAmount) : '$0 COP (Exento)'}
🤝 Saldo a Pagar Contraentrega: {formatCOP(balanceDue)}

{isMandatoryDeposit ? `Para tu primer pedido aseguras el despacho de tu orden (${checkoutItems.reduce((acc, it) => acc + it.quantity, 0)} items) con una separación fija única de $10.000 COP vía Bold y cancelas el saldo restante en efectivo al recibir tu paquete.` : '¡Gracias por tu confianza! Como cliente frecuente, cancelas el 100% del pedido contraentrega al recibir.'}

Garantía en {activeCity.name}:

{activeCity.firstExchangeFree ? '1er Cambio de Talla 100% GRATIS (cambios posteriores con flete estándar).' : 'Cambios de talla 100% garantizados (los costos de envío son asumidos por el cliente).'}

Al confirmar, se guardará tu pedido y recibirás asistencia inmediata por WhatsApp oficial ({officialWhatsapp}) para el seguimiento de tu entrega en 24 horas.

{showOtpModal && (

Verificación de WhatsApp (OTP)

Para validar tu cuenta y registrar tu orden contraentrega en la plataforma, te enviamos un código de seguridad de 6 dígitos a tu WhatsApp +57 {customerPhone}:

setOtpCode(e.target.value.replace(/\D/g, ''))} placeholder="• • • • • •" className="w-full bg-neutral-950 border border-neutral-700 focus:border-purple-500 rounded-xl px-4 py-3 text-center text-2xl font-mono tracking-widest text-white outline-none" /> {otpError &&

{otpError}

}

El código expira en 5 minutos.

)}
); }