import { getToken } from '../context/AuthContext' let _profileId = null export function setProfileId(id) { _profileId = id } const BASE = '/api' function hdrs(extra={}) { const h = {...extra} if (_profileId) h['X-Profile-Id'] = _profileId const token = getToken() if (token) h['X-Auth-Token'] = token return h } async function req(path, opts={}) { const res = await fetch(BASE+path, {...opts, headers:hdrs(opts.headers||{})}) if (!res.ok) { const err = await res.text() // Try to parse JSON error with detail field try { const parsed = JSON.parse(err) throw new Error(parsed.detail || err) } catch { throw new Error(err) } } return res.json() } const json=(d)=>({method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(d)}) const jput=(d)=>({method:'PUT', headers:{'Content-Type':'application/json'},body:JSON.stringify(d)}) export const api = { // Profiles getActiveProfile: () => req('/profile'), listProfiles: () => req('/profiles'), createProfile: (d) => req('/profiles', json(d)), updateProfile: (id,d) => req(`/profiles/${id}`, jput(d)), deleteProfile: (id) => req(`/profiles/${id}`, {method:'DELETE'}), getProfile: () => req('/profile'), updateActiveProfile:(d)=> req('/profile', jput(d)), // Weight listWeight: (l=365) => req(`/weight?limit=${l}`), upsertWeight: (date,weight,note='') => req('/weight',json({date,weight,note})), updateWeight: (id,date,weight,note='') => req(`/weight/${id}`,jput({date,weight,note})), deleteWeight: (id) => req(`/weight/${id}`,{method:'DELETE'}), weightStats: () => req('/weight/stats'), // Circumferences listCirc: (l=100) => req(`/circumferences?limit=${l}`), upsertCirc: (d) => req('/circumferences',json(d)), updateCirc: (id,d) => req(`/circumferences/${id}`,jput(d)), deleteCirc: (id) => req(`/circumferences/${id}`,{method:'DELETE'}), // Caliper listCaliper: (l=100) => req(`/caliper?limit=${l}`), upsertCaliper: (d) => req('/caliper',json(d)), updateCaliper: (id,d) => req(`/caliper/${id}`,jput(d)), deleteCaliper: (id) => req(`/caliper/${id}`,{method:'DELETE'}), // Activity listActivity: (l=200)=> req(`/activity?limit=${l}`), createActivity: (d) => req('/activity',json(d)), updateActivity: (id,d) => req(`/activity/${id}`,jput(d)), deleteActivity: (id) => req(`/activity/${id}`,{method:'DELETE'}), activityStats: () => req('/activity/stats'), listUncategorizedActivities: () => req('/activity/uncategorized'), bulkCategorizeActivities: (d) => req('/activity/bulk-categorize', json(d)), importActivityCsv: async(file)=>{ const fd=new FormData();fd.append('file',file) const r=await fetch(`${BASE}/activity/import-csv`,{method:'POST',body:fd,headers:hdrs()}) const d=await r.json();if(!r.ok)throw new Error(d.detail||JSON.stringify(d));return d }, // Photos uploadPhoto: (file,date='')=>{ const fd=new FormData();fd.append('file',file);fd.append('date',date) return fetch(`${BASE}/photos`,{method:'POST',body:fd,headers:hdrs()}).then(r=>r.json()) }, listPhotos: () => req('/photos'), photoUrl: (pid) => { const token = getToken() return `${BASE}/photos/${pid}${token ? `?token=${token}` : ''}` }, // Nutrition importCsv: async(file)=>{ const fd=new FormData();fd.append('file',file) const r=await fetch(`${BASE}/nutrition/import-csv`,{method:'POST',body:fd,headers:hdrs()}) const d=await r.json();if(!r.ok)throw new Error(d.detail||JSON.stringify(d));return d }, listNutrition: (l=365) => req(`/nutrition?limit=${l}`), nutritionCorrelations: () => req('/nutrition/correlations'), nutritionWeekly: (w=16) => req(`/nutrition/weekly?weeks=${w}`), nutritionImportHistory: () => req('/nutrition/import-history'), getNutritionByDate: (date) => req(`/nutrition/by-date/${date}`), createNutrition: (date,kcal,protein,fat,carbs) => req(`/nutrition?date=${date}&kcal=${kcal}&protein_g=${protein}&fat_g=${fat}&carbs_g=${carbs}`,{method:'POST'}), updateNutrition: (id,kcal,protein,fat,carbs) => req(`/nutrition/${id}?kcal=${kcal}&protein_g=${protein}&fat_g=${fat}&carbs_g=${carbs}`,{method:'PUT'}), deleteNutrition: (id) => req(`/nutrition/${id}`,{method:'DELETE'}), // Stats & AI getStats: () => req('/stats'), insightTrend: () => req('/insights/trend',{method:'POST'}), listPrompts: () => req('/prompts'), runInsight: (slug) => req(`/insights/run/${slug}`,{method:'POST'}), insightPipeline: () => req('/insights/pipeline',{method:'POST'}), listInsights: () => req('/insights'), latestInsights: () => req('/insights/latest'), deleteInsight: (id) => req(`/insights/${id}`, {method:'DELETE'}), exportZip: async () => { const res = await fetch(`${BASE}/export/zip`, {headers: hdrs()}) if (!res.ok) throw new Error('Export failed') const blob = await res.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.zip` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) }, exportJson: async () => { const res = await fetch(`${BASE}/export/json`, {headers: hdrs()}) if (!res.ok) throw new Error('Export failed') const blob = await res.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.json` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) }, exportCsv: async () => { const res = await fetch(`${BASE}/export/csv`, {headers: hdrs()}) if (!res.ok) throw new Error('Export failed') const blob = await res.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.csv` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) }, // Admin adminListProfiles: () => req('/admin/profiles'), adminCreateProfile: (d) => req('/admin/profiles',json(d)), adminDeleteProfile: (id) => req(`/admin/profiles/${id}`,{method:'DELETE'}), adminSetPermissions: (id,d) => req(`/admin/profiles/${id}/permissions`,jput(d)), changePin: (pin) => req('/auth/pin',json({pin})), register: (name,email,password) => req('/auth/register',json({name,email,password})), verifyEmail: (token) => req(`/auth/verify/${token}`), resendVerification: (email) => req('/auth/resend-verification',json({email})), // v9c Subscription System // User-facing getMySubscription: () => req('/subscription/me'), getMyUsage: () => req('/subscription/usage'), getMyLimits: () => req('/subscription/limits'), redeemCoupon: (code) => req('/coupons/redeem',json({code})), getFeatureUsage: () => req('/features/usage'), // Phase 3: Usage overview // Admin: Features listFeatures: () => req('/features'), createFeature: (d) => req('/features',json(d)), updateFeature: (id,d) => req(`/features/${id}`,jput(d)), deleteFeature: (id) => req(`/features/${id}`,{method:'DELETE'}), // Admin: Tiers listTiers: () => req('/tiers'), createTier: (d) => req('/tiers',json(d)), updateTier: (id,d) => req(`/tiers/${id}`,jput(d)), deleteTier: (id) => req(`/tiers/${id}`,{method:'DELETE'}), // Admin: Tier Limits (Matrix) getTierLimitsMatrix: () => req('/tier-limits'), updateTierLimit: (d) => req('/tier-limits',jput(d)), updateTierLimitsBatch:(updates) => req('/tier-limits/batch',jput({updates})), // Admin: User Restrictions listUserRestrictions: (pid) => req(`/user-restrictions${pid?'?profile_id='+pid:''}`), createUserRestriction:(d) => req('/user-restrictions',json(d)), updateUserRestriction:(id,d) => req(`/user-restrictions/${id}`,jput(d)), deleteUserRestriction:(id) => req(`/user-restrictions/${id}`,{method:'DELETE'}), // Admin: Coupons listCoupons: () => req('/coupons'), createCoupon: (d) => req('/coupons',json(d)), updateCoupon: (id,d) => req(`/coupons/${id}`,jput(d)), deleteCoupon: (id) => req(`/coupons/${id}`,{method:'DELETE'}), getCouponRedemptions: (id) => req(`/coupons/${id}/redemptions`), // Admin: Access Grants listAccessGrants: (pid,active)=> req(`/access-grants${pid?'?profile_id='+pid:''}${active?'&active_only=true':''}`), createAccessGrant: (d) => req('/access-grants',json(d)), updateAccessGrant: (id,d) => req(`/access-grants/${id}`,jput(d)), revokeAccessGrant: (id) => req(`/access-grants/${id}`,{method:'DELETE'}), // v9d: Training Types listTrainingTypes: () => req('/training-types'), // Grouped by category listTrainingTypesFlat:() => req('/training-types/flat'), // Flat list getTrainingCategories:() => req('/training-types/categories'), // Category metadata // Admin: Training Types (v9d Phase 1b) adminListTrainingTypes: () => req('/admin/training-types'), adminGetTrainingType: (id) => req(`/admin/training-types/${id}`), adminCreateTrainingType: (d) => req('/admin/training-types', json(d)), adminUpdateTrainingType: (id,d) => req(`/admin/training-types/${id}`, jput(d)), adminDeleteTrainingType: (id) => req(`/admin/training-types/${id}`, {method:'DELETE'}), getAbilitiesTaxonomy: () => req('/admin/training-types/taxonomy/abilities'), // Admin: Training Type Profiles (Phase 2 #15) getProfileStats: () => req('/admin/training-types/profiles/stats'), getProfileTemplates: () => req('/admin/training-types/profiles/templates'), getProfileTemplate: (key) => req(`/admin/training-types/profiles/templates/${key}`), applyProfileTemplate: (id,templateKey) => req(`/admin/training-types/${id}/profile/apply-template`, json({template_key: templateKey})), getTrainingParameters: () => req('/evaluation/parameters'), batchEvaluateActivities: () => req('/evaluation/batch', {method:'POST'}), // Admin: Activity Type Mappings (v9d Phase 1b - Learnable System) adminListActivityMappings: (profileId, globalOnly) => req(`/admin/activity-mappings${profileId?'?profile_id='+profileId:''}${globalOnly?'?global_only=true':''}`), adminGetActivityMapping: (id) => req(`/admin/activity-mappings/${id}`), adminCreateActivityMapping: (d) => req('/admin/activity-mappings', json(d)), adminUpdateActivityMapping: (id,d) => req(`/admin/activity-mappings/${id}`, jput(d)), adminDeleteActivityMapping: (id) => req(`/admin/activity-mappings/${id}`, {method:'DELETE'}), adminGetMappingCoverage: () => req('/admin/activity-mappings/stats/coverage'), // Sleep Module (v9d Phase 2b) listSleep: (l=90) => req(`/sleep?limit=${l}`), getSleepByDate: (date) => req(`/sleep/by-date/${date}`), createSleep: (d) => req('/sleep', json(d)), updateSleep: (id,d) => req(`/sleep/${id}`, jput(d)), deleteSleep: (id) => req(`/sleep/${id}`, {method:'DELETE'}), getSleepStats: (days=7) => req(`/sleep/stats?days=${days}`), getSleepDebt: (days=14) => req(`/sleep/debt?days=${days}`), getSleepTrend: (days=30) => req(`/sleep/trend?days=${days}`), getSleepPhases: (days=30) => req(`/sleep/phases?days=${days}`), // Sleep Import (v9d Phase 2c) importAppleHealthSleep: (file) => { const fd = new FormData() fd.append('file', file) return req('/sleep/import/apple-health', {method:'POST', body:fd}) }, // Rest Days (v9d Phase 2a) listRestDays: (l=90) => req(`/rest-days?limit=${l}`), createRestDay: (d) => req('/rest-days', json(d)), getRestDay: (id) => req(`/rest-days/${id}`), updateRestDay: (id,d) => req(`/rest-days/${id}`, jput(d)), deleteRestDay: (id) => req(`/rest-days/${id}`, {method:'DELETE'}), getRestDaysStats: (weeks=4) => req(`/rest-days/stats?weeks=${weeks}`), validateActivity: (date, activityType) => req('/rest-days/validate-activity', json({date, activity_type: activityType})), // Vitals Baseline (v9d Phase 2d Refactored - once daily, morning) listBaseline: (l=90) => req(`/vitals/baseline?limit=${l}`), getBaselineByDate: (date) => req(`/vitals/baseline/by-date/${date}`), createBaseline: (d) => req('/vitals/baseline', json(d)), updateBaseline: (id,d) => req(`/vitals/baseline/${id}`, jput(d)), deleteBaseline: (id) => req(`/vitals/baseline/${id}`, {method:'DELETE'}), getBaselineStats: (days=30) => req(`/vitals/baseline/stats?days=${days}`), importBaselineAppleHealth: (file) => { const fd = new FormData() fd.append('file', file) return req('/vitals/baseline/import/apple-health', {method:'POST', body:fd}) }, // Blood Pressure (v9d Phase 2d Refactored - multiple daily, context-aware) listBloodPressure: (l=90) => req(`/blood-pressure?limit=${l}`), getBPByDate: (date) => req(`/blood-pressure/by-date/${date}`), createBloodPressure:(d) => req('/blood-pressure', json(d)), updateBloodPressure:(id,d) => req(`/blood-pressure/${id}`, jput(d)), deleteBloodPressure:(id) => req(`/blood-pressure/${id}`, {method:'DELETE'}), getBPStats: (days=30) => req(`/blood-pressure/stats?days=${days}`), importBPOmron: (file) => { const fd = new FormData() fd.append('file', file) return req('/blood-pressure/import/omron', {method:'POST', body:fd}) }, // AI Prompts Management (Issue #28) listAdminPrompts: () => req('/prompts'), createPrompt: (d) => req('/prompts', json(d)), updatePrompt: (id,d) => req(`/prompts/${id}`, jput(d)), deletePrompt: (id) => req(`/prompts/${id}`, {method:'DELETE'}), duplicatePrompt: (id) => req(`/prompts/${id}/duplicate`, json({})), reorderPrompts: (order) => req('/prompts/reorder', jput(order)), previewPrompt: (tpl) => req('/prompts/preview', json({template:tpl})), generatePrompt: (d) => req('/prompts/generate', json(d)), optimizePrompt: (id) => req(`/prompts/${id}/optimize`, json({})), listPlaceholders: () => req('/prompts/placeholders'), resetPromptToDefault: (id) => req(`/prompts/${id}/reset-to-default`, json({})), // Pipeline Configs Management (Issue #28 Phase 2) listPipelineConfigs: () => req('/prompts/pipeline-configs'), createPipelineConfig: (d) => req('/prompts/pipeline-configs', json(d)), updatePipelineConfig: (id,d) => req(`/prompts/pipeline-configs/${id}`, jput(d)), deletePipelineConfig: (id) => req(`/prompts/pipeline-configs/${id}`, {method:'DELETE'}), setDefaultPipelineConfig: (id) => req(`/prompts/pipeline-configs/${id}/set-default`, json({})), // Pipeline Execution (Issue #28 Phase 2) executePipeline: (configId=null) => req('/insights/pipeline' + (configId ? `?config_id=${configId}` : ''), json({})), // Unified Prompt System (Issue #28 Phase 2) executeUnifiedPrompt: (slug, modules=null, timeframes=null, debug=false, save=false) => { const params = new URLSearchParams({ prompt_slug: slug }) if (debug) params.append('debug', 'true') if (save) params.append('save', 'true') const body = {} if (modules) body.modules = modules if (timeframes) body.timeframes = timeframes return req('/prompts/execute?' + params, json(body)) }, createUnifiedPrompt: (d) => req('/prompts/unified', json(d)), updateUnifiedPrompt: (id,d) => req(`/prompts/unified/${id}`, jput(d)), // Batch Import/Export exportAllPrompts: () => req('/prompts/export-all'), importPrompts: (data, overwrite=false) => { const params = new URLSearchParams() if (overwrite) params.append('overwrite', 'true') return req(`/prompts/import?${params}`, json(data)) }, // Placeholder Export exportPlaceholderValues: () => req('/prompts/placeholders/export-values'), // v9e: Goals System (Strategic + Tactical) getGoalMode: () => req('/goals/mode'), updateGoalMode: (mode) => req('/goals/mode', jput({goal_mode: mode})), // Focus Areas (v2.0) getFocusAreas: () => req('/goals/focus-areas'), updateFocusAreas: (d) => req('/goals/focus-areas', jput(d)), listGoals: () => req('/goals/list'), listGoalsGrouped: () => req('/goals/grouped'), createGoal: (d) => req('/goals/create', json(d)), updateGoal: (id,d) => req(`/goals/${id}`, jput(d)), deleteGoal: (id) => req(`/goals/${id}`, {method:'DELETE'}), // Goal Progress (v2.1) listGoalProgress: (id) => req(`/goals/${id}/progress`), createGoalProgress: (id,d) => req(`/goals/${id}/progress`, json(d)), deleteGoalProgress: (gid,pid) => req(`/goals/${gid}/progress/${pid}`, {method:'DELETE'}), // Goal Type Definitions (Phase 1.5) listGoalTypeDefinitions: () => req('/goals/goal-types'), createGoalType: (d) => req('/goals/goal-types', json(d)), updateGoalType: (id,d) => req(`/goals/goal-types/${id}`, jput(d)), deleteGoalType: (id) => req(`/goals/goal-types/${id}`, {method:'DELETE'}), getSchemaInfo: () => req('/goals/schema-info'), // Training Phases listTrainingPhases: () => req('/goals/phases'), createTrainingPhase: (d) => req('/goals/phases', json(d)), updatePhaseStatus: (id,status) => req(`/goals/phases/${id}/status?status=${status}`, jput({})), // Fitness Tests listFitnessTests: () => req('/goals/tests'), createFitnessTest: (d) => req('/goals/tests', json(d)), // Focus Areas (v2.0) listFocusAreaDefinitions: (includeInactive=false) => req(`/focus-areas/definitions?include_inactive=${includeInactive}`), createFocusAreaDefinition: (d) => req('/focus-areas/definitions', json(d)), updateFocusAreaDefinition: (id,d) => req(`/focus-areas/definitions/${id}`, jput(d)), deleteFocusAreaDefinition: (id) => req(`/focus-areas/definitions/${id}`, {method:'DELETE'}), getUserFocusPreferences: () => req('/focus-areas/user-preferences'), updateUserFocusPreferences: (d) => req('/focus-areas/user-preferences', jput(d)), getFocusAreaStats: () => req('/focus-areas/stats'), // Chart Endpoints (Phase 0c - Phase 1: Nutrition + Recovery) // Nutrition Charts (E1-E5) getEnergyBalanceChart: (days=28) => req(`/charts/energy-balance?days=${days}`), getProteinAdequacyChart: (days=28) => req(`/charts/protein-adequacy?days=${days}`), getNutritionConsistencyChart: (days=28) => req(`/charts/nutrition-consistency?days=${days}`), getWeeklyMacroDistributionChart: (weeks=12) => req(`/charts/weekly-macro-distribution?weeks=${weeks}`), getNutritionAdherenceScore: (days=28) => req(`/charts/nutrition-adherence-score?days=${days}`), getEnergyAvailabilityWarning: (days=14) => req(`/charts/energy-availability-warning?days=${days}`), // Recovery Charts (R1-R5) getRecoveryScoreChart: (days=28) => req(`/charts/recovery-score?days=${days}`), getHrvRhrBaselineChart: (days=28) => req(`/charts/hrv-rhr-baseline?days=${days}`), getSleepDurationQualityChart: (days=28) => req(`/charts/sleep-duration-quality?days=${days}`), getSleepDebtChart: (days=28) => req(`/charts/sleep-debt?days=${days}`), getVitalSignsMatrixChart: (days=7) => req(`/charts/vital-signs-matrix?days=${days}`), // Placeholder Metadata Export (v1.0) exportPlaceholdersExtendedJson: () => req('/prompts/placeholders/export-values-extended'), }