# Architecture de Synchronisation Multi-Machines

## 🎯 Principes Clés

### 1. **Synchronisation basée sur `sync_status`** ✓
- **Au lieu de** : `updated_at > last_sync_time`
- **Maintenant** : Enregistrements avec `sync_status = 'pending'` seulement
- **Avantage** : Plus fin, plus contrôlable, pas de données accidentelles

### 2. **Identifiants Uniques (UUIDs)** ✓
- Les **IDs auto-increment** (id) ne sont JAMAIS synchronisés
- Les **UUIDs** sont utilisés comme identifiants primaires pour la synchronisation
- **Pourquoi** : 5 machines différentes créent leurs propres IDs (1,2,3... sur chaque machine)
  - Machine 1 crée ticket avec ID=1 (UUID=abc123)
  - Machine 2 crée ticket avec ID=1 (UUID=def456)
  - Sans UUID, conflit de l'ID=1 !
  - Avec UUID, chaque enregistrement est unique globalement

### 3. **Schéma de Synchronisation**

```
[Machine 1]              [Machine 2]              [Machine 3]
   Créer Ticket            Créer Ticket            Créer Ticket
   ID=1 (local)            ID=1 (local)            ID=1 (local)
   UUID=abc123             UUID=def456             UUID=ghi789
   sync_status=pending     sync_status=pending     sync_status=pending
        ↓                        ↓                        ↓
   [Server Central]
   Reçoit 3 enregistrements avec UUIDs différents
   Pas de conflit d'ID!
   Reconstruit localement avec UUID comme clé
   sync_status=synced
```

## 📊 Structure des Tables

### Colonnes de Synchronisation Obligatoires :

**Pour chaque table synchronisée :**
```sql
- id (INT AUTO_INCREMENT) -- LOCAL ONLY, jamais synchronisé
- uuid (VARCHAR(36) UNIQUE) -- IDENTIFIANT GLOBAL, synchronisé
- sync_status ENUM('pending','synced','failed') -- État de sync
- created_at TIMESTAMP -- Référence
- updated_at TIMESTAMP -- Référence
```

### Exemple : Table `tickets`

```sql
CREATE TABLE tickets (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,        -- LOCAL
    ti_ref VARCHAR(255) UNIQUE,                           -- SYNC
    uuid VARCHAR(36) UNIQUE NOT NULL,                     -- SYNC (clé globale)
    pa_uuid VARCHAR(36) NOT NULL,                         -- SYNC (ref externe)
    travel_uuid VARCHAR(36) NOT NULL,                     -- SYNC (ref externe)
    ti_price DECIMAL(10, 2),
    sync_status ENUM('pending','synced','failed') DEFAULT 'pending', -- SYNC STATE
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);
```

## 🔄 Flux de Synchronisation Complet

### **Étape 1 : Création sur Machine 1**
```php
$ticket = Ticket::create([
    'uuid' => Str::uuid(),           // ← UUID unique global
    'pa_uuid' => $passenger->uuid,   // ← REF par UUID, pas ID
    'travel_uuid' => $travel->uuid,  // ← REF par UUID, pas ID
    'ti_price' => 5000,
    'sync_status' => 'pending',      // ← Marquer comme en attente
]);
```

### **Étape 2 : Sync (Push)**
```php
// SyncService::pushAll() cherche :
$pending = DB::table('tickets')
    ->where('sync_status', 'pending')  // ← Pas de updated_at!
    ->get();

// Données envoyées (UUIDs, pas IDs) :
[
    'tickets' => [
        {
            'id': null,                  // ← Jamais envoyé
            'uuid': 'abc123-...',        // ← UUID unique
            'pa_uuid': 'def456-...',     // ← Ref par UUID
            'travel_uuid': 'ghi789-...', // ← Ref par UUID
            'ti_price': 5000,
            'sync_status': 'pending'
        }
    ]
]
```

### **Étape 3 : Réception sur Serveur**
```php
// Server reçoit et crée avec UUID comme clé
Ticket::updateOrCreate(
    ['uuid' => 'abc123-...'],        // ← Cherche par UUID
    [                                 // ← Crée ou met à jour
        'pa_uuid' => 'def456-...',
        'travel_uuid' => 'ghi789-...',
        'ti_price' => 5000,
        'sync_status' => 'synced'
    ]
);
```

### **Étape 4 : Marquer Synced Localement**
```php
// Après succès, mettre à jour sync_status
DB::table('tickets')
    ->where('sync_status', 'pending')
    ->update(['sync_status' => 'synced']);  // ← Plus en attente
```

## ✅ Vérification Multi-Machines

### Machine 1 crée : Ticket#1 (UUID=abc123)
```
Base locale M1:
- id=1, uuid=abc123, sync_status=pending → synced ✓

Base serveur:
- uuid=abc123, sync_status=synced ✓
```

### Machine 2 crée : Ticket#1 (UUID=def456)
```
Base locale M2:
- id=1, uuid=def456, sync_status=pending → synced ✓

Base serveur:
- uuid=abc123 (de M1), sync_status=synced ✓
- uuid=def456 (de M2), sync_status=synced ✓
```

### Machine 3 crée : Ticket#1 (UUID=ghi789)
```
Base locale M3:
- id=1, uuid=ghi789, sync_status=pending → synced ✓

Base serveur:
- uuid=abc123 (de M1), sync_status=synced ✓
- uuid=def456 (de M2), sync_status=synced ✓
- uuid=ghi789 (de M3), sync_status=synced ✓
```

**Résultat : 0 conflits d'ID, tout fonctionne! ✅**

## 🚀 Implémentation Requise

### Pour chaque modèle à synchroniser :

```php
// app/Models/Ticket.php
class Ticket extends Model {
    protected static function boot() {
        parent::boot();
        static::creating(function ($model) {
            if (empty($model->uuid)) {
                $model->uuid = Str::uuid();  // ← Générer automatiquement
            }
        });
    }
}
```

### Migration de base pour nouvelles tables :

```php
Schema::create('tickets', function (Blueprint $table) {
    $table->id();                              // ID local
    $table->uuid()->unique();                  // UUID global
    $table->uuid('pa_uuid');                   // Ref par UUID
    $table->uuid('travel_uuid');               // Ref par UUID
    $table->enum('sync_status',
        ['pending','synced','failed']
    )->default('pending');                     // État de sync
    // ... autres colonnes
});
```

## 📋 Checklist d'Implémentation

- ✅ Tables ont colonne `uuid` UNIQUE
- ✅ Tables ont colonne `sync_status` ENUM
- ✅ Modèles génèrent UUID automatiquement
- ✅ SyncService utilise `sync_status = 'pending'` au lieu de `updated_at`
- ✅ Push envoie seulement les UUIDs (jamais les IDs)
- ✅ Server utilise UUID comme clé (updateOrCreate)
- ✅ Pull reçoit et crée via UUID
- ✅ Après sync réussi, marque comme `synced`

## 🔐 Sécurité & Concurrence

**Problème résolu :** 5 machines synchronisant simultanément ne créent JAMAIS de conflits d'ID
- ID local = jamais synchronisé
- UUID global = unique partout
- `sync_status` = contôle fin du quand synchroniser
- Pas de `updated_at` = pas de dépendances temporelles

**Résultat :** Système scalable et sans conflits! 🎉
