# Prompt / spécification finale — intégration Android native ↔ WebView pour les notifications push POS

## Contexte

Tu travailles sur l'application Android native qui héberge la WebView POS de TTM.
Le backend et la WebView ont déjà été préparés.
Ton objectif est de brancher exactement le contrat natif → WebView attendu pour les notifications push.

**Important :**
- le backend existe déjà ;
- la WebView existe déjà ;
- tu ne dois pas inventer un autre contrat ;
- tu dois implémenter exactement les événements décrits ci-dessous.

---

## Objectif

L'application Android native est la source de vérité pour :
- `terminalId` : identifiant stable de l'installation Android/WebView ;
- `fcmToken` : token Firebase Cloud Messaging courant ;
- `notificationsEnabled` : état réel des notifications côté Android.

La WebView reçoit ces informations depuis le natif puis appelle elle-même le backend via :
- `POST /push/register`
- `POST /push/heartbeat`
- `POST /push/unregister`

### Règle absolue
L'application Android native **ne doit pas appeler directement** ces endpoints dans ce scope.
Le rôle du natif ici est uniquement de **pousser les bons événements vers la WebView**.

---

## Canal de communication imposé

La WebView expose une fonction globale JavaScript :

```js
window.onNativeEvent(event)
```

Le natif doit appeler cette fonction avec un **objet JavaScript**.

### Format général imposé

```js
window.onNativeEvent({
  type: "<event_name>",
  schemaVersion: 1,
  ...payload
})
```

### Règles strictes
- `window.onNativeEvent(...)` est l'unique point d'entrée pour ces événements.
- `schemaVersion: 1` doit être envoyé systématiquement.
- Les champs sont sensibles à la casse.
- Il faut envoyer un **objet JS** et non une string JSON brute.
- Les noms de propriétés doivent être exacts :
  - `type`
  - `schemaVersion`
  - `terminalId`
  - `fcmToken`
  - `notificationsEnabled`

---

## Événements obligatoires à implémenter

## 1) `push_status`

### Rôle
Envoyer l'état push courant connu par Android à la WebView.

### Contrat exact

```ts
{
  type: "push_status"
  schemaVersion?: number
  terminalId?: string
  fcmToken?: string
  notificationsEnabled?: boolean
}
```

### Exemple nominal

```js
window.onNativeEvent({
  type: "push_status",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  fcmToken: "fcm_token_v1",
  notificationsEnabled: true
})
```

### Quand l'émettre
Le natif doit émettre `push_status` dans les cas suivants :

1. **Juste après** avoir reçu le message `webview_ready` venant de la WebView.
2. À chaque recréation / rechargement de la WebView.
3. Quand l'application Android a déterminé son état push courant au démarrage.
4. Quand l'état `notificationsEnabled` change.
5. Chaque fois qu'une resynchronisation de l'état courant est nécessaire.

### Ce que la WebView fera ensuite
Après réception de `push_status`, la WebView :
- stocke `terminalId` ;
- stocke `fcmToken` ;
- stocke `notificationsEnabled` ;
- puis, si l'utilisateur POS est authentifié, si `terminalId` existe, si `fcmToken` existe et si `notificationsEnabled !== false`, elle appelle :
  - `POST /push/register`
  - puis `POST /push/heartbeat`

---

## 2) `push_token_refreshed`

### Rôle
Informer la WebView qu'un nouveau token FCM est disponible.

### Contrat exact

```ts
{
  type: "push_token_refreshed"
  schemaVersion?: number
  terminalId?: string
  fcmToken?: string
  notificationsEnabled?: boolean
}
```

### Exemple nominal

```js
window.onNativeEvent({
  type: "push_token_refreshed",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  fcmToken: "fcm_token_v2",
  notificationsEnabled: true
})
```

### Quand l'émettre
Le natif doit émettre `push_token_refreshed` dans les cas suivants :

1. Immédiatement après qu'Android/Firebase a fourni un nouveau token FCM.
2. Dès qu'un token courant diffère de celui précédemment connu.
3. Après restauration d'état si le token récupéré a changé.

### Ce que la WebView fera ensuite
Après réception de `push_token_refreshed`, la WebView :
- remplace le token mémorisé ;
- conserve le même `terminalId` ;
- relance ensuite `POST /push/register` avec le nouveau token ;
- continue à faire ses `heartbeat` normalement.

---

## 3) Payload métier des notifications `order_item_ready`

Quand le backend envoie une notification push de type `order_item_ready`, Android doit considérer les champs data suivants comme la source de vérité pour l'affichage de la notification locale et la navigation au clic :

```ts
{
  event: "order_item_ready"
  notificationTitle: string
  notificationBody: string
  targetRouteName: "cashier-order-detail"
  targetRoutePath: string
  targetOrderId: string
  targetStationId?: string
  orderId: string
  orderItemId: string
  orderShortId: string
  productLabel: string
  readyStationName: string
}
```

### Règles Android côté affichage
- Android doit afficher une notification locale avec :
  - `notificationTitle`
  - `notificationBody`
- Android ne doit pas recomposer lui-même le texte métier si ces champs sont présents.

### Exemple attendu

```json
{
  "event": "order_item_ready",
  "notificationTitle": "Produit prêt · #42",
  "notificationBody": "Burger Classic prêt à Grill",
  "targetRouteName": "cashier-order-detail",
  "targetRoutePath": "/pos/stations/cashier/orders/e081aee7-c7da-4b49-8773-01a9797cbc10?stationId=station-cash-1",
  "targetOrderId": "e081aee7-c7da-4b49-8773-01a9797cbc10",
  "targetStationId": "station-cash-1",
  "orderId": "e081aee7-c7da-4b49-8773-01a9797cbc10",
  "orderItemId": "c44e20a0-17dd-48bd-b460-d82244c6319c",
  "orderShortId": "#42",
  "productLabel": "Burger Classic",
  "readyStationName": "Grill"
}
```

---

## 4) Événements WebView supplémentaires attendus pour l'ouverture d'une notification

En plus de `push_status` et `push_token_refreshed`, Android doit renvoyer à la WebView les événements suivants :

### `push_message_received`

Émis quand un payload push est reçu côté natif.

```js
window.onNativeEvent({
  type: "push_message_received",
  schemaVersion: 1,
  event: "order_item_ready",
  notificationTitle: "Produit prêt · #42",
  notificationBody: "Burger Classic prêt à Grill",
  targetRouteName: "cashier-order-detail",
  targetRoutePath: "/pos/stations/cashier/orders/<orderId>?stationId=<stationId>",
  targetOrderId: "<orderId>",
  targetStationId: "<stationId>"
})
```

### `push_notification_opened`

Émis quand l'utilisateur clique sur la notification. Android doit retransmettre le **même payload métier** que celui reçu dans le push, afin que la WebView puisse router directement vers la bonne vue.

```js
window.onNativeEvent({
  type: "push_notification_opened",
  schemaVersion: 1,
  event: "order_item_ready",
  notificationTitle: "Produit prêt · #42",
  notificationBody: "Burger Classic prêt à Grill",
  targetRouteName: "cashier-order-detail",
  targetRoutePath: "/pos/stations/cashier/orders/<orderId>?stationId=<stationId>",
  targetOrderId: "<orderId>",
  targetStationId: "<stationId>"
})
```

---

## Données métier à respecter strictement

## `terminalId`

### Définition
`terminalId` représente l'identité stable de l'installation Android/WebView.

### Contraintes
- doit être stable dans le temps ;
- doit être unique par installation ;
- ne doit pas dépendre du user connecté ;
- ne doit pas dépendre du shop ;
- ne doit pas dépendre de la session ;
- ne doit pas changer à chaque lancement ;
- doit être renvoyé à la fois dans `push_status` et dans `push_token_refreshed`.

### Accepté
- UUID généré une fois puis persisté localement.

### Interdit
- ID utilisateur ;
- ID shop ;
- timestamp de session ;
- ID recalculé à chaque démarrage.

---

## `fcmToken`

### Définition
Token FCM courant de l'installation.

### Contraintes
- peut changer dans le temps ;
- doit toujours refléter le token valide courant ;
- doit être envoyé au plus vite après refresh ;
- si disponible, il doit être inclus dans `push_status` ;
- il doit être inclus dans `push_token_refreshed`.

---

## `notificationsEnabled`

### Définition
État réel de disponibilité/autorisation des notifications côté Android.

### Valeurs attendues
- `true` : notifications activées et utilisables ;
- `false` : notifications désactivées, refusées ou indisponibles.

### Contraintes
- envoyer un booléen réel, jamais une string ;
- si `notificationsEnabled === false`, la WebView ne synchronisera pas le push backend ;
- ce champ doit être renvoyé dans `push_status` et recommandé aussi dans `push_token_refreshed`.

---

## Séquence attendue de bout en bout

## Étape 1 — la WebView annonce qu'elle est prête
La WebView envoie au natif un message de type :

```json
{ "type": "webview_ready", "schemaVersion": 1 }
```

## Étape 2 — Android répond avec `push_status`

```js
window.onNativeEvent({
  type: "push_status",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  fcmToken: "fcm_token_v1",
  notificationsEnabled: true
})
```

## Étape 3 — la WebView synchronise le backend
La WebView appelle ensuite elle-même :
- `POST /push/register`
- `POST /push/heartbeat`

## Étape 4 — le token FCM change

```js
window.onNativeEvent({
  type: "push_token_refreshed",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  fcmToken: "fcm_token_v2",
  notificationsEnabled: true
})
```

## Étape 5 — la WebView se réenregistre
La WebView relance `POST /push/register` avec le nouveau token.

---

## Cas limites à gérer

## Cas A — notifications refusées / désactivées
Exemple acceptable :

```js
window.onNativeEvent({
  type: "push_status",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  notificationsEnabled: false
})
```

Conséquence attendue :
- la WebView sait que le push n'est pas utilisable ;
- elle ne tente pas la synchronisation backend tant que `notificationsEnabled` reste `false`.

## Cas B — `terminalId` prêt avant `fcmToken`
Toléré temporairement, mais non idéal.

Exemple :

```js
window.onNativeEvent({
  type: "push_status",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  notificationsEnabled: true
})
```

Puis plus tard :

```js
window.onNativeEvent({
  type: "push_token_refreshed",
  schemaVersion: 1,
  terminalId: "android-pos-123",
  fcmToken: "fcm_token_v1",
  notificationsEnabled: true
})
```

## Cas C — rechargement de la WebView
À chaque nouveau `webview_ready`, Android doit renvoyer un `push_status` complet.

## Cas D — changement de user POS
Le `terminalId` ne change pas.
Le natif n'a pas besoin d'émettre un événement spécial pour le changement d'utilisateur dans cette spec.
C'est la WebView qui gère ensuite son cycle `register` / `heartbeat` / `unregister` selon l'authentification POS.

---

## Ce que tu ne dois pas faire

- Ne pas appeler directement `/push/register`, `/push/heartbeat` ou `/push/unregister` depuis le natif pour ce contrat.
- Ne pas inventer d'autres noms de propriétés comme `token`, `deviceId`, `enabled`, etc.
- Ne pas envoyer `notificationsEnabled` sous forme de string.
- Ne pas régénérer `terminalId` à chaque lancement.
- Ne pas omettre `terminalId` dans `push_token_refreshed`.
- Ne pas créer un autre nom d'événement pour remplacer `push_status` ou `push_token_refreshed`.

---

## Critères d'acceptation

L'implémentation Android sera considérée correcte si :

- après `webview_ready`, la WebView reçoit bien un `push_status` valide ;
- `push_status` contient les bons champs avec les bons noms ;
- `push_token_refreshed` est émis dès qu'un token FCM change ;
- `terminalId` reste stable entre plusieurs lancements ;
- `terminalId` est identique entre `push_status` et `push_token_refreshed` ;
- `notificationsEnabled` reflète l'état réel Android ;
- la WebView peut enchaîner correctement sa sync backend sans adaptation supplémentaire.

---

## Résumé ultra-court à respecter

Implémente exactement ceci côté Android :

```js
window.onNativeEvent({
  type: "push_status",
  schemaVersion: 1,
  terminalId: "<stable_installation_id>",
  fcmToken: "<current_fcm_token>",
  notificationsEnabled: true
})
```

et lors d'un refresh token :

```js
window.onNativeEvent({
  type: "push_token_refreshed",
  schemaVersion: 1,
  terminalId: "<stable_installation_id>",
  fcmToken: "<new_fcm_token>",
  notificationsEnabled: true
})
```

Aucun autre contrat ne doit être inventé.

