# language: fr

Fonctionnalité: API - Déclenchement GPS et géolocalisation audio-guides
  En tant que système backend
  Je veux calculer les déclenchements GPS et distances pour audio-guides
  Afin de permettre une expérience automatique en mode voiture/vélo/transport

  Contexte:
    Étant donné que l'API RoadWave est démarrée
    Et que l'utilisateur "user@example.com" est authentifié

  # 16.3.1 - Calcul de proximité et déclenchement

  Scénario: POST /api/v1/audio-guides/{id}/check-proximity - Vérification proximité
    Étant donné un audio-guide voiture avec 8 séquences
    Et que l'utilisateur est à la position (43.1233, 2.5677)
    Et que le prochain point GPS (séquence 2) est à (43.1245, 2.5690) avec rayon 30m
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity":
      """json
      {
        "user_position": {
          "latitude": 43.1233,
          "longitude": 2.5677
        },
        "current_sequence": 1
      }
      """
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "in_trigger_zone": false,
        "next_sequence_id": "seq_2",
        "distance_to_next": 145.3,
        "eta_seconds": 18,
        "direction_degrees": 45,
        "should_trigger": false
      }
      """

  Scénario: Déclenchement automatique dans rayon 30m (voiture)
    Étant donné un audio-guide voiture
    Et que l'utilisateur entre à 25m du point GPS suivant
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity"
    Alors le code HTTP de réponse est 200
    Et should_trigger est true
    Et in_trigger_zone est true
    Et le message "Séquence déclenchée automatiquement" est retourné

  Plan du Scénario: Rayon de déclenchement selon mode
    Étant donné un audio-guide en mode <mode>
    Et un point GPS avec rayon par défaut
    Quand l'utilisateur entre à <distance> du point
    Alors should_trigger est <trigger>

    Exemples:
      | mode      | distance | trigger |
      | voiture   | 25m      | true    |
      | voiture   | 35m      | false   |
      | velo      | 45m      | true    |
      | velo      | 55m      | false   |
      | transport | 95m      | true    |
      | transport | 105m     | false   |

  # Calcul distance avec PostGIS

  Scénario: Calcul distance avec ST_Distance (geography)
    Étant donné deux points GPS:
      | point        | latitude | longitude |
      | Position user| 43.1234  | 2.5678    |
      | Point séq. 2 | 43.1245  | 2.5690    |
    Quand le calcul PostGIS ST_Distance est effectué
    Alors la distance retournée est 145.3 mètres
    Et le calcul utilise le type geography (WGS84)
    Et la précision est au mètre près

  Scénario: Calcul ETA basé sur vitesse actuelle
    Étant donné que l'utilisateur est à 320m du prochain point
    Et que sa vitesse actuelle est 28 km/h
    Quand l'ETA est calculé
    Alors l'ETA retourné est 41 secondes
    Et la formule appliquée est: (distance_m / 1000) / (vitesse_kmh) * 3600

  Scénario: ETA non calculé si vitesse < 5 km/h
    Étant donné que l'utilisateur est à 200m du prochain point
    Et que sa vitesse actuelle est 2 km/h (arrêté)
    Quand l'ETA est calculé
    Alors l'ETA retourné est null
    Et le message "En attente de déplacement" est inclus

  Scénario: Calcul direction (bearing) avec PostGIS
    Étant donné la position utilisateur (43.1234, 2.5678)
    Et le prochain point (43.1245, 2.5690) au nord-est
    Quand le bearing est calculé avec ST_Azimuth
    Alors l'angle retourné est 45° (nord-est)
    Et la flèche correspondante est "↗"

  Plan du Scénario: Conversion angle en flèche (8 directions)
    Étant donné un angle de <degrees>°
    Quand la flèche est calculée
    Alors la direction retournée est "<arrow>"

    Exemples:
      | degrees | arrow |
      | 0       | ↑     |
      | 45      | ↗     |
      | 90      | →     |
      | 135     | ↘     |
      | 180     | ↓     |
      | 225     | ↙     |
      | 270     | ←     |
      | 315     | ↖     |

  # 16.3.3 - Gestion point manqué

  Scénario: Détection point manqué (hors rayon mais dans tolérance)
    Étant donné un audio-guide voiture
    Et un point GPS avec rayon 30m et tolérance 100m
    Et que l'utilisateur passe à 65m du point
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "in_trigger_zone": false,
        "missed_point": true,
        "distance_to_point": 65,
        "tolerance_zone": true,
        "actions_available": ["listen_anyway", "skip", "navigate_back"]
      }
      """

  Scénario: Point manqué au-delà tolérance (>100m en voiture)
    Étant donné un audio-guide voiture
    Et que l'utilisateur passe à 150m du point GPS
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity"
    Alors missed_point est false
    Et tolerance_zone est false
    Et aucune popup "point manqué" n'est déclenchée

  Plan du Scénario: Rayon tolérance selon mode
    Étant donné un audio-guide en mode <mode>
    Et que l'utilisateur passe à <distance> du point
    Alors tolerance_zone est <in_tolerance>

    Exemples:
      | mode      | distance | in_tolerance |
      | voiture   | 60m      | true         |
      | voiture   | 110m     | false        |
      | velo      | 70m      | true         |
      | velo      | 80m      | false        |
      | transport | 120m     | true         |
      | transport | 160m     | false        |

  # Progress bar dynamique

  Scénario: Calcul progress bar vers prochain point
    Étant donné que la distance initiale vers le prochain point était 500m
    Et que l'utilisateur est maintenant à 175m du point
    Quand le pourcentage de progression est calculé
    Alors le progress_percentage retourné est 65%
    Et la formule est: 100 - (distance_actuelle / distance_initiale * 100)

  # Gestion trajectoire et itinéraire

  Scénario: Calcul distance totale parcours
    Étant donné un audio-guide avec les points GPS suivants:
      | sequence | latitude | longitude |
      | 1        | 43.1234  | 2.5678    |
      | 2        | 43.1245  | 2.5690    |
      | 3        | 43.1250  | 2.5700    |
    Quand je fais un GET sur "/api/v1/audio-guides/ag_123/route-stats"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "total_distance": 350,
        "distances_between_points": [
          {"from": 1, "to": 2, "distance": 150},
          {"from": 2, "to": 3, "distance": 200}
        ]
      }
      """

  Scénario: Vérification cohérence itinéraire (alerte distance excessive)
    Étant donné un audio-guide en mode "pieton"
    Et deux points GPS distants de 465 km (Paris - Lyon)
    Quand la cohérence est vérifiée
    Alors un warning est retourné:
      """json
      {
        "warning": "distance_excessive",
        "message": "Distance de 465 km entre séquences 2 et 3. Mode 'pieton' inapproprié.",
        "suggested_mode": "voiture"
      }
      """

  # Distinction audio-guides vs contenus géolocalisés simples

  Scénario: Pas de notification 7s avant pour audio-guides multi-séquences
    Étant donné un audio-guide multi-séquences en mode voiture
    Et que l'utilisateur approche du prochain point GPS
    Quand la distance et ETA sont calculés
    Alors aucun décompte "7→1" n'est déclenché
    Et le déclenchement se fait au point GPS exact (rayon 30m)
    Et une notification "Ding" + toast 2s est envoyée

  Scénario: Notification 7s avant pour contenus géolocalisés simples (1 séquence)
    Étant donné un contenu géolocalisé simple (1 séquence unique)
    Et que l'utilisateur approche du point GPS
    Quand l'ETA devient 7 secondes
    Alors une notification avec compteur "7→1" est déclenchée
    Et l'utilisateur doit valider avec bouton "Suivant"

  # Exception quota pour audio-guides multi-séquences

  Scénario: Audio-guide multi-séquences compte 1 seul contenu dans quota horaire
    Étant donné un audio-guide "Visite Safari" avec 12 séquences
    Et que l'utilisateur a un quota de 0/6 contenus géolocalisés
    Quand l'utilisateur démarre l'audio-guide (séquence 1)
    Alors le quota passe à 1/6
    Quand l'utilisateur écoute les 12 séquences complètes
    Alors le quota reste à 1/6
    Et toutes les séquences ne consomment PAS 12 quotas
    Et l'audio-guide entier compte comme 1 seul contenu

  Scénario: Contenus géolocalisés simples consomment 1 quota chacun
    Étant donné que l'utilisateur a un quota de 0/6
    Quand l'utilisateur accepte un contenu géolocalisé simple "Tour Eiffel"
    Alors le quota passe à 1/6
    Quand l'utilisateur accepte un contenu géolocalisé simple "Arc de Triomphe"
    Alors le quota passe à 2/6
    Quand l'utilisateur accepte un contenu géolocalisé simple "Louvre"
    Alors le quota passe à 3/6
    Et chaque contenu simple consomme 1 quota

  Scénario: Mixte audio-guides + contenus simples respecte quota 6/h
    Étant donné que l'utilisateur a un quota de 0/6
    Quand l'utilisateur démarre un audio-guide 8 séquences "Safari"
    Alors le quota passe à 1/6
    Quand l'utilisateur accepte 5 contenus géolocalisés simples
    Alors le quota passe à 6/6
    Et le quota horaire est atteint
    Quand un 7ème contenu est détecté
    Alors aucune notification n'est envoyée (quota atteint)

  # Cache et optimisations

  Scénario: Cache Redis pour calculs GPS fréquents
    Étant donné que les points GPS d'un audio-guide sont en cache Redis
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/check-proximity"
    Alors les points GPS sont récupérés depuis Redis (pas PostgreSQL)
    Et le temps de réponse est < 50ms

  Scénario: Geospatial GEORADIUS Redis pour recherche proximité
    Étant donné que tous les audio-guides sont indexés dans Redis (GEOADD)
    Et une position utilisateur (43.1234, 2.5678)
    Quand je recherche les audio-guides dans un rayon de 5 km
    Alors Redis GEORADIUS retourne les audio-guides proches
    Et le temps de réponse est < 20ms

  # Mise à jour position temps réel

  Scénario: WebSocket pour mise à jour position en temps réel
    Étant donné une connexion WebSocket active pour l'audio-guide
    Quand l'utilisateur envoie sa nouvelle position via WS
    Alors le serveur calcule immédiatement la proximité
    Et retourne distance + ETA via WS (pas de polling HTTP)

  Scénario: Throttling position updates (max 1/seconde)
    Étant donné que le client envoie des positions GPS toutes les 200ms
    Quand le serveur reçoit les mises à jour
    Alors seules les positions espacées de >1 seconde sont traitées
    Et les autres sont ignorées (throttling)

  # Cas d'erreur

  Scénario: Position GPS invalide (coordonnées hors limites)
    Étant donné une position avec latitude 95.0000 (invalide)
    Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity"
    Alors le code HTTP de réponse est 400
    Et le message d'erreur est "latitude: doit être entre -90 et 90"

  Scénario: Audio-guide sans points GPS (mode piéton)
    Étant donné un audio-guide en mode piéton sans points GPS
    Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity"
    Alors le code HTTP de réponse est 400
    Et le message d'erreur est "Audio-guide en mode manuel, pas de déclenchement GPS"

  Scénario: Séquence déjà complétée (skip calcul si utilisateur a déjà passé)
    Étant donné que l'utilisateur est à la séquence 5
    Et qu'il vérifie la proximité du point 3 (déjà écouté)
    Quand je fais un POST sur "/api/v1/audio-guides/{id}/check-proximity"
    Alors le calcul n'est pas effectué pour les séquences passées
    Et le message "Séquence déjà écoutée" est retourné

  Scénario: Précision GPS insuffisante
    Étant donné une position avec accuracy ±150m
    Et un rayon de déclenchement de 30m
    Quand la précision est vérifiée
    Alors un warning est retourné:
      """json
      {
        "warning": "low_gps_accuracy",
        "message": "Précision GPS insuffisante (±150m). Déclenchement automatique peut être perturbé.",
        "accuracy": 150,
        "trigger_radius": 30
      }
      """

  # Performance

  Scénario: Optimisation requêtes PostGIS avec index spatial
    Étant donné que les points GPS ont un index GIST (PostGIS)
    Quand une requête ST_DWithin est exécutée
    Alors l'index spatial est utilisé
    Et le temps d'exécution est < 10ms

  Scénario: Batch proximity check pour tous les points
    Étant donné un audio-guide avec 20 séquences
    Quand je fais un POST sur "/api/v1/audio-guides/{id}/batch-proximity":
      """json
      {
        "user_position": {"latitude": 43.1234, "longitude": 2.5678}
      }
      """
    Alors toutes les distances sont calculées en une seule requête PostGIS
    Et le corps de réponse contient:
      """json
      {
        "sequences": [
          {"sequence_id": "seq_1", "distance": 0, "in_zone": true},
          {"sequence_id": "seq_2", "distance": 150, "in_zone": false},
          {"sequence_id": "seq_3", "distance": 350, "in_zone": false}
        ],
        "current_sequence": 1,
        "next_sequence": 2
      }
      """
