# Offline API Design Document

## 📋 Overview

This document outlines the design for new offline-specific APIs that will handle batch synchronization of schedule updates and log time operations from mobile apps. These APIs are separate from existing online APIs and include conflict detection.

---

## 🎯 Design Goals

1. **Single Combined API** - One endpoint for both updates (trailer/door/comments) and log times
2. **Conflict Detection** - Detect when server data is newer than client data (via `client_updated_at`)
3. **Batch Processing** - Handle multiple operations in one request (max 10 schedules)
4. **Partial Success** - Return which operations succeeded and which failed

---

## 📡 API Endpoints

### 1. Offline Sync API (Combined)

**Endpoint:** `POST /api/schedules/offline-sync`

**Purpose:** Single endpoint to sync both schedule updates (trailer/door/comments) and log time operations from offline queue.

**Authentication:** Required (driver role only)

**Request Structure:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "door_id": "789",
      "comments": "Updated comments",
      "log_times": [
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:30:00Z"
        },
        {
          "type": "out",
          "local_timestamp": "2025-01-15T11:00:00Z"
        },
        {
          "type": "completed",
          "local_timestamp": "2025-01-15T11:30:00Z"
        },
        {
          "type": "parked",
          "local_timestamp": "2025-01-15T12:00:00Z",
          "latitude": 40.7128,
          "longitude": -74.0060
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    },
    {
      "id": 124,
      "trailer_id": "999",
      "log_times": [
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:35:00Z"
        }
      ],
      "client_updated_at": "2025-01-15T10:35:00Z"
    },
    {
      "id": 125,
      "comments": "Only updating comments",
      "client_updated_at": "2025-01-15T10:40:00Z"
    },
    {
      "id": 126,
      "log_times": [
        {
          "type": "parked",
          "local_timestamp": "2025-01-15T12:00:00Z",
          "latitude": 40.7128,
          "longitude": -74.0060
        }
      ],
      "client_updated_at": "2025-01-15T12:00:00Z"
    }
  ]
}
```

**Response Structure:**
```json
{
  "status": "success",
  "message": "Offline sync completed",
  "data": {
    "succeeded": [
      {
        "id": 123,
        "status": "updated",
        "trailer_id": "456",
        "door_id": "789",
        "comments": "Updated comments",
        "log_times": {
          "succeeded": [
            {
              "type": "in",
              "in_time": "10:30:00",
              "in_user_name": "John Doe"
            },
            {
              "type": "out",
              "out_time": "11:00:00",
              "out_user_name": "John Doe"
            },
            {
              "type": "completed",
              "completed_time": "11:30:00",
              "completed_user_name": "John Doe"
            },
            {
              "type": "parked",
              "parked_time": "12:00:00",
              "parked_user_name": "John Doe"
            }
          ],
          "failed": []
        },
        "schedule_status": "parked",
        "updated_at": "2025-01-15T12:00:00Z",
        "schedule": {
          // Full schedule object (all fields) for complete state update
          "id": 123,
          "location_id": 1,
          "carrier_id": 5,
          "trailer_id": "456",
          "door_id": "789",
          "invoice_number": "INV-123",
          "schedule_type": "inbound",
          "comments": "Updated comments",
          "in_time": "10:30:00",
          "out_time": "11:00:00",
          "completed_time": "11:30:00",
          "parked_time": "12:00:00",
          "schedule_status": "parked",
          // ... all other schedule fields
        }
      },
      {
        "id": 124,
        "status": "updated",
        "trailer_id": "999",
        "log_times": {
          "succeeded": [
            {
              "type": "in",
              "in_time": "10:35:00",
              "in_user_name": "John Doe"
            }
          ],
          "failed": []
        },
        "schedule_status": "in",
        "updated_at": "2025-01-15T12:00:00Z"
      }
    ],
    "failed": [
      {
        "id": 125,
        "status": "conflict",
        "error": "Schedule was modified on server",
        "error_code": "CONFLICT",
        "client_updated_at": "2025-01-15T10:40:00Z",
        "server_updated_at": "2025-01-15T11:00:00Z",
        "conflict_details": {
          "changed_fields": ["comments", "trailer_id"],
          "client_values": {
            "comments": "Updated comments",
            "trailer_id": "456"
          },
          "server_values": {
            "comments": "Different comments from server",
            "trailer_id": "999"
          },
          "changed_by": "Manager Name",
          "changed_at": "2025-01-15T11:00:00Z"
        },
        "server_data": {
          "comments": "Different comments from server",
          "trailer_id": "999"
        }
      },
      {
        "id": 126,
        "status": "error",
        "error": "One or more log time operations failed",
        "log_times": {
          "succeeded": [],
          "failed": [
            {
              "type": "parked",
              "error": "Schedule must have completed_time before parking",
              "error_code": "PREREQUISITE_MISSING"
            }
          ]
        }
      }
    ],
    "server_time": "2025-01-15T12:00:00Z"
  }
}
```

---

**Note:** Sync Status API is not needed. The offline-sync response already provides all necessary information:
- Conflicts are detected via `client_updated_at` comparison
- Succeeded/failed arrays show what happened
- Server data is returned in conflict responses
- Mobile app can track sync status from responses

---

## 📦 Batch Management & Large Data Handling

### Batch Size Limit (Backend Validation)

**Backend Limit:**
- **Maximum schedules per request:** 10 schedules
- **If exceeded:** Return error `BATCH_SIZE_EXCEEDED`

**Why 10 Schedules?**
- Fast processing (typically < 5 seconds)
- Small payload size (reduces network issues)
- Better progress tracking (10 rows = 10% increments for 100 rows)
- Easier error handling and retry logic
- Prevents server timeout issues

### Mobile App Batch Management

**Mobile App Responsibilities:**

1. **Split Large Queues into Batches of 10**
   ```
   Offline Queue: 47 schedules
   ↓
   Batch 1: Schedules 1-10   (10 schedules)
   Batch 2: Schedules 11-20   (10 schedules)
   Batch 3: Schedules 21-30   (10 schedules)
   Batch 4: Schedules 31-40   (10 schedules)
   Batch 5: Schedules 41-47   (7 schedules)
   ```

2. **Process Batches Sequentially**
   - Send batch 1 → Wait for response → Update progress
   - Send batch 2 → Wait for response → Update progress
   - Continue until all batches complete

3. **Track Progress & Status**
   - Show progress bar (e.g., "Syncing 15/47 schedules")
   - Mark each schedule as: ⏳ Syncing | ✅ Synced | ❌ Failed
   - Update UI in real-time as batches complete

### Progress Tracking Example

**Mobile App UI:**
```
┌─────────────────────────────────────┐
│  Syncing Offline Changes            │
│  ████████████░░░░░░░░  15/47        │
│                                     │
│  ✅ Schedule #123 - Trailer updated │
│  ✅ Schedule #124 - Logged IN       │
│  ⏳ Schedule #125 - Syncing...      │
│  ⏸️ Schedule #126 - Pending         │
│  ❌ Schedule #127 - Conflict        │
└─────────────────────────────────────┘
```

### Request Structure (Simple - No Batch Info Needed)

**Example Request (10 schedules max):**
```json
{
  "schedules": [
    { "id": 1, "trailer_id": "456", ... },
    { "id": 2, "door_id": "789", ... },
    // ... up to 10 schedules
    { "id": 10, "comments": "Updated", ... }
  ]
}
```

**Response (Simple - No Batch Info):**
```json
{
  "status": "success",
  "message": "Sync completed",
  "data": {
    "succeeded": [
      { "id": 1, "status": "updated", ... },
      { "id": 2, "status": "updated", ... }
    ],
    "failed": [
      { "id": 5, "status": "conflict", ... }
    ]
  }
}
```

**Note:** Backend doesn't need to know about batches. It just processes up to 10 schedules and returns results. Mobile app handles all batch management.

### Backend Validation (Simple)

**Backend Will:**
1. **Validate batch size on receipt**
   ```php
   if (count($request->schedules) > 10) {
       return ResponseHelper::sendResponse('error', 
           'Maximum 10 schedules allowed per request. Please split into multiple batches.',
           ['error_code' => 'BATCH_SIZE_EXCEEDED', 'max_allowed' => 10]
       );
   }
   ```

2. **Process all schedules in request**
   - Process sequentially (one at a time)
   - Return succeeded/failed arrays
   - No need for batch tracking on backend

### Mobile App Best Practices

1. **Progress Tracking**
   - Show progress bar: `(synced_count / total_count) * 100`
   - Update after each batch completes
   - Mark individual schedules with status icons

2. **Error Handling**
   - If batch fails (network error): Retry entire batch
   - If some schedules fail: Continue with next batch, retry failed ones later
   - Show failed schedules in UI for user review

3. **User Experience**
   - Allow user to cancel sync (stop remaining batches)
   - Show estimated time remaining
   - Allow user to retry failed schedules manually

4. **Retry Strategy**
   - Retry failed batches with exponential backoff
   - Don't retry entire queue if one batch fails
   - Track which schedules succeeded/failed in local database

### Example Flow

**Scenario: 47 schedules to sync**

```
Mobile App:
1. Split into 5 batches (10, 10, 10, 10, 7)
2. Show progress: "0/47 synced"

Batch 1 (schedules 1-10):
  → Send to API
  → Response: 9 succeeded, 1 failed
  → Update progress: "9/47 synced"
  → Mark schedule #5 as failed

Batch 2 (schedules 11-20):
  → Send to API
  → Response: 10 succeeded
  → Update progress: "19/47 synced"

Batch 3 (schedules 21-30):
  → Send to API
  → Response: 10 succeeded
  → Update progress: "29/47 synced"

Batch 4 (schedules 31-40):
  → Send to API
  → Response: 10 succeeded
  → Update progress: "39/47 synced"

Batch 5 (schedules 41-47):
  → Send to API
  → Response: 7 succeeded
  → Update progress: "46/47 synced"
  → Show: "1 schedule failed - Tap to retry"

Final: 46 succeeded, 1 failed (schedule #5)
```

### Benefits of This Approach

✅ **Simple Backend** - Just validates max 10, processes, returns results  
✅ **Better UX** - Mobile app can show detailed progress per schedule  
✅ **Flexible** - Mobile app controls batch size and retry logic  
✅ **Reliable** - Small batches = less chance of failure  
✅ **User-Friendly** - Progress bar and status updates in real-time  

### Error Code

**BATCH_SIZE_EXCEEDED**
```json
{
  "status": "error",
  "message": "Maximum 10 schedules allowed per request. Please split into multiple batches.",
  "error_code": "BATCH_SIZE_EXCEEDED",
  "data": {
    "max_allowed": 10,
    "received": 15
  }
}
```

**Mobile App Should:**
- Split the request into batches of 10
- Retry with smaller batches
- Show error message to user if needed

---

## 🔍 Conflict Detection Logic

### How Conflicts Are Detected

1. **Client sends `client_updated_at`** - Timestamp when client last knew about the schedule
2. **Server compares** - Server's `updated_at` vs client's `client_updated_at`
3. **If server is newer** - Conflict detected, return conflict response
4. **If client is newer or equal** - Proceed with update

### Conflict Response Format

When a conflict is detected, the response includes:
- `status: "conflict"`
- `error_code: "CONFLICT"`
- `client_updated_at` - What client thought was latest
- `server_updated_at` - What server actually has
- `server_data` - Current server state (so client can merge or show to user)

### Conflict Resolution Strategy

**Server-side:** Server does NOT automatically resolve conflicts. It:
- Detects the conflict
- Returns server's current data
- Lets client decide what to do

**Client-side:** Mobile app can:
- Show conflict dialog to user
- Accept server version
- Retry with client version (if user chooses)
- Merge intelligently (e.g., if different fields changed)

---

## 📝 Request Field Details

### Schedules Array

Each schedule object represents all changes for a single schedule (like a row). It can contain:

**Required Fields:**
- `id` (required) - Schedule ID
- `client_updated_at` (required) - When client last knew about this schedule (for conflict detection)

**Optional Update Fields:**
- `trailer_id` (optional) - New trailer ID
- `door_id` (optional) - New door ID
- `comments` (optional) - New comments

**Optional Log Times Array:**
- `log_times` (optional) - Array of log time operations for this schedule
  - Each object in array contains:
    - `type` (required) - One of: `in`, `out`, `completed`, `parked`
    - `local_timestamp` (optional) - Client's local timestamp (for reference only, server generates UTC time)
    - `latitude` (required for `parked` type) - GPS latitude
    - `longitude` (required for `parked` type) - GPS longitude

**Rules:**
- At least one update field (`trailer_id`, `door_id`, `comments`) OR `log_times` array must be provided
- Can update multiple fields in one schedule object (e.g., both `trailer_id` and `door_id`)
- Can combine updates and log_times in same schedule object
- Can include multiple log_times in array (e.g., log IN, OUT, Completed, and Parked all at once)
- **CRITICAL RULE 1:** Cannot update `trailer_id` or `door_id` if `in_time` is already logged (only comments can be updated)
- **CRITICAL RULE 2:** Cannot log ANY time (IN, OUT, Completed, Parked) if `trailer_id` or `door_id` is not set
- **CRITICAL RULE 3:** Log times MUST be in strict order: IN → OUT → Completed → Parked (will fail if order is wrong)
- **Processing order:** Validate updates first (check if IN logged), then validate log_times (check trailer/door + order), then process
- `client_updated_at` is required for conflict detection
- Server always generates UTC time for each log_time (ignores `local_timestamp` from client)
- `local_timestamp` is only for logging/reference purposes
- GPS coordinates (`latitude`, `longitude`) required only for `parked` type
- **Re-logging Prevention:** Cannot re-log a time that's already logged (returns error `ALREADY_LOGGED`)

---

## ✅ Validation & Business Rules

### For Updates (Trailer/Door/Comments)

1. **Schedule Exists**
   - Schedule ID must exist
   - Schedule must belong to driver's location

2. **Trailer Validation**
   - Trailer must exist in system
   - Trailer must be available in the location
   - (Optional) Trailer must belong to schedule's carrier
   - **CRITICAL: Cannot update trailer if `in_time` is already logged**
     - If schedule has `in_time` → Return error: "Cannot update trailer after IN time is logged"
     - Error code: `UPDATE_NOT_ALLOWED`

3. **Door Validation**
   - Door must exist
   - Door must belong to schedule's location
   - Door must not be in use by another schedule (same date, has `in_time`, no `out_time`)
   - **CRITICAL: Cannot update door if `in_time` is already logged**
     - If schedule has `in_time` → Return error: "Cannot update door after IN time is logged"
     - Error code: `UPDATE_NOT_ALLOWED`

4. **Comments**
   - No special validation (free text)
   - Can be updated at any time (even after IN time is logged)

5. **Conflict Check**
   - If `client_updated_at` provided, compare with server's `updated_at`
   - If server is newer, return conflict

### For Log Times

1. **Schedule Exists**
   - Schedule ID must exist
   - Schedule must belong to driver's location

2. **CRITICAL: Trailer and Door Required for ALL Log Times**
   - **Cannot log ANY time (IN, OUT, Completed, Parked) if `trailer_id` is not set**
     - Return error: "Trailer must be selected before logging time"
     - Error code: `TRAILER_REQUIRED`
   - **Cannot log ANY time (IN, OUT, Completed, Parked) if `door_id` is not set**
     - Return error: "Door must be selected before logging time"
     - Error code: `DOOR_REQUIRED`
   - This validation applies to ALL log time types (IN, OUT, Completed, Parked)

3. **IN Operation**
   - Requires `trailer_id` and `door_id` to be set (validated above)
   - Door must not be in use by another schedule (same date, has `in_time`, no `out_time`)
   - **CRITICAL: Cannot log IN if `in_time` already exists**
     - If `in_time` is already logged → Return error: "IN time is already logged"
     - Error code: `ALREADY_LOGGED`
   - **Must be first in sequence** (cannot have OUT, Completed, or Parked before IN)

4. **OUT Operation**
   - Requires `trailer_id` and `door_id` to be set (validated above)
   - Requires `in_time` to be set
   - **CRITICAL: Cannot log OUT if `out_time` already exists**
     - If `out_time` is already logged → Return error: "OUT time is already logged"
     - Error code: `ALREADY_LOGGED`
   - **Must come after IN** (cannot log OUT before IN)

5. **Completed Operation**
   - Requires `trailer_id` and `door_id` to be set (validated above)
   - Requires both `in_time` and `out_time` to be set
   - **CRITICAL: Cannot log Completed if `completed_time` already exists**
     - If `completed_time` is already logged → Return error: "Completed time is already logged"
     - Error code: `ALREADY_LOGGED`
   - **Must come after OUT** (cannot log Completed before OUT)

6. **Parked Operation**
   - Requires `trailer_id` and `door_id` to be set (validated above)
   - Requires `completed_time` to be set
   - Requires GPS coordinates (`latitude`, `longitude`)
   - **CRITICAL: Cannot log Parked if `parked_time` already exists**
     - If `parked_time` is already logged → Return error: "Parked time is already logged"
     - Error code: `ALREADY_LOGGED`
   - **Must come after Completed** (cannot log Parked before Completed)

7. **CRITICAL: Log Times Must Be In Order**
   - **Strict order required:** IN → OUT → Completed → Parked
   - If order is wrong (e.g., OUT before IN, Completed before OUT), return error
   - Error: "Log times must be in order: IN → OUT → Completed → Parked"
   - Error code: `INVALID_ORDER`
   - Validate order BEFORE processing any log_times

---

## ⚠️ Error Handling

### Error Response Format

```json
{
  "status": "error",
  "message": "Human-readable error message",
  "error_code": "VALIDATION_ERROR",
  "data": {
    "schedule_id": 123,
    "operation": "log_time",
    "details": {
      "missing_field": "in_time",
      "required_for": "out",
      "current_state": {
        "in_time": null,
        "out_time": null
      }
    }
  }
}
```

**Enhanced Error Context:**
- Include which field/operation failed
- Include what's missing/required
- Include current state (helps debugging)
- Include prerequisite information

### Error Codes

| Error Code | Description | When It Occurs |
|------------|-------------|----------------|
| `VALIDATION_ERROR` | Business rule validation failed | Missing prerequisites, invalid data |
| `CONFLICT` | Data conflict detected | Server version is newer than client version |
| `NOT_FOUND` | Schedule not found | Schedule ID doesn't exist or not accessible |
| `UNAUTHORIZED` | Permission denied | User doesn't have access to schedule |
| `DOOR_IN_USE` | Door is currently in use | Another schedule is using the door |
| `PREREQUISITE_MISSING` | Required previous step not completed | e.g., trying to log OUT without IN |
| `ALREADY_COMPLETED` | Operation already performed | e.g., trying to log IN when already logged IN |
| `INVALID_TYPE` | Invalid log time type | Type not one of: in, out, completed, parked |
| `MISSING_COORDINATES` | GPS coordinates missing | Required for parked type |
| `BATCH_SIZE_EXCEEDED` | Too many schedules in request | More than 10 schedules sent |
| `INVALID_ORDER` | Log times in wrong order | Must be: IN → OUT → Completed → Parked |
| `UPDATE_NOT_ALLOWED` | Cannot update after IN logged | Trailer/door cannot be updated if in_time exists |
| `TRAILER_REQUIRED` | Trailer must be selected | Cannot log time without trailer_id |
| `DOOR_REQUIRED` | Door must be selected | Cannot log time without door_id |
| `ALREADY_LOGGED` | Time already logged | Cannot re-log a time that already exists (e.g., IN already logged) |

### Partial Success Handling

The API returns partial success:
- Operations that succeed are in `succeeded` array
- Operations that fail are in `failed` array with error details
- Client can retry failed operations individually if needed

---

## 🔄 Processing Flow

### Offline Sync API Flow

```
1. Receive request with schedules[] array
   ↓
2. Validate request structure (max 10 schedules)
   ↓
3. Process Each Schedule (Row):
   For each schedule in schedules[]:
     a. Check if schedule exists
     b. Check conflict (if client_updated_at provided)
        - If conflict → Add to failed array, skip rest
     c. Validate Updates (if provided):
        - **Check if IN time is logged** → If yes, reject trailer_id and door_id updates
        - Validate trailer/door if provided and allowed
        - Comments can always be updated
     d. Validate Log Times (if provided):
        - **Check trailer_id and door_id are set** (required for ALL log times)
        - **Validate order: IN → OUT → Completed → Parked** (strict order required)
        - If validation fails → Add to failed array, skip rest
     e. Process Updates (if provided and validated):
        - Update schedule fields (trailer_id, door_id, comments)
        - Only update fields that are allowed (trailer/door only if in_time is null)
     f. Process Log Times Array (if provided and validated):
        - Process in strict order: IN → OUT → Completed → Parked
        - For each log_time:
          * **Check if time already exists** (if yes, return error - cannot re-log)
          * Generate UTC time (server-side)
          * Update schedule with time
          * Update yard check status (if applicable)
          * Track succeeded/failed for this log_time
        - Continue processing even if one fails (partial success)
     g. Build response:
        - If all operations succeed: Add to succeeded array with full schedule object
        - If any operation fails: Add to failed array with detailed error context
   ↓
4. Return response with succeeded and failed arrays
```

**Processing Order (Important):**
1. **Conflict Check** - First (if fails, skip everything)
2. **Validate Updates** - Check if IN time logged (if yes, reject trailer/door updates)
3. **Validate Log Times** - Check trailer/door required, validate order (IN → OUT → Completed → Parked)
4. **Process Updates** - Only if validation passed (trailer/door only if in_time is null)
5. **Process Log Times** - Only if validation passed (in strict order: IN → OUT → Completed → Parked)

**Critical Business Rules:**
- **Rule 1:** If `in_time` is logged → Cannot update `trailer_id` or `door_id` (only comments allowed)
- **Rule 2:** If `trailer_id` or `door_id` not set → Cannot log ANY time (IN, OUT, Completed, Parked)
- **Rule 3:** Log times MUST be in order: IN → OUT → Completed → Parked (strict validation)

**Re-logging Prevention:**
- **CRITICAL: Cannot re-log a time that's already logged**
- If `in_time` already exists → Cannot log IN again (return error)
- If `out_time` already exists → Cannot log OUT again (return error)
- If `completed_time` already exists → Cannot log Completed again (return error)
- If `parked_time` already exists → Cannot log Parked again (return error)
- Error code: `ALREADY_LOGGED`
- Updates are naturally idempotent (setting same value is safe)


---

## 📊 Example Scenarios

### Scenario 1: Successful Sync (Update + Multiple Log Times)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "door_id": "789",
      "log_times": [
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:30:00Z"
        },
        {
          "type": "out",
          "local_timestamp": "2025-01-15T11:00:00Z"
        },
        {
          "type": "completed",
          "local_timestamp": "2025-01-15T11:30:00Z"
        },
        {
          "type": "parked",
          "local_timestamp": "2025-01-15T12:00:00Z",
          "latitude": 40.7128,
          "longitude": -74.0060
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [
      {
        "id": 123,
        "status": "updated",
        "trailer_id": "456",
        "door_id": "789",
        "log_times": {
          "succeeded": [
            {
              "type": "in",
              "in_time": "10:30:00",
              "in_user_name": "John Doe"
            },
            {
              "type": "out",
              "out_time": "11:00:00",
              "out_user_name": "John Doe"
            },
            {
              "type": "completed",
              "completed_time": "11:30:00",
              "completed_user_name": "John Doe"
            },
            {
              "type": "parked",
              "parked_time": "12:00:00",
              "parked_user_name": "John Doe"
            }
          ],
          "failed": []
        },
        "schedule_status": "parked",
        "updated_at": "2025-01-15T12:00:00Z"
      }
    ],
    "failed": []
  }
}
```

### Scenario 2: Conflict Detected

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [],
    "failed": [
      {
        "id": 123,
        "status": "conflict",
        "error": "Schedule was modified on server",
        "error_code": "CONFLICT",
        "client_updated_at": "2025-01-15T10:30:00Z",
        "server_updated_at": "2025-01-15T11:00:00Z",
        "server_data": {
          "trailer_id": "999",
          "door_id": "888",
          "comments": "Server updated comments"
        }
      }
    ]
  }
}
```

### Scenario 3: Update Trailer/Door After IN Logged (Error)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "999",
      "door_id": "888",
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Note:** Schedule #123 already has `in_time` logged

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [],
    "failed": [
      {
        "id": 123,
        "status": "error",
        "error": "Cannot update trailer or door after IN time is logged",
        "error_code": "UPDATE_NOT_ALLOWED",
        "details": {
          "in_time": "10:00:00",
          "attempted_updates": ["trailer_id", "door_id"],
          "allowed_updates": ["comments"]
        }
      }
    ]
  }
}
```

### Scenario 4: Log Time Without Trailer/Door (Error)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "log_times": [
        {
          "type": "out",
          "local_timestamp": "2025-01-15T11:00:00Z"
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Note:** Schedule #123 has `in_time` but `trailer_id` or `door_id` is null

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [],
    "failed": [
      {
        "id": 123,
        "status": "error",
        "error": "Trailer and door must be selected before logging time",
        "error_code": "TRAILER_REQUIRED",
        "details": {
          "trailer_id": null,
          "door_id": null,
          "log_time_type": "out"
        }
      }
    ]
  }
}
```

### Scenario 5: Log Times Wrong Order (Error)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "door_id": "789",
      "log_times": [
        {
          "type": "out",
          "local_timestamp": "2025-01-15T11:00:00Z"
        },
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:30:00Z"
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [],
    "failed": [
      {
        "id": 123,
        "status": "error",
        "error": "Log times must be in order: IN → OUT → Completed → Parked",
        "error_code": "INVALID_ORDER",
        "details": {
          "provided_order": ["out", "in"],
          "required_order": ["in", "out", "completed", "parked"]
        }
      }
    ]
  }
}
```

### Scenario 6: Re-logging Time That Already Exists (Error)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "door_id": "789",
      "log_times": [
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:30:00Z"
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Note:** Schedule #123 already has `in_time` logged at "10:00:00"

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [],
    "failed": [
      {
        "id": 123,
        "status": "error",
        "error": "IN time is already logged",
        "error_code": "ALREADY_LOGGED",
        "details": {
          "log_time_type": "in",
          "existing_time": "10:00:00",
          "existing_user": "John Doe"
        }
      }
    ]
  }
}
```

### Scenario 7: Partial Success (Some Log Times Fail)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "log_times": [
        {
          "type": "in",
          "local_timestamp": "2025-01-15T10:30:00Z"
        },
        {
          "type": "out",
          "local_timestamp": "2025-01-15T11:00:00Z"
        },
        {
          "type": "parked",
          "local_timestamp": "2025-01-15T12:00:00Z",
          "latitude": 40.7128,
          "longitude": -74.0060
        }
      ],
      "client_updated_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [
      {
        "id": 123,
        "status": "updated",
        "log_times": {
          "succeeded": [
            {
              "type": "in",
              "in_time": "10:30:00",
              "in_user_name": "John Doe"
            },
            {
              "type": "out",
              "out_time": "11:00:00",
              "out_user_name": "John Doe"
            }
          ],
          "failed": [
            {
              "type": "parked",
              "error": "Schedule must have completed_time before parking",
              "error_code": "PREREQUISITE_MISSING"
            }
          ]
        },
        "schedule_status": "out",
        "updated_at": "2025-01-15T12:00:00Z"
      }
    ],
    "failed": []
  }
}
```

### Scenario 4: Multiple Schedules (Partial Success)

**Request:**
```json
{
  "schedules": [
    {
      "id": 123,
      "trailer_id": "456",
      "client_updated_at": "2025-01-15T10:30:00Z"
    },
    {
      "id": 124,
      "door_id": "789",
      "client_updated_at": "2025-01-15T10:35:00Z"
    },
    {
      "id": 125,
      "comments": "Updated",
      "client_updated_at": "2025-01-15T10:40:00Z"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "data": {
    "succeeded": [
      {
        "id": 123,
        "status": "updated",
        "trailer_id": "456",
        "updated_at": "2025-01-15T12:00:00Z"
      },
      {
        "id": 124,
        "status": "updated",
        "door_id": "789",
        "updated_at": "2025-01-15T12:00:00Z"
      }
    ],
    "failed": [
      {
        "id": 125,
        "status": "conflict",
        "error": "Schedule was modified on server",
        "error_code": "CONFLICT",
        "client_updated_at": "2025-01-15T10:40:00Z",
        "server_updated_at": "2025-01-15T11:00:00Z",
        "server_data": {
          "comments": "Different comments from server"
        }
      }
    ]
  }
}
```


---

## 🔐 Security & Authorization

1. **Authentication Required**
   - All endpoints require valid JWT token
   - Token must be in `Authorization: Bearer {token}` header

2. **Role-Based Access**
   - Only `driver` role can access these endpoints
   - Drivers can only sync schedules for their assigned location

3. **Data Isolation**
   - Drivers can only see/modify schedules for their location
   - Server validates location access for each schedule

4. **Rate Limiting**
   - Apply throttle middleware (same as other driver endpoints)
   - Prevent abuse of batch operations

---

## 📋 Questions for Mobile App Developer

### 1. Request Structure
- ✅ Is the row-based structure clear? (Each schedule object contains all changes for that schedule)
- ✅ Multiple fields can be updated in one schedule object (e.g., both trailer_id and door_id)
- ✅ Can combine updates and log_time in same schedule object
- ❓ Should `local_timestamp` be required or optional in log_time object?
- ✅ Multiple log_time operations allowed in `log_times` array (e.g., log IN, OUT, Completed, Parked all at once)

### 2. Conflict Detection
- ✅ Is `client_updated_at` approach clear?
- ❓ What should mobile app do when conflict is detected? (Show dialog? Auto-merge? Retry?)
- ❓ Should we include more server data in conflict response? (e.g., full schedule object)


### 4. Error Handling
- ✅ Are the error codes sufficient?
- ❓ Should we include more context in error responses?
- ❓ How should mobile app handle partial success? (Retry failed ones? Show notification?)

### 5. Performance & Batching
- ✅ Backend limit: Max 10 schedules per request (validated on backend)
- ✅ Mobile app handles all batching (splits queue into batches of 10)
- ✅ Mobile app shows progress bar and tracks which rows are syncing/completed
- ❓ How should mobile app handle timeout scenarios? (Retry entire batch? Retry unprocessed only?)

### 6. Time Handling
- ✅ Server generates UTC time - is this acceptable?
- ❓ Should we return both UTC and local time in responses?
- ❓ Should `local_timestamp` from client be stored anywhere? (For audit/logging?)

---

## 🚀 Implementation Notes

### Backend Considerations

1. **Transaction Handling**
   - Each operation (update or log_time) should be in its own transaction
   - If one operation fails, others should still process
   - Don't rollback entire batch if one fails

2. **Performance & Batch Processing**
   - Validate batch size on receipt (max 10 schedules) - return `BATCH_SIZE_EXCEEDED` if exceeded
   - Process schedules sequentially (not in parallel) to avoid race conditions
   - Consider database indexes on `updated_at` for conflict detection
   - Cache door availability checks if needed
   - Simple processing: receive up to 10 schedules, process all, return succeeded/failed arrays
   - No need for batch tracking or partial results - mobile app handles all batching

3. **Logging**
   - Log all sync operations for audit trail
   - Log conflicts for analysis
   - Log validation errors

4. **Real-time Updates**
   - After successful sync, broadcast schedule updates via WebSocket (if applicable)
   - Other users should see changes in real-time

### Testing Considerations

1. **Unit Tests**
   - Test conflict detection logic
   - Test validation rules
   - Test business rules (prerequisites)

2. **Integration Tests**
   - Test batch processing (10 schedules - max allowed)
   - Test batch size validation (reject > 10, return BATCH_SIZE_EXCEEDED)
   - Test partial success scenarios (some succeed, some fail)
   - Test with exactly 10 schedules (boundary test)
   - Test with 1 schedule (minimum)

3. **Edge Cases**
   - Empty arrays in request
   - Invalid schedule IDs
   - Concurrent modifications
   - Network timeouts during processing
   - Batch size exceeded (> 10 schedules) - return BATCH_SIZE_EXCEEDED
   - Exactly 10 schedules (boundary test)
   - 1 schedule (minimum test)

---

## 📝 Summary

### What We're Building

1. **POST /api/schedules/offline-sync**
   - Combined endpoint for updates + log times
   - Batch processing (max 10 schedules per request)
   - Conflict detection via `client_updated_at`
   - Partial success support
   - Returns all necessary sync information in response

### Key Features

- ✅ Single combined API (not separate endpoints)
- ✅ Row-based structure (each schedule object contains all changes for that schedule)
- ✅ Can combine updates (trailer/door/comments) and log_times array in same schedule object
- ✅ Multiple log_times per schedule (array allows IN, OUT, Completed, Parked all at once)
- ✅ GPS coordinates (latitude/longitude) required for parked type
- ✅ Batch management (max 10 schedules per request, mobile app handles all batching and progress tracking)
- ✅ Conflict detection via `client_updated_at`
- ✅ Partial success (some schedules can succeed, others fail; some log_times can succeed, others fail)
- ✅ Detailed error messages with error codes
- ✅ Server generates UTC time (client doesn't send time)

### Next Steps

1. Review this design with mobile app developer
2. Confirm request/response structures
3. Clarify conflict resolution strategy
4. Finalize error codes and messages
5. Set batch size limits
6. Implement APIs

---

**Document Version:** 1.0  
**Last Updated:** 2025-01-15  
**Status:** Draft - For Discussion
