import { authFetch } from '../../../utils/authFetch' export type SubscribeAboItem = { coffeeId: string | number; quantity?: number } export type SubscribeAboInput = { coffeeId?: string | number // optional when items provided items?: SubscribeAboItem[] // NEW: whole order in one call billing_interval?: string interval_count?: number is_auto_renew?: boolean target_user_id?: number recipient_name?: string recipient_email?: string recipient_notes?: string // NEW: customer fields firstName?: string lastName?: string email?: string street?: string postalCode?: string city?: string country?: string frequency?: string startDate?: string // NEW: logged-in user id referred_by?: number } type Abonement = any type HistoryEvent = any const apiBase = (process.env.NEXT_PUBLIC_API_BASE_URL || '').replace(/\/+$/, '') const parseJson = async (res: Response) => { const ct = res.headers.get('content-type') || '' const isJson = ct.includes('application/json') const json = isJson ? await res.json().catch(() => ({})) : null return { json, isJson } } export async function subscribeAbo(input: SubscribeAboInput) { const hasItems = Array.isArray(input.items) && input.items.length > 0 if (!hasItems && !input.coffeeId) throw new Error('coffeeId is required') const hasRecipientFields = !!(input.recipient_name || input.recipient_email || input.recipient_notes) if (hasRecipientFields && !input.recipient_name) { throw new Error('recipient_name is required when gifting to a non-account recipient.') } // NEW: validate customer fields (required in UI) const requiredFields = ['firstName','lastName','email','street','postalCode','city','country','frequency','startDate'] as const const missing = requiredFields.filter(k => { const v = (input as any)[k] return typeof v !== 'string' || v.trim() === '' }) if (missing.length) { throw new Error(`Missing required fields: ${missing.join(', ')}`) } const body: any = { billing_interval: input.billing_interval ?? 'month', interval_count: input.interval_count ?? 1, is_auto_renew: input.is_auto_renew ?? true, // NEW: include customer fields firstName: input.firstName, lastName: input.lastName, email: input.email, street: input.street, postalCode: input.postalCode, city: input.city, country: input.country?.toUpperCase?.() ?? input.country, frequency: input.frequency, startDate: input.startDate, } if (hasItems) { body.items = input.items!.map(i => ({ coffeeId: i.coffeeId, quantity: i.quantity != null ? i.quantity : 1, })) // NEW: enforce exactly 12 packs const sumPacks = body.items.reduce((s: number, it: any) => s + Number(it.quantity || 0), 0) if (sumPacks !== 12) { console.warn('[subscribeAbo] Invalid pack total:', sumPacks, 'expected 12') throw new Error('Order must contain exactly 12 packs (120 capsules).') } } else { body.coffeeId = input.coffeeId // single-item legacy path — backend expects bundle, prefer items usage } // NEW: always include available recipient fields and target_user_id when provided if (input.target_user_id != null) body.target_user_id = input.target_user_id if (input.recipient_name) body.recipient_name = input.recipient_name if (input.recipient_email) body.recipient_email = input.recipient_email if (input.recipient_notes) body.recipient_notes = input.recipient_notes // NEW: always include referred_by if provided if (input.referred_by != null) body.referred_by = input.referred_by const url = `${apiBase}/api/abonements/subscribe` console.info('[subscribeAbo] POST', url, { body }) // NEW: explicit JSON preview that matches the actual request body console.info('[subscribeAbo] Body JSON:', JSON.stringify(body)) const res = await authFetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(body), credentials: 'include', }) const { json } = await parseJson(res) console.info('[subscribeAbo] Response', res.status, json) if (!res.ok || !json?.success) throw new Error(json?.message || `Subscribe failed: ${res.status}`) return json.data as Abonement } async function postAction(url: string) { const res = await authFetch(url, { method: 'POST', headers: { Accept: 'application/json' }, credentials: 'include' }) const { json } = await parseJson(res) if (!res.ok || !json?.success) throw new Error(json?.message || `Request failed: ${res.status}`) return json.data as Abonement } export const pauseAbo = (id: string | number) => postAction(`${apiBase}/abonements/${id}/pause`) export const resumeAbo = (id: string | number) => postAction(`${apiBase}/abonements/${id}/resume`) export const cancelAbo = (id: string | number) => postAction(`${apiBase}/abonements/${id}/cancel`) export const renewAbo = (id: string | number) => postAction(`${apiBase}/admin/abonements/${id}/renew`) export async function getMyAbonements(status?: string) { const qs = status ? `?status=${encodeURIComponent(status)}` : '' const res = await authFetch(`${apiBase}/abonements/mine${qs}`, { method: 'GET', headers: { Accept: 'application/json' }, credentials: 'include' }) const { json } = await parseJson(res) if (!res.ok || !json?.success) throw new Error(json?.message || `Fetch failed: ${res.status}`) return (json.data || []) as Abonement[] } export async function getAboHistory(id: string | number) { const res = await authFetch(`${apiBase}/abonements/${id}/history`, { method: 'GET', headers: { Accept: 'application/json' }, credentials: 'include' }) const { json } = await parseJson(res) if (!res.ok || !json?.success) throw new Error(json?.message || `History failed: ${res.status}`) return (json.data || []) as HistoryEvent[] }