# Mobile App Changes — Session-Based Path Tracking

## ⚠️ CRITICAL: Namespace Change

The WebSocket server uses **namespaces** to separate agent and admin connections.

- **Agents** must connect to **`/agent`** namespace: `https://dayloan.agniplay.com/agent`
- **Admins** must connect to **`/admin`** namespace: `https://dayloan.agniplay.com/admin`
- Connecting to the default namespace (`/`) is **rejected** with error code `4003`.

**Fix in `agent_client.dart` — change:**
```dart
// WRONG — connects to default namespace, no auth, no handlers
_socket = IO.io(serverUrl, IO.OptionBuilder()...)

// RIGHT — connects to /agent namespace with auth middleware
_socket = IO.io('$serverUrl/agent', IO.OptionBuilder()...)
```

**Fix in `admin_client.dart` — change:**
```dart
// WRONG
_socket = IO.io(serverUrl, IO.OptionBuilder()...)

// RIGHT
_socket = IO.io('$serverUrl/admin', IO.OptionBuilder()...)
```

## What Changed in the Backend

| Area | Before | After |
|------|--------|-------|
| Location storage | No session grouping | All GPS tagged with `tracking_session_id` |
| Session model | None (just raw points) | `tracking_sessions` table with distance, duration, speed stats |
| Battery/GPS metadata | Not stored | Every point stores `battery_level`, `battery_charging`, `gps_provider` |
| Device timestamps | `created_at` only (server time) | `recorded_at` (device time) + `received_at` (server time) |
| Geofence events | Not supported | `tracking_geofence_events` table (pause, resume, delivery_completed, etc.) |
| Path retrieval | No simplified path endpoint | Douglas-Peucker compression via `?simplify=true` |
| New events | — | `tracking.start`, `tracking.stop`, `tracking.pause`, `tracking.resume`, `tracking.delivery_completed` |

---

## File-by-File Changes

### 1. `lib/services/ws_packet.dart`

| Change | Details |
|--------|---------|
| `location()` | **Added params**: `sessionId`, `recordedAt`, `batteryLevel`, `batteryCharging`, `gpsProvider`, `mockLocation` |
| `trackingStart()` | **New method**: sends `latitude`, `longitude`, `device_id`, `recorded_at` — used when agent taps "Start Duty" |
| `trackingStop()` | **New method**: sends `latitude`, `longitude` — used when agent taps "Stop Duty" |
| `deliveryCompleted()` | **New method**: sends `receipt_id`, `loan_id`, `latitude`, `longitude`, `accuracy`, `recorded_at`, `metadata` — used when agent completes a delivery |
| `pauseTracking()` | **New method**: sends current location — used when agent pauses tracking (app background) |
| `resumeTracking()` | **New method**: sends current location — used when agent resumes tracking |
| `WSACK.sessionId` | **New field**: the server returns `session_id` in `tracking.start` ACK |

**You need to**: replace your old `ws_packet.dart` with the new one from `flutter_websocket/ws_packet.dart`.

---

### 2. `lib/services/agent_client.dart`

| Change | Details |
|--------|---------|
| `deviceId` | **New constructor param**: unique device ID for session tracking |
| `_sessionId` | **New state**: stores the current tracking session UUID |
| `onSession` | **New stream**: emits `String?` when session ID changes (connect/disconnect duty) |
| `connect()` | **Updated**: reads saved `session_id` from `SharedPreferences` and sends it in `auth` on reconnect — this resumes the same session automatically |
| `sendLocation()` | **Updated**: passes `sessionId`, `batteryLevel`, `batteryCharging`, `gpsProvider`, `mockLocation` to `WSPacket.location()` |
| `startTracking()` | **New method**: emits `tracking.start` event, saves returned `session_id` to `SharedPreferences` |
| `stopTracking()` | **New method**: emits `tracking.stop` event, clears `session_id` from `SharedPreferences` |
| `pauseTracking()` | **New method**: emits `tracking.pause` event |
| `resumeTracking()` | **New method**: emits `tracking.resume` event |
| `deliveryCompleted()` | **New method**: emits `tracking.delivery_completed` event |

**You need to**: replace your old `agent_client.dart` with the new one from `flutter_websocket/agent_client.dart`.

**New dependency**: `shared_preferences: ^2.3.0`

---

### 3. `lib/services/admin_client.dart`

| Change | Details |
|--------|---------|
| `LiveLocation.sessionId` | **New field**: session ID from the broadcast |
| `AgentStatus.sessionId` | **New field**: session ID from the connect event |
| `EmergencyAlert.sessionId` | **New field**: session ID from the alert |
| `baseUrl` | **New constructor param**: REST API base URL (default `https://dayloan.agniplay.com/api`) |
| `SessionSummary` | **New class**: model for a completed session (start/end time, distance, duration, speed, point count) |
| `SessionPathPoint` | **New class**: model for a GPS point including battery/GPS metadata |
| `GeofenceEvent` | **New class**: model for geofence events (delivery_completed, pause, resume) |
| `getAgentLiveLocation()` | **New method**: REST `GET /admin/agents/{id}/live` — returns current position + active session |
| `getSessionHistory()` | **New method**: REST `GET /admin/agents/{id}/history` — returns list of past sessions |
| `getSessionPath()` | **New method**: REST `GET /admin/agents/{id}/history/{session_id}` — returns simplified path + events |
| `getSessionEvents()` | **New method**: REST `GET /admin/agents/{id}/history/{session_id}/events` — returns geofence events |
| `getTodayPath()` | **New method**: REST `GET /admin/agents/{id}/today-path` — returns today's merged path across sessions |

**You need to**: replace your old `admin_client.dart` with the new one from `flutter_websocket/admin_client.dart`.

**New dependency**: `http: ^1.2.0`

---

## Changes in Your App Code

### Agent App

| Where | Before | After |
|-------|--------|-------|
| Dashboard init | Just connect WebSocket + start GPS | **Call `startTracking()`** after connecting to create a session |
| GPS callback | `sendLocation(lat, lng, accuracy)` | `sendLocation(LocationData(lat, lng, accuracy, **batteryLevel: X, gpsProvider: 'fused'**))` |
| Start duty button | Nothing (always tracking) | **Call `startTracking()`** → duty session created |
| Stop duty button | Nothing (just disconnect) | **Call `stopTracking()`** → session ended, stats calculated |
| App background | Nothing | **Call `pauseTracking()`** → geofence event logged |
| App foreground | Nothing | **Call `resumeTracking()`** → geofence event logged |
| Delivery complete | Nothing | **Call `deliveryCompleted(receiptId: X)** → geofence event logged |

**Critical new pattern** — session lifecycle:
```dart
// Before: always-on tracking
agent.connect();
agent.sendLocation(...);

// After: explicit duty cycle
agent.connect();
await agent.startTracking();   // ← NEW: creates session
// ... GPS sends session-aware locations ...
await agent.pauseTracking();   // ← NEW: pause on background
await agent.resumeTracking();  // ← NEW: resume on foreground
await agent.deliveryCompleted(receiptId: 123); // ← NEW: mark delivery
await agent.stopTracking();    // ← NEW: ends session
```

---

### Admin App

| Where | Before | After |
|-------|--------|-------|
| Live location marker | Just lat/lng | lat/lng + **session_id** (show on marker tooltip) |
| Agent online event | Just agent name | agent name + **session_id** |
| Session history button | Didn't exist | **Call `getSessionHistory(agentId)`** → list of past trips |
| Path replay button | Didn't exist | **Call `getSessionPath(agentId, sessionId)`** → polyline-ready path |
| Today path button | Didn't exist | **Call `getTodayPath(agentId)`** → today's full trail |
| Map polyline | Not supported | Draw path using `getSessionPath()` results |
| Map event markers | Not supported | Show geofence event icons using `getSessionEvents()` |

**New REST calls in admin:**
```dart
// Before: only WebSocket live stream
admin.onLocation.listen((loc) => updateMap(loc));

// After: WebSocket for live + REST for history
// Live: same as before
admin.onLocation.listen((loc) => updateMap(loc));

// History (REST):
final sessions = await admin.getSessionHistory(agentId);
final result = await admin.getSessionPath(agentId, sessionId, simplify: true);
final path = result['path'];     // List<SessionPathPoint> → draw Polyline
final events = result['events']; // List<GeofenceEvent> → draw markers
final summary = result['summary']; // compression stats
```

---

## New WebSocket Events for Agent

| Event | When to Send | Payload |
|-------|-------------|---------|
| `tracking.start` | User taps "Start Duty" | `{ latitude?, longitude?, device_id? }` |
| `tracking.stop` | User taps "Stop Duty" | `{ latitude?, longitude? }` |
| `tracking.pause` | App goes to background | `{ latitude?, longitude?, accuracy? }` |
| `tracking.resume` | App returns to foreground | `{ latitude?, longitude?, accuracy? }` |
| `tracking.delivery_completed` | Delivery is done | `{ receipt_id, loan_id?, latitude?, longitude?, accuracy?, metadata? }` |
| `battery_level` | In every `tracking.location` | `{ battery_level: 85.0, battery_charging: false, gps_provider: 'fused' }` |

---

## New REST Endpoints for Admin

| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/admin/agents/{id}/live` | Current position + active session |
| `GET` | `/admin/agents/{id}/history` | List of past sessions (paginated) |
| `GET` | `/admin/agents/{id}/history/{session_id}` | Full GPS path with Douglas-Peucker |
| `GET` | `/admin/agents/{id}/history/{session_id}/events` | Geofence events |
| `GET` | `/admin/agents/{id}/today-path` | All today's points merged |

---

## Required pubspec.yaml Changes

```yaml
dependencies:
  socket_io_client: ^3.0.2       # already present
  uuid: ^4.5.1                   # already present
  geolocator: ^13.0.2            # already present (agent only)
  shared_preferences: ^2.3.0     # NEW - for session_id persistence
  http: ^1.2.0                   # NEW - admin REST calls
  flutter_map: ^7.0.0            # NEW (optional) - map polyline
  latlong2: ^0.9.0              # NEW (optional) - map coordinates
```

---

## Testing Checklist

- [ ] Agent app shows "Start Duty" / "Stop Duty" button
- [ ] Tapping Start Duty creates a `tracking_sessions` row (check DB)
- [ ] GPS locations tagged with `tracking_session_id` 
- [ ] Battery level and GPS provider saved with each point
- [ ] Pause/resume creates `tracking_geofence_events` rows
- [ ] Delivery completed creates geofence event
- [ ] Stop Duty calculates distance and duration
- [ ] Admin app receives live locations (same as before)
- [ ] Admin REST: `GET /agents/{id}/history` returns sessions
- [ ] Admin REST: `GET /agents/{id}/history/{session_id}?simplify=true` returns compressed path
- [ ] Admin map shows polyline
- [ ] Disconnect WiFi → 30-min session expiry → auto-ends session
- [ ] Reconnect within 30 min → same session continues
