openapi: 3.0.3
info:
  title: Voyage Tickets API
  version: "0.1.0"
  description: >
    Contrat de référence consommé par web/ et mobile/. Toute modification de
    l'API Laravel doit être répercutée ici avant de toucher les clients.

servers:
  - url: https://api.voyage-tickets.example/v1
    description: Production (placeholder)
  - url: http://localhost:8000/api/v1
    description: Local (php artisan serve)

paths:
  /departures:
    get:
      summary: Rechercher des départs disponibles
      description: >
        Endpoint public (pas d'authentification requise). Utilisé par
        l'écran de recherche voyageur dans web/ et mobile/.
      parameters:
        - name: origin
          in: query
          required: true
          schema:
            type: string
          example: "Abidjan"
        - name: destination
          in: query
          required: true
          schema:
            type: string
          example: "Bouake"
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
          example: "2026-07-01"
      responses:
        "200":
          description: Liste des départs correspondant aux critères
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Departure"
              example:
                data:
                  - id: "9c1f2e3a-1111-4a2b-8c3d-000000000001"
                    company_name: "Compagnie Exemple"
                    origin: "Abidjan"
                    destination: "Bouake"
                    departure_at: "2026-07-01T14:00:00Z"
                    price_cents: 500000
                    seats_available: 12
        "422":
          $ref: "#/components/responses/ValidationError"

  /bookings:
    post:
      summary: Créer une réservation (pose un hold sur un siège)
      description: >
        Authentification requise. Décrémente app_seats_available sur le
        Departure ciblé et crée un Booking en statut "pending" avec une
        expiration de 10 minutes (reserved_until).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [departure_id, seats_count]
              properties:
                departure_id:
                  type: string
                  format: uuid
                seats_count:
                  type: integer
                  minimum: 1
                  maximum: 6
            example:
              departure_id: "9c1f2e3a-1111-4a2b-8c3d-000000000001"
              seats_count: 2
      responses:
        "201":
          description: Booking créé en statut "pending"
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Booking"
        "409":
          description: Plus assez de sièges disponibles sur ce départ
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          $ref: "#/components/responses/ValidationError"

  /payments/initiate:
    post:
      summary: Initier le paiement d'un booking via l'agrégateur mobile money
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [booking_id, provider]
              properties:
                booking_id:
                  type: string
                  format: uuid
                provider:
                  type: string
                  enum: [orange_money, wave, mtn_money, moov_money]
      responses:
        "200":
          description: >
            Paiement initié côté agrégateur. Le booking reste "pending"
            jusqu'à confirmation via webhook — ne JAMAIS marquer payé ici.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      payment_id:
                        type: string
                        format: uuid
                      status:
                        type: string
                        enum: [initiated]
                      provider_redirect_url:
                        type: string
                        nullable: true

  /payments/webhook:
    post:
      summary: Callback serveur-à-serveur de l'agrégateur (interne, non consommé par web/mobile)
      description: >
        Vérifie la signature HMAC, applique l'idempotence sur
        provider_tx_id, recalcule le montant côté serveur, puis fait passer
        le Booking en "paid" et génère le Ticket associé.
      responses:
        "200":
          description: Webhook traité (ou ignoré si déjà traité — idempotent)
        "401":
          description: Signature invalide

  /tickets/{id}:
    get:
      summary: Récupérer un ticket (QR + code SMS de secours)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Détail du ticket
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Ticket"
        "404":
          description: Ticket introuvable ou n'appartenant pas à l'utilisateur

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Sanctum token

  responses:
    ValidationError:
      description: Erreur de validation des paramètres
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    Departure:
      type: object
      properties:
        id:
          type: string
          format: uuid
        company_name:
          type: string
        origin:
          type: string
        destination:
          type: string
        departure_at:
          type: string
          format: date-time
        price_cents:
          type: integer
        seats_available:
          type: integer

    Booking:
      type: object
      properties:
        id:
          type: string
          format: uuid
        departure_id:
          type: string
          format: uuid
        seats_count:
          type: integer
        status:
          type: string
          enum: [pending, paid, expired, cancelled]
        reserved_until:
          type: string
          format: date-time
          nullable: true
        total_price_cents:
          type: integer

    Ticket:
      type: object
      properties:
        id:
          type: string
          format: uuid
        booking_id:
          type: string
          format: uuid
        qr_code_url:
          type: string
        sms_backup_code:
          type: string
          example: "K7H2P9"

    Error:
      type: object
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
