const crypto=require("crypto"),db=require("../../models"),inventory=require("../inventory/inventory.service"),state=require("./orderState.service"),{nextSequence,pad}=require("../../utils/referenceNumber.util");const fault=(m,c,s=400)=>Object.assign(new Error(m),{code:c,status:s});const ref=async(prefix,key,t)=>`${prefix}-${new Date().getUTCFullYear()}-${pad(await nextSequence(key,t),6)}`; async function createFromCheckout({checkoutId,userId}){return db.sequelize.transaction(async transaction=>{const checkout=await db.CheckoutSession.findOne({where:{id:checkoutId,user_id:userId},include:[{model:db.CheckoutItem,as:"items"}],transaction,lock:transaction.LOCK.UPDATE});if(!checkout)throw fault("Checkout not found","NOT_FOUND",404);let order=await db.Order.findOne({where:{checkout_session_id:checkout.id},transaction,lock:transaction.LOCK.UPDATE});if(order)return {order,idempotent:true};if(checkout.status!=="READY"||new Date(checkout.expires_at)<=new Date())throw fault("Checkout is not ready","CHECKOUT_NOT_READY",409);for(const item of checkout.items){const reservation=await db.InventoryReservation.findOne({where:{reservation_key:item.reservation_key,status:"ACTIVE"},transaction,lock:transaction.LOCK.UPDATE});if(!reservation)throw fault("Checkout reservation is unavailable","RESERVATION_UNAVAILABLE",409);}order=await db.Order.create({id:crypto.randomUUID(),order_number:await ref("ORD","orders",transaction),user_id:userId,business_customer_id:checkout.business_customer_id,checkout_session_id:checkout.id,status:"PENDING_PAYMENT",payment_status:"PENDING",currency:checkout.currency,subtotal:checkout.subtotal,discount_total:checkout.discount_total,shipping_amount:checkout.shipping_amount,tax_amount:checkout.tax_amount,duty_amount:checkout.duty_amount,grand_total:checkout.grand_total,shipping_address_snapshot:checkout.shipping_address_snapshot,billing_address_snapshot:checkout.billing_address_snapshot,shipping_method_snapshot:checkout.shipping_snapshot,coupon_code:checkout.coupon_code,placed_at:new Date()},{transaction});await db.OrderItem.bulkCreate(checkout.items.map(x=>({id:crypto.randomUUID(),order_id:order.id,product_id:x.product_id,variant_id:x.variant_id,reservation_key:x.reservation_key,sku:x.sku,product_name:x.product_name,variant_description:x.variant_description,quantity:x.quantity,unit_price:x.unit_price,discount_amount:x.discount_amount,line_total:x.line_total,currency:x.currency,metadata:x.metadata})),{transaction});await checkout.update({status:"CONVERTED"},{transaction});await db.Cart.update({status:"CONVERTED"},{where:{id:checkout.cart_id,status:"CHECKOUT_LOCKED"},transaction});return {order,idempotent:false};});} async function cancel({orderId,userId,admin=false,requestId}){return db.sequelize.transaction(async transaction=>{const where={id:orderId,...(!admin&&{user_id:userId})},order=await db.Order.findOne({where,include:[{model:db.OrderItem,as:"items"},{model:db.Payment,as:"payments"}],transaction,lock:transaction.LOCK.UPDATE});if(!order)throw fault("Order not found","NOT_FOUND",404);if(order.status==="CANCELLED")return order;if(order.payment_status==="PAID")throw fault("Paid orders require a refund","REFUND_REQUIRED",409);state.assertTransition(order.status,"CANCELLED");for(const item of order.items)await inventory.releaseReservation({reservationKey:item.reservation_key,requestId,transaction});await db.Payment.update({status:"CANCELLED",cancelled_at:new Date()},{where:{order_id:order.id,status:["PENDING","REQUIRES_ACTION"]},transaction});await order.update({status:"CANCELLED",payment_status:"CANCELLED",cancelled_at:new Date()},{transaction});return order;});} module.exports={createFromCheckout,cancel,ref};