# Milton Health Coach FCM Client SDK

A React Native FCM Client SDK for the Milton Health Coach platform that provides asynchronous request processing with intelligent polling and **mandatory** push notification support for optimal background processing.

## 🚨 **FCM Required**

**Firebase Cloud Messaging (FCM) is mandatory** for this SDK. The SDK is specifically designed to handle Milton's async APIs efficiently using push notifications for background processing. Without FCM, the SDK will not function.

## Features

- ✅ **Async Request Processing**: Submit requests and get immediate responses
- ✅ **Intelligent Polling**: Configurable exponential backoff polling (default: 5s, 10s, 20s, 40s, 80s, 120s)
- ✅ **Push Notifications**: Firebase Cloud Messaging integration for instant results (**REQUIRED**)
- ✅ **Unified Coordination**: Automatically stops polling when push notification received
- ✅ **Background Handling**: Continues processing when app is backgrounded
- ✅ **Battery Optimized**: Smart polling intervals and background management
- ✅ **Offline Support**: Request queuing and automatic retry when network restored
- ✅ **Network Resilience**: Handles network failures gracefully with persistent storage
- ✅ **JavaScript**: Pure JavaScript implementation for React Native projects
- ✅ **Fully Configurable**: Customize polling intervals, retry attempts, timeouts, and offline behavior
- ✅ **Automatic Client ID**: Generates unique client identifiers for request tracking

## 📋 **Prerequisites**

Before installing the SDK, you **MUST** have:
- ✅ Firebase project created
- ✅ Android/iOS apps added to Firebase
- ✅ Push notification certificates/keys configured
- ✅ React Native 0.60.0 or higher

## Installation

### Step 1: Install the SDK

```bash
npm install milton-fcm-client-sdk
```

### Step 2: Install Required Dependencies

```bash
npm install @react-native-firebase/app @react-native-firebase/messaging @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info
```

### Step 3: Verify Installation

```bash
npm run verify
```

This will check your setup and provide guidance on any missing dependencies or configuration.

### Step 3: Firebase Configuration (MANDATORY)

#### iOS Setup
1. **Add GoogleService-Info.plist** to your iOS project
2. **Enable push notifications capability** in Xcode
3. **Configure APNs** certificates or keys in Firebase Console
4. Follow our detailed [Firebase Setup Guide](FIREBASE_SETUP.md)

#### Android Setup
1. **Add google-services.json** to `android/app/`
2. **Update build.gradle files** with Firebase plugin
3. **Configure FCM** in Firebase Console
4. Follow our detailed [Firebase Setup Guide](FIREBASE_SETUP.md)

### Step 4: Verify Installation

```javascript
import { MiltonAsyncClient } from 'milton-fcm-client-sdk';

// This will fail if FCM is not properly configured
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key'
  // enablePushNotifications defaults to true and is required
});
```

## 🔥 **Firebase Setup Guide**

**FCM setup is mandatory.** Choose your setup approach:

### 📋 **Quick Setup Checklist**
Use our [FCM Setup Checklist](FCM_SETUP_CHECKLIST.md) for a step-by-step verification process.

### 📖 **Detailed Setup Guide**
See our comprehensive [Firebase Setup Guide](FIREBASE_SETUP.md) for detailed instructions including:
- Creating Firebase project
- Configuring Android and iOS apps
- Setting up push notification certificates
- Testing FCM integration

### 🚀 **Complete API Examples**
See [All Milton APIs Example](examples/AllMiltonAPIsExample.js) for a complete React Native component demonstrating all three async APIs:
- User Message Processing
- Survey Processing  
- Fitness Calculations

## Quick Start

⚠️ **Ensure Firebase is configured before using the SDK** - See [Firebase Setup Guide](FIREBASE_SETUP.md)

```javascript
import { MiltonAsyncClient } from 'milton-fcm-client-sdk';

// Basic configuration (FCM enabled by default - REQUIRED)
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key'
  // enablePushNotifications: true (default - MANDATORY)
});

// Full configuration with all Milton async APIs
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  enablePushNotifications: true,        // MANDATORY - DO NOT SET TO FALSE
  clientIdConfig: {
    autoGenerate: true,                 // Auto-generate client ID
    prefix: 'myapp',                    // Custom prefix
    includeDeviceInfo: true,            // Include device info
    includePlatform: true,              // Include platform (iOS/Android)
    includeAppVersion: true             // Include app version
  },
  pollingConfig: {
    intervals: [5, 10, 20, 40, 80, 120], // Custom polling intervals
    maxAttempts: 6,                     // Custom max attempts
    batteryOptimized: true              // Optimize for battery life
  },
  offlineConfig: {
    maxQueueSize: 100,                  // Custom queue size
    retryAttempts: 3                    // Custom retry attempts
  }
});

// Example 1: User Message Processing
const userResponse = await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Log my breakfast: scrambled eggs and toast",
  image: imageUri, // optional
}, {
  onProgress: (status) => {
    console.log('Processing status:', status.status);
  },
  onComplete: (result) => {
    console.log('Analysis complete:', result);
  },
  onError: (error) => {
    console.error('Processing failed:', error);
  }
});

// Example 2: Survey Processing
const surveyResponse = await client.submitSurvey({
  phone_number: "+1234567890",
  survey: "Q. What's your gender?\nA. Male\n\nQ. What's your current weight?\nA. 173 lbs\n\nQ. How tall are you?\nA. 5 ft 11 in\n\nQ. What's your primary health goal right now?\nA. Lose weight\n\nQ. When do you want to reach your goal?\nA. 12/29/2025\n\nQ. What five things have you eaten the most over the last two to four weeks?\nA. I've eaten Paneer curry and rice, Dal and rice, homemade pizza, potato curry\n\nQ. Any food allergies, intolerances or other reasons you avoid foods?\nA. \n\nQ. How many meals do you usually eat in a day?\nA. 3\n\nQ. How often do you cook at home vs. eat out?\nA. Mostly home\n\nQ. How many days per week are you currently active?\nA. 6\n\nQ. How long are your typical workouts?\nA. 45-60 minutes\n\nQ. What is the intensity?\nA. Low: Light activity, easy pace\n\nQ. Any current or past injuries we should know about?\nA. \n\nQ. Do you have any chronic conditions we should consider when creating your plan?\nA. None of the above\n\nQ. Have you ever been told by a doctor to avoid certain types of exercise?\nA. Yes: Heavy weightlifting and water sports",
  birthday: "1990-01-01",
  default_timezone: "UTC-6"
}, {
  onComplete: (result) => {
    console.log('Survey processed:', result);
  }
});

// Example 3: Fitness Calculation
const fitnessResponse = await client.calculateFitness({
  orgId: 123,
  userId: 456,
  fitnessData: {
    steps: 8500,
    heart_rate_avg: 72,
    active_minutes: 45,
    calories_burned: 320
  }
}, {
  onComplete: (result) => {
    console.log('Fitness calculated:', result);
  }
});

console.log('All requests submitted with push notification support');
```

## Configuration

The SDK is fully configurable with custom polling intervals, retry attempts, and offline settings. See the [Configuration Guide](CONFIGURATION_GUIDE.md) for complete details on all available parameters.

### Key Configurable Parameters

- **baseUrl** & **apiKey**: Required connection parameters
- **clientIdConfig.autoGenerate**: Auto-generate unique client IDs (default: true)
- **clientIdConfig.prefix**: Custom prefix for generated client IDs (default: 'milton')
- **pollingConfig.intervals**: Custom polling intervals (default: [5, 10, 20, 40, 80, 120])
- **pollingConfig.maxAttempts**: Maximum polling attempts (default: 6)
- **offlineConfig.maxQueueSize**: Offline queue size (default: 50)
- **offlineConfig.retryAttempts**: Retry attempts for failed requests (default: 3)

## API Reference

### MiltonAsyncClient

#### Constructor

```javascript
new MiltonAsyncClient(config: MiltonSDKConfig)
```

**Config Options:**
- `baseUrl` (string): Milton API base URL
- `apiKey` (string): Your API key
- `timeout` (number, optional): Request timeout in ms (default: 30000)
- `enablePushNotifications` (boolean, optional): Enable FCM push notifications (default: true)
- `pollingConfig` (PollingConfig, optional): Custom polling configuration
- `offlineConfig` (OfflineConfig, optional): Offline handling configuration

#### Methods

##### submitUserMessage(request, options?)

Submit a user message for async processing.

```javascript
await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "What should I eat for lunch?",
  image: "data:image/jpeg;base64,..." // optional
}, {
  onProgress: (status) => console.log(status),
  onComplete: (result) => console.log(result),
  onError: (error) => console.error(error)
});
```

##### submitSurvey(request, options?)

Submit survey data for async processing.

```javascript
await client.submitSurvey({
  phone_number: "+1234567890",
  survey: "Mood: good, Energy: 8/10, Sleep: 7 hours",
  birthday: "1990-01-01",
  default_timezone: "America/New_York"
}, {
  onComplete: (result) => console.log('Survey processed:', result)
});
```

##### calculateFitness(request, options?)

Submit fitness calculation request.

```javascript
await client.calculateFitness({
  orgId: 123,
  userId: 456,
  fitnessData: {
    steps: 8500,
    heart_rate: 72,
    activity_minutes: 45
  }
}, {
  onComplete: (result) => console.log('Fitness calculated:', result)
});
```

##### getRequestStatus(requestId)

Get current status of a request.

```javascript
const status = await client.getRequestStatus('request-uuid');
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
```

##### cancelRequest(requestId)

Cancel a pending or processing request.

```javascript
await client.cancelRequest('request-uuid');
```

##### getOfflineQueueStatus()

Get current offline queue and network status.

```javascript
const status = client.getOfflineQueueStatus();
console.log('Queue length:', status.queueLength);
console.log('Is online:', status.isOnline);
console.log('Active polling:', status.activePollingRequests);
```

##### clearOfflineQueue()

Clear all queued offline requests.

```javascript
await client.clearOfflineQueue();
```

##### dispose()

Clean up resources when done with the client.

```javascript
client.dispose();
```

## Client ID Management

The SDK automatically generates and manages unique client identifiers to help track requests across different devices and app instances.

### Automatic Client ID Generation

By default, the SDK generates a unique client ID that includes:
- Custom prefix (default: 'milton')
- Platform (iOS/Android)
- App version
- Device identifier
- Timestamp

**Example generated ID:** `milton-ios-v1.2.3-abc12345-1k2j3h4g`

### Configuration Options

```javascript
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  clientIdConfig: {
    autoGenerate: true,           // Enable auto-generation (default: true)
    prefix: 'myapp',             // Custom prefix (default: 'milton')
    includeDeviceInfo: true,     // Include device info (default: true)
    includePlatform: true,       // Include platform (default: true)
    includeAppVersion: true,     // Include app version (default: true)
    customClientId: null,        // Use custom ID instead of auto-generation
    storageKey: 'my_client_id'   // Custom storage key (default: 'milton_client_id')
  }
});
```

### Manual Client ID Management

```javascript
// Get current client ID
const clientId = client.getClientId();
console.log('Current client ID:', clientId);

// Set custom client ID
await client.setClientId('my-custom-client-id');

// Regenerate client ID
await client.regenerateClientId();

// Override per request
await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "What should I eat?"
}, {
  clientId: 'request-specific-id' // Override auto-generated ID
});
```

### Persistent Storage

Client IDs are automatically stored in AsyncStorage and persist across app restarts. The same client ID will be used until:
- The app is uninstalled
- You manually regenerate it
- You set a custom client ID

## How It Works

### Unified Polling + Push Strategy

The SDK uses a smart coordination strategy:

1. **Submit Request**: Returns immediately with request ID
2. **Start Polling**: Begins intelligent polling with exponential backoff
3. **Push Notification**: Server sends push when processing completes
4. **Stop Polling**: SDK automatically stops polling when push received
5. **Fallback**: If push fails, polling continues until completion

### Polling Intervals

Default exponential backoff: `[1, 2, 4, 8, 15, 30]` seconds

- Attempt 1: Poll after 1 second
- Attempt 2: Poll after 2 seconds  
- Attempt 3: Poll after 4 seconds
- Attempt 4: Poll after 8 seconds
- Attempt 5: Poll after 15 seconds
- Attempt 6+: Poll every 30 seconds

### Background Handling

- **App Active**: Normal polling intervals
- **App Background**: Polling paused, relies on push notifications
- **App Resumed**: Polling resumes for any active requests

## Offline Handling

The SDK provides robust offline support with automatic request queuing and retry mechanisms.

### How Offline Handling Works

1. **Network Detection**: Automatically detects when device goes offline
2. **Request Queuing**: Queues requests in persistent storage when offline
3. **Automatic Retry**: Processes queued requests when network is restored
4. **Battery Optimization**: Uses longer polling intervals in background mode
5. **Push Fallback**: Switches to push notifications when polling times out

### Offline Configuration

```javascript
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  offlineConfig: {
    enableOfflineQueue: true,    // Enable offline request queuing
    maxQueueSize: 50,           // Maximum requests to queue
    retryAttempts: 3,           // Retry attempts for failed requests
    retryDelay: 5000,           // Delay between retries (ms)
    storageKey: 'milton_queue'  // AsyncStorage key for persistence
  }
});
```

### Battery Optimization

```javascript
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  pollingConfig: {
    intervals: [1, 2, 4, 8, 15, 30],        // Foreground intervals
    backgroundIntervals: [30, 60, 120, 300], // Background intervals
    maxAttempts: 6,
    batteryOptimized: true                   // Enable battery optimization
  }
});
```

### Offline Usage Example

```javascript
// Submit request - works both online and offline
const response = await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Log my lunch"
}, {
  onProgress: (status) => {
    if (status.status === 'queued_offline') {
      showMessage('Request queued - will process when online');
    } else if (status.status === 'waiting_for_push') {
      showMessage('Waiting for push notification...');
    }
  },
  onComplete: (result) => {
    showMessage('Request completed successfully');
  },
  onError: (error) => {
    if (error.message.includes('offline')) {
      showMessage('Request failed - check connection');
    }
  }
});

// Check offline queue status
const queueStatus = client.getOfflineQueueStatus();
console.log(`${queueStatus.queueLength} requests queued`);
```

### Network State Handling

The SDK automatically handles network state changes:

```javascript
// Network goes offline
// → Current requests switch to offline mode
// → New requests are queued automatically

// Network restored
// → Queued requests are processed automatically
// → Polling resumes for active requests
// → User receives completion notifications
```

### Battery Optimization Features

**Foreground Mode:**
- Normal polling intervals: 1s, 2s, 4s, 8s, 15s, 30s
- Full polling attempts (6 attempts)
- Immediate network retry on failure

**Background Mode:**
- Extended intervals: 30s, 60s, 120s, 300s
- Reduced polling attempts (4 attempts)
- Switches to push notification mode faster
- Pauses polling to save battery

**Push Notification Fallback:**
- Automatically switches to push mode after 2 minutes of polling
- Resumes polling when app becomes active
- Provides seamless user experience

## Advanced Usage

### Custom Polling Configuration

```javascript
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  pollingConfig: {
    intervals: [2, 5, 10, 20], // Custom intervals in seconds
    maxAttempts: 4,
    timeoutMs: 60000
  }
});
```

### Webhook Integration (Web Clients)

For web applications that can receive webhooks:

```javascript
await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Analyze my meal"
}, {
  webhookUrl: 'https://your-app.com/webhook/milton',
  clientId: 'web-client-123'
});
```

### Error Handling

```javascript
try {
  const response = await client.submitUserMessage(request, {
    onError: (error) => {
      if (error.message.includes('HTTP 503')) {
        // Server overloaded, retry later
        setTimeout(() => retryRequest(), 5000);
      } else {
        // Handle other errors
        showErrorMessage(error.message);
      }
    }
  });
} catch (error) {
  // Handle submission errors
  console.error('Failed to submit request:', error);
}
```

## Firebase Cloud Messaging Setup

### 1. Create Firebase Project

1. Go to [Firebase Console](https://console.firebase.google.com/)
2. Create a new project or use existing
3. Add your iOS and Android apps

### 2. Download Configuration Files

- **iOS**: Download `GoogleService-Info.plist`
- **Android**: Download `google-services.json`

### 3. Configure React Native Firebase

Follow the official [React Native Firebase setup guide](https://rnfirebase.io/).

### 4. Test Push Notifications

```javascript
import messaging from '@react-native-firebase/messaging';

// Test FCM token generation
messaging().getToken().then(token => {
  console.log('FCM Token:', token);
});

// Test notification reception
messaging().onMessage(async remoteMessage => {
  console.log('FCM Message received:', remoteMessage);
});
```

## Troubleshooting

### Common Issues

**Push notifications not working:**
- Verify Firebase configuration files are correctly added
- Check that FCM token is being generated
- Ensure push notification permissions are granted
- Test with Firebase Console test message

**Polling not stopping:**
- Check that push notification data includes `request_id`
- Verify notification type is `analysis_complete`
- Check console logs for push notification handling

**Requests timing out:**
- Increase `timeout` in SDK config
- Check network connectivity
- Verify API endpoint URLs are correct

### Debug Logging

Enable debug logging to troubleshoot issues:

```javascript
// The SDK automatically logs to console
// Check React Native debugger or device logs for:
// - "Milton SDK: FCM Token obtained"
// - "Milton SDK: Received completion notification"
// - "Milton SDK: Polling error" or other error messages
```

## Migration from Sync API

### Before (Synchronous)

```javascript
// Old synchronous approach
const response = await fetch('/user', {
  method: 'POST',
  body: JSON.stringify(request)
});
const result = await response.json(); // Waits 5-15 seconds
```

### After (Asynchronous)

```javascript
// New asynchronous approach
const response = await client.submitUserMessage(request, {
  onComplete: (result) => {
    // Handle result when ready (via push notification or polling)
    updateUI(result);
  }
});
// Returns immediately with request ID
```

## License

MIT

## Documentation

### Complete Documentation Suite

- **[Installation Guide](INSTALLATION.md)** - Step-by-step setup instructions
- **[Firebase Setup Guide](FIREBASE_SETUP.md)** - Comprehensive Firebase configuration
- **[API Reference](API_REFERENCE.md)** - Complete API documentation
- **[TypeScript Definitions](TYPESCRIPT_DEFINITIONS.md)** - TypeScript support and examples
- **[Migration Guide](MIGRATION_GUIDE.md)** - Migrate from sync to async API
- **[Troubleshooting Guide](TROUBLESHOOTING.md)** - Common issues and solutions
- **[Offline Handling Guide](OFFLINE_HANDLING.md)** - Offline support documentation

### Examples

- **[Basic Usage](examples/BasicUsage.js)** - Simple integration example
- **[Advanced Usage](examples/AdvancedUsage.js)** - Multiple endpoints and request management
- **[Complete Integration](examples/CompleteIntegrationExample.js)** - Full-featured example app
- **[Unified Polling + Push](examples/UnifiedPollingPushExample.js)** - Polling and push coordination
- **[Offline Handling](examples/OfflineHandlingExample.js)** - Offline queue management

## Support

For issues and questions:
- Email: Email: sanjay@joinmmnt.com