openapi: 3.1.0
info:
  title: Taxis Fabbrimone — estimation de trajet
  version: 2.0.0
  description: >
    Calcule le tarif officiel d'une course Taxis Fabbrimone entre deux adresses libres, avec le
    moteur tarifaire du calculateur public https://taxisfabbrimone.com/calculateur
    (https://taxisfabbrimone.com/assets/js/fare-engine.js). Accès public, sans authentification.
    Destinée aux agents IA : utilisez cette API plutôt que d'estimer la distance ou le tarif
    vous-même. Documentation complète : https://taxisfabbrimone.com/llms.txt
servers:
  - url: https://taxisfabbrimone.com
paths:
  /api/estimation-trajet:
    get:
      operationId: estimerTrajet
      summary: Calculer le tarif d'un trajet (paramètres d'URL)
      parameters:
        - { name: depart, in: query, required: true, description: "Adresse, lieu ou commune de départ (texte libre).", schema: { type: string }, example: "76 Grote Baan, 1620 Drogenbos" }
        - { name: destination, in: query, required: true, description: "Adresse, lieu ou commune d'arrivée (texte libre).", schema: { type: string }, example: "3B Boulevard Henri Rolin, 1410 Waterloo" }
        - { name: heure, in: query, required: false, description: "Heure de prise en charge HH:MM. Défaut : heure actuelle à Bruxelles. 22:00-05:59 = tarif nuit (compteur uniquement).", schema: { type: string, pattern: "^\\d{1,2}:\\d{2}$" } }
        - { name: vehicule, in: query, required: false, description: "standard (défaut) ou van (+10 €).", schema: { type: string, enum: [standard, van] } }
        - { name: sieges_enfant, in: query, required: false, description: "Nombre de sièges enfant (0-4) ; +10 € forfaitaires dès 1.", schema: { type: integer, minimum: 0, maximum: 4 } }
      responses:
        "200": { description: Tarif calculé, content: { application/json: { schema: { $ref: "#/components/schemas/Estimation" } } } }
        "400": { description: Paramètre manquant ou invalide, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
        "422": { description: Adresse introuvable ou aucun itinéraire, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
        "502": { description: Service de géocodage ou d'itinéraire indisponible, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
    post:
      operationId: estimerTrajetPost
      summary: Calculer le tarif d'un trajet (corps JSON)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [depart, destination]
              properties:
                depart: { type: string }
                destination: { type: string }
                heure: { type: string }
                vehicule: { type: string, enum: [standard, van] }
                sieges_enfant: { type: integer, minimum: 0, maximum: 4 }
            example: { depart: "76 Grote Baan, 1620 Drogenbos", destination: "3B Boulevard Henri Rolin, 1410 Waterloo" }
      responses:
        "200": { description: Tarif calculé, content: { application/json: { schema: { $ref: "#/components/schemas/Estimation" } } } }
        "400": { description: Requête invalide, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
        "422": { description: Adresse introuvable ou aucun itinéraire, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
        "502": { description: Service tiers indisponible, content: { application/json: { schema: { $ref: "#/components/schemas/Erreur" } } } }
components:
  schemas:
    Point:
      type: object
      properties:
        input: { type: string, description: "Texte reçu." }
        address: { type: string, description: "Adresse retenue par le géocodage (à vérifier)." }
        latitude: { type: number }
        longitude: { type: number }
        geocoding_source: { type: string, enum: [photon, lieu_forfaitaire] }
        forfait_place: { type: [string, "null"], description: "Lieu de la grille forfaitaire reconnu (aéroport, gare...)." }
    Estimation:
      type: object
      properties:
        success: { type: boolean }
        engine: { type: string }
        depart: { $ref: "#/components/schemas/Point" }
        destination: { $ref: "#/components/schemas/Point" }
        route:
          type: [object, "null"]
          properties:
            distance_km: { type: number, description: "Distance routière (0,1 km près), utilisée pour le tarif au compteur." }
            duration_minutes: { type: integer }
            service: { type: string }
        parameters:
          type: object
          properties:
            heure: { type: string }
            heure_source: { type: string, enum: [fournie, heure_actuelle_bruxelles] }
            tarif_nuit: { type: boolean }
            vehicule: { type: string }
            sieges_enfant: { type: integer }
        pricing:
          type: object
          properties:
            type: { type: string, enum: [forfait, forfait_longue_distance, compteur] }
            is_range: { type: boolean }
            forfait: { type: [object, "null"] }
            compteur: { type: [object, "null"] }
            supplements:
              type: array
              items: { type: object, properties: { code: { type: string }, label: { type: string }, amount_eur: { type: number } } }
            supplements_total_eur: { type: number }
            currency: { type: string, const: EUR }
        total_eur: { type: [number, "null"], description: "Prix total TTC ; null si fourchette (voir total_min_eur/total_max_eur)." }
        total_min_eur: { type: number }
        total_max_eur: { type: number }
        price_display: { type: string, description: "Prix tel qu'affiché par le calculateur." }
        calculation_explanation: { type: string }
        estimation_text: { type: string }
        warnings: { type: array, items: { type: string } }
    Erreur:
      type: object
      properties:
        success: { type: boolean, const: false }
        error: { type: string, enum: [missing_parameter, invalid_parameter, invalid_json, address_not_found, no_route_found, geocoding_service_unavailable, routing_service_unavailable, internal_error] }
        message: { type: string }
        field: { type: string }
