# language: fr
Fonctionnalité: API - File d'attente et pré-calcul des contenus
  En tant qu'API backend
  Je veux pré-calculer et gérer la file d'attente de contenus
  Afin d'assurer une navigation fluide sans latence

  Contexte:
    Étant donné que l'API RoadWave est disponible
    Et que Redis est accessible
    Et que PostgreSQL avec PostGIS est accessible
    Et qu'un utilisateur "user123" existe avec token JWT valide

  # Pré-calcul initial

  Scénario: API pré-calcule 5 contenus au démarrage de session
    Étant donné que l'utilisateur "user123" démarre une session
    Et qu'il est situé à Paris (48.8566, 2.3522)
    Et qu'il est en mode voiture (vitesse ≥ 5 km/h)
    Quand je POST /api/v1/queue/initialize
      """json
      {
        "user_id": "user123",
        "latitude": 48.8566,
        "longitude": 2.3522,
        "mode": "voiture"
      }
      """
    Alors le statut de réponse est 201
    Et la réponse contient:
      """json
      {
        "queue_size": 5,
        "contents": [
          {"id": "content1", "title": "...", "position": 1},
          {"id": "content2", "title": "...", "position": 2},
          {"id": "content3", "title": "...", "position": 3},
          {"id": "content4", "title": "...", "position": 4},
          {"id": "content5", "title": "...", "position": 5}
        ]
      }
      """
    Et en Redis, la clé "user:user123:queue" contient 5 contenus
    Et les métadonnées incluent:
      | champ       | valeur   |
      | last_lat    | 48.8566  |
      | last_lon    | 2.3522   |
      | mode        | voiture  |
      | computed_at | (timestamp actuel) |
    Et le TTL est de 900 secondes (15 minutes)

  Scénario: API GET retourne la file d'attente en cache
    Étant donné qu'une file de 5 contenus existe en cache Redis pour "user123"
    Quand je GET /api/v1/queue
    Alors le statut de réponse est 200
    Et la réponse contient les 5 contenus pré-calculés
    Et la latence de réponse est < 50ms (lecture Redis)

  Scénario: API retire un contenu de la file après lecture
    Étant donné qu'une file de 5 contenus [C1, C2, C3, C4, C5] existe pour "user123"
    Quand je POST /api/v1/queue/consume
      """json
      {
        "user_id": "user123",
        "content_id": "C1"
      }
      """
    Alors le statut de réponse est 200
    Et la file d'attente devient [C2, C3, C4, C5]
    Et la taille de la file est 4

  # Recalcul automatique

  Scénario: API recalcule après déplacement >10km
    Étant donné qu'une file a été calculée à Paris (48.8566, 2.3522)
    Et que l'utilisateur se déplace à Versailles (48.8049, 2.1204) soit 12km
    Quand je POST /api/v1/queue/update-location
      """json
      {
        "user_id": "user123",
        "latitude": 48.8049,
        "longitude": 2.1204
      }
      """
    Alors le statut de réponse est 200
    Et la réponse contient:
      """json
      {
        "queue_invalidated": true,
        "reason": "distance_threshold_exceeded",
        "distance_km": 12.1,
        "new_queue_size": 5
      }
      """
    Et la nouvelle file est basée sur la position Versailles
    Et l'ancienne file a été supprimée de Redis

  Scénario: API ne recalcule pas si déplacement ≤10km
    Étant donné qu'une file a été calculée à Paris (48.8566, 2.3522)
    Et que l'utilisateur se déplace de 8 km
    Quand je POST /api/v1/queue/update-location
      """json
      {
        "user_id": "user123",
        "latitude": 48.8500,
        "longitude": 2.3600
      }
      """
    Alors le statut de réponse est 200
    Et la réponse contient:
      """json
      {
        "queue_invalidated": false,
        "distance_km": 8.2,
        "threshold": 10
      }
      """
    Et la file en cache reste inchangée

  Scénario: API recalcule après 10 minutes
    Étant donné qu'une file a été calculée à 10:00:00
    Et que l'heure actuelle est 10:10:01
    Quand je GET /api/v1/queue
    Alors le statut de réponse est 200
    Et la réponse contient:
      """json
      {
        "queue_invalidated": true,
        "reason": "time_threshold_exceeded",
        "elapsed_minutes": 10,
        "new_queue_size": 5
      }
      """
    Et une nouvelle file de 5 contenus est recalculée
    Et le timestamp "computed_at" est mis à jour

  Scénario: API recalcule quand il reste <3 contenus
    Étant donné qu'il reste 3 contenus [C3, C4, C5] dans la file
    Quand je POST /api/v1/queue/consume (consomme C3)
    Alors le statut de réponse est 200
    Et la file devient [C4, C5]
    Et un recalcul asynchrone est déclenché
    Et 3 nouveaux contenus [C6, C7, C8] sont ajoutés
    Et la file finale est [C4, C5, C6, C7, C8]

  # Invalidation immédiate

  Scénario: API invalide après modification préférences utilisateur
    Étant donné qu'une file de 5 contenus existe pour "user123"
    Et que l'utilisateur est en mode piéton (vitesse < 5 km/h)
    Quand je PUT /api/v1/users/user123/preferences
      """json
      {
        "geo_radius": 20,
        "discovery_factor": 0.7,
        "political_content": false
      }
      """
    Alors le statut de réponse est 200
    Et la file d'attente est invalidée immédiatement
    Et une nouvelle file est recalculée avec les nouvelles préférences
    Et en Redis, l'ancienne file a été supprimée

  Scénario: API refuse modification préférences si vitesse >10 km/h
    Étant donné que l'utilisateur roule à 50 km/h
    Quand je PUT /api/v1/users/user123/preferences
      """json
      {
        "geo_radius": 30
      }
      """
    Alors le statut de réponse est 403
    Et la réponse contient:
      """json
      {
        "error": "MODIFICATION_BLOCKED_WHILE_DRIVING",
        "message": "Modification des préférences interdite en conduite (vitesse > 10 km/h)",
        "current_speed_kmh": 50
      }
      """
    Et les préférences ne sont pas modifiées

  Scénario: API invalide après démarrage live d'un créateur suivi
    Étant donné que l'utilisateur "user123" suit le créateur "creator456"
    Et qu'une file de 5 contenus existe
    Et que l'utilisateur est dans la zone du créateur
    Quand le créateur "creator456" démarre un live
    Alors une notification push est envoyée à "user123"
    Et la file d'attente est recalculée
    Et le contenu live est inséré en tête de file
    Et la nouvelle file commence par le live

  # Métadonnées et persistence

  Scénario: API stocke métadonnées complètes en Redis
    Étant donné qu'une file est calculée à 10:30:00
    Quand je consulte Redis avec la clé "user:user123:queue"
    Alors la structure est:
      """json
      {
        "contents": [
          {"id": "C1", "position": 1},
          {"id": "C2", "position": 2},
          {"id": "C3", "position": 3},
          {"id": "C4", "position": 4},
          {"id": "C5", "position": 5}
        ],
        "metadata": {
          "last_lat": 48.8566,
          "last_lon": 2.3522,
          "computed_at": "2026-02-02T10:30:00Z",
          "mode": "voiture"
        }
      }
      """
    Et le TTL est exactement 900 secondes

  Scénario: API calcule distance avec PostGIS
    Étant donné que l'ancienne position est Paris (48.8566, 2.3522)
    Et que la nouvelle position est Versailles (48.8049, 2.1204)
    Quand l'API calcule la distance
    Alors la requête SQL utilise:
      """sql
      SELECT ST_Distance(
        ST_MakePoint(2.3522, 48.8566)::geography,
        ST_MakePoint(2.1204, 48.8049)::geography
      ) / 1000 AS distance_km
      """
    Et le résultat est 12.1 km

  # Gestion erreurs

  Scénario: API gère échec Redis gracieusement
    Étant donné que Redis est indisponible
    Quand je GET /api/v1/queue
    Alors le statut de réponse est 503
    Et la réponse contient:
      """json
      {
        "error": "CACHE_UNAVAILABLE",
        "message": "Service de cache temporairement indisponible",
        "fallback": "Calcul direct sans cache"
      }
      """
    Et une nouvelle file est calculée directement depuis PostgreSQL

  Scénario: API gère aucun contenu disponible
    Étant donné qu'aucun contenu n'existe dans la zone de l'utilisateur
    Quand je POST /api/v1/queue/initialize
    Alors le statut de réponse est 200
    Et la réponse contient:
      """json
      {
        "queue_size": 0,
        "contents": [],
        "message": "Aucun contenu disponible dans cette zone",
        "suggested_action": "expand_radius"
      }
      """

  Scénario: API élargit la zone de recherche si aucun contenu
    Étant donné qu'aucun contenu n'existe dans un rayon de 20km
    Quand je POST /api/v1/queue/expand-radius
      """json
      {
        "user_id": "user123",
        "additional_radius_km": 50
      }
      """
    Alors le statut de réponse est 200
    Et la recherche utilise un rayon de 70km (20 + 50)
    Et une nouvelle file est calculée avec ce rayon

  # Performance

  Scénario: API répond en <100ms pour lecture cache
    Étant donné qu'une file existe en Redis
    Quand je GET /api/v1/queue à 10:30:00.000
    Alors la réponse est reçue à 10:30:00.050 (50ms)
    Et la latence est < 100ms

  Scénario: API recalcule en arrière-plan sans bloquer
    Étant donné qu'il reste 2 contenus dans la file
    Quand je POST /api/v1/queue/consume
    Alors le statut de réponse est 200 (immédiat)
    Et la réponse est retournée en < 100ms
    Et le recalcul asynchrone démarre en parallèle
    Et le client ne perçoit aucune latence

  Plan du Scénario: Conditions de recalcul selon distance
    Étant donné qu'une file a été calculée à une position donnée
    Quand l'utilisateur se déplace de <distance> km
    Alors la file est <action>

    Exemples:
      | distance | action                      |
      | 5        | conservée                   |
      | 9.9      | conservée                   |
      | 10.0     | conservée                   |
      | 10.1     | invalidée et recalculée     |
      | 15       | invalidée et recalculée     |
      | 50       | invalidée et recalculée     |

  Plan du Scénario: Conditions de recalcul selon temps écoulé
    Étant donné qu'une file a été calculée il y a <temps> minutes
    Quand je consulte la file
    Alors la file est <action>

    Exemples:
      | temps | action                      |
      | 5     | conservée                   |
      | 9     | conservée                   |
      | 10    | invalidée et recalculée     |
      | 15    | invalidée et recalculée     |
      | 60    | invalidée et recalculée     |
