# language: fr

Fonctionnalité: API - Publicités dans audio-guides
  En tant que système backend
  Je veux gérer l'insertion et la diffusion de publicités dans les audio-guides
  Afin de monétiser les contenus gratuits

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

  # 16.5.1 - Insertion publicité

  Scénario: Calcul insertion publicité (1 pub toutes les 5 séquences)
    Étant donné un audio-guide gratuit avec 12 séquences
    Et que la fréquence pub est configurée à "1/5"
    Quand je fais un GET sur "/api/v1/audio-guides/ag_123/ad-schedule"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "ad_frequency": "1/5",
        "ad_insertions": [
          {"after_sequence": 5, "position": "after"},
          {"after_sequence": 10, "position": "after"}
        ],
        "total_ads": 2
      }
      """

  Plan du Scénario: Fréquence publicité configurable admin
    Étant donné que la fréquence pub est configurée à <frequence>
    Et un audio-guide avec 12 séquences
    Quand les insertions pub sont calculées
    Alors le nombre de pubs insérées est <nombre_pubs>

    Exemples:
      | frequence | nombre_pubs |
      | 1/3       | 4           |
      | 1/5       | 2           |
      | 1/10      | 1           |

  Scénario: Utilisateur Premium - Aucune publicité
    Étant donné un utilisateur Premium
    Et un audio-guide gratuit
    Quand je fais un GET sur "/api/v1/audio-guides/ag_123/ad-schedule"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "ad_frequency": "0",
        "ad_insertions": [],
        "total_ads": 0,
        "reason": "premium_user"
      }
      """

  Scénario: POST /api/v1/audio-guides/playback/next-ad - Récupération pub suivante
    Étant donné qu'un utilisateur termine la séquence 5
    Et qu'une pub doit être insérée
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/playback/next-ad":
      """json
      {
        "sequence_completed": 5,
        "user_position": {
          "latitude": 43.1234,
          "longitude": 2.5678
        },
        "mode": "voiture"
      }
      """
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "should_play_ad": true,
        "ad": {
          "ad_id": "ad_789",
          "audio_url": "https://cdn.roadwave.fr/ads/ad_789.m4a",
          "duration_seconds": 30,
          "skippable_after": 5,
          "advertiser": "Brand X"
        }
      }
      """

  Scénario: Sélection pub géolocalisée
    Étant donné que l'utilisateur est en Île-de-France (43.1234, 2.5678)
    Et que des publicités géolocalisées existent pour cette région
    Quand la pub suivante est sélectionnée
    Alors l'API filtre les pubs par:
      | critère         | valeur              |
      | Géolocalisation | Île-de-France       |
      | Catégorie       | Tourisme, Culture   |
      | Langue          | Français            |
      | Budget actif    | true                |
    Et une pub correspondante est retournée

  Scénario: Fallback pub nationale si pas de pub locale
    Étant donné que l'utilisateur est dans une région sans pubs locales
    Quand la pub suivante est sélectionnée
    Alors l'API sélectionne une pub nationale (France entière)
    Et la pub est retournée normalement

  Scénario: Pas de pub si séquence non multiple de 5
    Étant donné qu'un utilisateur termine la séquence 4
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/playback/next-ad"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "should_play_ad": false,
        "reason": "not_ad_sequence"
      }
      """

  # Comportement selon mode

  Scénario: Pub en mode piéton (auto-play puis pause)
    Étant donné un audio-guide en mode piéton
    Et qu'une pub doit être insérée après séquence 5
    Quand la pub est récupérée
    Alors le mode_behavior retourné est:
      """json
      {
        "auto_play": true,
        "pause_after": true,
        "reason": "pedestrian_mode"
      }
      """

  Scénario: Pub en mode voiture/vélo/transport (auto-play puis séquence suivante)
    Étant donné un audio-guide en mode voiture
    Et qu'une pub doit être insérée
    Quand la pub est récupérée
    Alors le mode_behavior retourné est:
      """json
      {
        "auto_play": true,
        "pause_after": false,
        "continue_to_next": true,
        "reason": "vehicle_mode"
      }
      """

  # 16.5.2 - Métriques et tracking

  Scénario: POST /api/v1/ads/{ad_id}/impressions - Enregistrement impression
    Étant donné qu'une pub "ad_789" démarre
    Quand je fais un POST sur "/api/v1/ads/ad_789/impressions":
      """json
      {
        "audio_guide_id": "ag_123",
        "sequence_after": 5,
        "user_id": "user_456",
        "timestamp": "2026-01-22T14:35:00Z"
      }
      """
    Alors le code HTTP de réponse est 201
    Et l'impression est enregistrée dans ad_impressions
    Et le compteur impressions_count est incrémenté

  Scénario: POST /api/v1/ads/{ad_id}/completions - Enregistrement écoute complète
    Étant donné qu'une pub de 30 secondes est écoutée à 25 secondes (83%)
    Quand je fais un POST sur "/api/v1/ads/ad_789/completions":
      """json
      {
        "audio_guide_id": "ag_123",
        "listened_seconds": 25,
        "total_duration": 30,
        "completion_rate": 83
      }
      """
    Alors le code HTTP de réponse est 201
    Et l'écoute complète est enregistrée (>80%)
    Et le créateur de l'audio-guide reçoit 0.003€ de revenus

  Scénario: POST /api/v1/ads/{ad_id}/skips - Enregistrement skip
    Étant donné qu'une pub est skippée après 6 secondes
    Quand je fais un POST sur "/api/v1/ads/ad_789/skips":
      """json
      {
        "audio_guide_id": "ag_123",
        "skipped_at_second": 6
      }
      """
    Alors le code HTTP de réponse est 201
    Et le skip est enregistré dans ad_skips
    Et le taux de skip est mis à jour

  Scénario: Validation écoute complète (>80%)
    Étant donné qu'une pub de 30 secondes est écoutée 20 secondes (66%)
    Quand je fais un POST sur "/api/v1/ads/ad_789/completions"
    Alors le code HTTP de réponse est 400
    Et le message d'erreur est "completion_rate: minimum 80% requis pour écoute complète"
    Et aucun revenu n'est attribué

  # Métriques créateur

  Scénario: GET /api/v1/creators/me/audio-guides/{id}/ad-metrics - Métriques pub
    Étant donné un audio-guide "ag_123" avec publicités
    Et les métriques suivantes sur 30 jours:
      | impressions | ecoutes_completes | skips | revenus |
      | 1000        | 650               | 350   | 1.95€   |
    Quand je fais un GET sur "/api/v1/creators/me/audio-guides/ag_123/ad-metrics"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "period": "30_days",
        "impressions": 1000,
        "completions": 650,
        "skips": 350,
        "completion_rate": 65,
        "revenue": 1.95,
        "cpm": 1.95
      }
      """

  Scénario: Distinction revenus contenus classiques vs audio-guides
    Étant donné un créateur avec contenus classiques et audio-guides
    Quand je fais un GET sur "/api/v1/creators/me/revenue-breakdown"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "total_revenue": 85.40,
        "breakdown": {
          "classic_content": {
            "revenue": 45.20,
            "impressions": 15000
          },
          "audio_guides": {
            "revenue": 40.20,
            "impressions": 13000
          }
        }
      }
      """

  # Répartition revenus

  Scénario: Calcul revenus créateur (3€ / 1000 écoutes complètes)
    Étant donné un audio-guide avec 1000 écoutes complètes pub ce mois
    Quand les revenus sont calculés
    Alors le créateur reçoit 3€
    Et le revenu par écoute complète est 0.003€

  Scénario: POST /api/v1/ads/revenue/process - Calcul revenus batch mensuel
    Étant donné le 1er du mois
    Et que 500 créateurs ont des revenus pub à calculer
    Quand le job batch s'exécute
    Alors pour chaque créateur:
      | creator_id | ecoutes_completes | revenus |
      | creator_1  | 5000              | 15.00€  |
      | creator_2  | 2000              | 6.00€   |
      | creator_3  | 1200              | 3.60€   |
    Et les revenus sont ajoutés au solde creator_balance

  # Normalisation audio

  Scénario: Validation volume pub (-14 LUFS)
    Étant donné qu'une pub est uploadée avec volume -10 LUFS
    Quand la pub est validée
    Alors un processus de normalisation est déclenché
    Et le volume est ajusté à -14 LUFS (standard RoadWave)
    Et la pub normalisée est stockée sur le CDN

  Scénario: Validation durée pub (max 60 secondes)
    Étant donné qu'une pub de 75 secondes est uploadée
    Quand la validation est effectuée
    Alors le code HTTP de réponse est 400
    Et le message d'erreur est "duration: maximum 60 secondes pour une publicité"

  # Cas d'erreur

  Scénario: Aucune pub disponible (stock épuisé)
    Étant donné qu'aucune campagne pub n'est active dans la région
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/playback/next-ad"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "should_play_ad": false,
        "reason": "no_ads_available"
      }
      """
    Et aucune pub n'est insérée (séquence suivante démarre directement)

  Scénario: Budget campagne épuisé
    Étant donné qu'une campagne pub a un budget de 1000€
    Et que le budget est épuisé
    Quand la pub est sélectionnée
    Alors cette campagne est exclue de la sélection
    Et une autre campagne active est choisie

  Scénario: Pub corrompue ou indisponible
    Étant donné qu'une pub sélectionnée a un fichier audio corrompu
    Quand le client tente de la charger
    Alors une pub de fallback (backup) est retournée
    Et un log d'erreur est créé pour investigation

  # Filtrage et ciblage

  Scénario: Ciblage par catégorie audio-guide
    Étant donné un audio-guide tagué "tourisme", "culture", "musée"
    Et une campagne pub ciblée "tourisme + culture"
    Quand la pub est sélectionnée
    Alors cette campagne a une priorité élevée (matching tags)
    Et elle est préférée aux pubs génériques

  Scénario: Filtrage par classification âge
    Étant donné un audio-guide classifié "tout_public"
    Et une campagne pub classifiée "18+"
    Quand la pub est sélectionnée
    Alors cette campagne est exclue
    Et seules les pubs "tout_public" sont éligibles

  Scénario: Limite fréquence pub par utilisateur (cap frequency)
    Étant donné qu'un utilisateur a déjà entendu la pub "ad_789" 3 fois ce jour
    Et que le cap frequency est configuré à 3/jour
    Quand la pub est sélectionnée
    Alors "ad_789" est exclue
    Et une autre pub est choisie

  Scénario: GET /api/v1/audio-guides/{id}/ad-policy - Politique pub
    Étant donné un audio-guide
    Quand je fais un GET sur "/api/v1/audio-guides/ag_123/ad-policy"
    Alors le code HTTP de réponse est 200
    Et le corps de réponse contient:
      """json
      {
        "has_ads": true,
        "frequency": "1/5",
        "skippable_after_seconds": 5,
        "average_ad_duration": 30,
        "revenue_share": {
          "creator": "100%",
          "platform": "0%"
        }
      }
      """

  # Performance

  Scénario: Cache Redis pour pubs actives
    Étant donné que les campagnes actives sont en cache Redis
    Quand je fais un POST sur "/api/v1/audio-guides/ag_123/playback/next-ad"
    Alors les pubs sont récupérées depuis Redis (pas PostgreSQL)
    Et le temps de réponse est < 30ms

  Scénario: Pre-fetching pub suivante (client)
    Étant donné que l'utilisateur est à la séquence 3
    Et qu'une pub sera insérée après la séquence 5
    Quand le client détecte l'approche de la séquence 5
    Alors il peut pré-charger la pub via GET "/api/v1/audio-guides/ag_123/ad-prefetch?after_sequence=5"
    Et la transition sera instantanée
