openapi: 3.1.0
info:
  title: Centre Pensees / MYT API
  version: 1.3.0
  description: >
    API multi-sites (CP Berlin, CP Barcelone, MYT Berlin).
    Lecture seule : emails + Bookly (therapeutes, rdv, patients, services, disponibilites, stats, paiements, invoices).
    IMPORTANT: endpoints avec slash final pour éviter les 301.

servers:
  - url: https://api.centre-pensees.com

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  schemas:
    Error:
      type: object
      properties:
        error: { type: string }

    EmailItem:
      type: object
      properties:
        uid: { type: integer }
        from: { type: string }
        subject: { type: string }
        date: { type: string }
        seen: { type: boolean }

    Staff:
      type: object
      properties:
        id: { type: integer }
        full_name: { type: string }
        email: { type: [string, "null"] }
        phone: { type: [string, "null"] }
        time_zone: { type: [string, "null"] }
        visibility: { type: [string, "null"] }
        position: { type: [integer, "null"] }

    Patient:
      type: object
      properties:
        id: { type: integer }
        full_name: { type: string }
        email: { type: [string, "null"] }
        phone: { type: [string, "null"] }
        created_at: { type: [string, "null"] }

    Service:
      type: object
      properties:
        id: { type: integer }
        title: { type: string }
        duration_sec: { type: integer, description: "Durée Bookly en secondes" }
        duration_min: { type: integer, description: "Compat: durée arrondie en minutes" }
        slot_length: { type: [string, "null"], description: "Valeur brute Bookly (souvent 'default' ou vide)" }
        price: { type: [number, "null"] }
        category_id: { type: [integer, "null"] }
        capacity_min: { type: [integer, "null"] }
        capacity_max: { type: [integer, "null"] }
        one_booking_per_slot: { type: [integer, "null"] }
        padding_left_sec: { type: [integer, "null"] }
        padding_right_sec: { type: [integer, "null"] }

    Appointment:
      type: object
      properties:
        appointment_id: { type: integer }
        start_at: { type: string, format: date-time }
        end_at: { type: string, format: date-time }
        status: { type: [string, "null"], description: "Status customer_appointment (approved/pending/done/cancelled/rejected...)" }
        staff_id: { type: integer }
        service_id: { type: [integer, "null"] }
        patient_id: { type: [integer, "null"] }
        patient_name: { type: [string, "null"] }
        patient_email: { type: [string, "null"] }
        service_title: { type: [string, "null"] }

    AvailabilitySlot:
      type: object
      properties:
        staff_id: { type: integer }
        start_at: { type: string, format: date-time }
        end_at: { type: string, format: date-time }
        slot_minutes: { type: integer }
        state:
          type: string
          enum: [available, partially_booked, fully_booked]
          description: "fully_booked n'est renvoyé que si debug_blocked=1"
        capacity_max: { type: integer }
        capacity_used: { type: integer }
        timezone: { type: string }

    StatsItem:
      type: object
      properties:
        staff_id: { type: integer }
        staff_name: { type: string }
        count_total: { type: integer }
        count_approved: { type: integer }
        count_pending: { type: integer }
        count_done: { type: integer }
        count_cancelled: { type: integer }
        count_rejected: { type: integer }
        seconds_total: { type: integer }
        seconds_approved: { type: integer }
        hours_total: { type: number }
        hours_approved: { type: number }

    StatsResponse:
      type: object
      properties:
        range:
          type: object
          properties:
            from: { type: string, format: date-time }
            to: { type: string, format: date-time }
            timezone: { type: string }
        items:
          type: array
          items: { $ref: "#/components/schemas/StatsItem" }

    Payment:
      type: object
      properties:
        payment_id: { type: integer }
        type: { type: [string, "null"], description: "local/free/stripe/paypal/..." }
        status: { type: [string, "null"], description: "pending/completed/refunded/..." }
        total: { type: [number, "null"] }
        paid: { type: [number, "null"] }
        tax: { type: [number, "null"] }
        discount: { type: [number, "null"] }
        invoice_id: { type: [string, "null"] }
        ref_id: { type: [string, "null"] }
        created_at: { type: [string, "null"] }
        updated_at: { type: [string, "null"] }
        customer_id: { type: [integer, "null"] }
        customer_name: { type: [string, "null"] }
        customer_email: { type: [string, "null"] }
        appointment_id: { type: [integer, "null"] }
        staff_id: { type: [integer, "null"] }
        appointment_start_at: { type: [string, "null"] }
        appointment_end_at: { type: [string, "null"] }
        customer_appointment_status: { type: [string, "null"] }

    Snapshot:
      type: object
      properties:
        site: { type: string }
        range:
          type: object
          properties:
            from: { type: string, format: date-time }
            to: { type: string, format: date-time }
            timezone: { type: string }
        therapeutes:
          type: array
          items: { $ref: "#/components/schemas/Staff" }
        services:
          type: array
          items: { $ref: "#/components/schemas/Service" }
        rdv:
          type: array
          items: { $ref: "#/components/schemas/Appointment" }
        disponibilites:
          type: array
          items: { $ref: "#/components/schemas/AvailabilitySlot" }
        stats:
          type: array
          items: { $ref: "#/components/schemas/StatsItem" }
        paiements:
          type: array
          items: { $ref: "#/components/schemas/Payment" }

security:
  - ApiKeyAuth: []

paths:
  /penseesberlin/emails/:
    get:
      operationId: cp_berlin_get_emails
      summary: Liste les emails (CP Berlin)
      parameters:
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 50, default: 10 }
        - name: unseen_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/EmailItem" } }

  /penseesbarcelone/emails/:
    get:
      operationId: cp_barcelone_get_emails
      summary: Liste les emails (CP Barcelone)
      parameters:
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 50, default: 10 }
        - name: unseen_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/EmailItem" } }

  /mytberlin/emails/:
    get:
      operationId: myt_berlin_get_emails
      summary: Liste les emails (MYT Berlin)
      parameters:
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 50, default: 10 }
        - name: unseen_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/EmailItem" } }

  /penseesberlin/bookly/therapeutes/:
    get:
      operationId: cp_berlin_get_therapeutes
      summary: Liste des thérapeutes (penseesberlin)
      parameters:
        - name: active_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Staff" } }

  /penseesberlin/bookly/patients/:
    get:
      operationId: cp_berlin_get_patients
      summary: Liste des patients (penseesberlin)
      parameters:
        - name: q
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Patient" } }

  /penseesberlin/bookly/services/:
    get:
      operationId: cp_berlin_get_services
      summary: Liste des services (penseesberlin)
      parameters:
        - name: category_id
          in: query
          schema: { type: integer }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Service" } }

  /penseesberlin/bookly/rdv/:
    get:
      operationId: cp_berlin_get_rendezvous
      summary: Liste des rendez-vous (penseesberlin)
      parameters:
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: patient_id
          in: query
          schema: { type: integer }
        - name: status
          in: query
          description: "Ex: approved,pending,cancelled,rejected,done"
          schema: { type: string }
        - name: mode
          in: query
          schema: { type: string, enum: [future, past, range], default: future }
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: include_patient
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: include_service
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Appointment" } }

  /penseesberlin/bookly/disponibilites/:
    get:
      operationId: cp_berlin_get_disponibilites
      summary: Disponibilités (slots libres) (penseesberlin)
      description: >
        Calcule les slots *acceptés par Bookly* (subset sans Locations/Custom Statuses/Waiting List) :
        horaires + breaks + holidays (+ special days si présent),
        en retirant les RDV existants (statuses bloquants: approved,pending) et en appliquant service duration + padding.
      parameters:
        - name: staff_id
          in: query
          description: "Si absent: calcule pour tous les thérapeutes"
          schema: { type: integer }
        - name: service_id
          in: query
          required: true
          schema: { type: integer }
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: slot_minutes
          in: query
          description: "Override manuel (sinon durée du service)"
          schema: { type: integer, minimum: 5, maximum: 240 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 2000, default: 500 }
        - name: debug_blocked
          in: query
          description: "Si 1: inclut aussi les slots fully_booked pour debug"
          schema: { type: integer, enum: [0, 1], default: 0 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/AvailabilitySlot" } }

  /penseesberlin/bookly/stats_rdv/:
    get:
      operationId: cp_berlin_get_rdv_stats
      summary: Stats RDV (penseesberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatsResponse" }

  /penseesberlin/bookly/paiements/:
    get:
      operationId: cp_berlin_get_paiements
      summary: Paiements Bookly (penseesberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          description: "Ex: completed,pending,refunded"
          schema: { type: string }
        - name: type
          in: query
          description: "Ex: local,free,stripe,paypal,..."
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: payment_id
          in: query
          schema: { type: integer }
        - name: invoice_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /penseesberlin/bookly/invoices/:
    get:
      operationId: cp_berlin_get_invoices
      summary: Invoices (via payments.invoice_id) (penseesberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          schema: { type: string }
        - name: type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /penseesberlin/bookly/snapshot/:
    get:
      operationId: cp_berlin_get_snapshot
      summary: Snapshot (bundle lecture seule) (penseesberlin)
      description: >
        Regroupe en 1 appel (therapeutes/services/rdv/disponibilites/stats/paiements).
        Utilise include=... pour contrôler la charge.
      parameters:
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: service_id
          in: query
          description: "Requis si include contient disponibilites"
          schema: { type: integer }
        - name: include
          in: query
          description: "CSV: therapeutes,services,rdv,disponibilites,stats,paiements"
          schema: { type: string }
        - name: slot_minutes
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          description: "Limite des slots disponibilites"
          schema: { type: integer, default: 500 }
        - name: payments_limit
          in: query
          schema: { type: integer, default: 200 }
        - name: payment_status
          in: query
          schema: { type: string }
        - name: payment_type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: debug_blocked
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Snapshot" }

  /penseesbarcelone/bookly/therapeutes/:
    get:
      operationId: cp_barcelone_get_therapeutes
      summary: Liste des thérapeutes (penseesbarcelone)
      parameters:
        - name: active_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Staff" } }

  /penseesbarcelone/bookly/patients/:
    get:
      operationId: cp_barcelone_get_patients
      summary: Liste des patients (penseesbarcelone)
      parameters:
        - name: q
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Patient" } }

  /penseesbarcelone/bookly/services/:
    get:
      operationId: cp_barcelone_get_services
      summary: Liste des services (penseesbarcelone)
      parameters:
        - name: category_id
          in: query
          schema: { type: integer }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Service" } }

  /penseesbarcelone/bookly/rdv/:
    get:
      operationId: cp_barcelone_get_rendezvous
      summary: Liste des rendez-vous (penseesbarcelone)
      parameters:
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: patient_id
          in: query
          schema: { type: integer }
        - name: status
          in: query
          description: "Ex: approved,pending,cancelled,rejected,done"
          schema: { type: string }
        - name: mode
          in: query
          schema: { type: string, enum: [future, past, range], default: future }
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: include_patient
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: include_service
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Appointment" } }

  /penseesbarcelone/bookly/disponibilites/:
    get:
      operationId: cp_barcelone_get_disponibilites
      summary: Disponibilités (slots libres) (penseesbarcelone)
      description: >
        Calcule les slots *acceptés par Bookly* (subset sans Locations/Custom Statuses/Waiting List) :
        horaires + breaks + holidays (+ special days si présent),
        en retirant les RDV existants (statuses bloquants: approved,pending) et en appliquant service duration + padding.
      parameters:
        - name: staff_id
          in: query
          description: "Si absent: calcule pour tous les thérapeutes"
          schema: { type: integer }
        - name: service_id
          in: query
          required: true
          schema: { type: integer }
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: slot_minutes
          in: query
          description: "Override manuel (sinon durée du service)"
          schema: { type: integer, minimum: 5, maximum: 240 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Madrid" }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 2000, default: 500 }
        - name: debug_blocked
          in: query
          description: "Si 1: inclut aussi les slots fully_booked pour debug"
          schema: { type: integer, enum: [0, 1], default: 0 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/AvailabilitySlot" } }

  /penseesbarcelone/bookly/stats_rdv/:
    get:
      operationId: cp_barcelone_get_rdv_stats
      summary: Stats RDV (penseesbarcelone)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Madrid" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatsResponse" }

  /penseesbarcelone/bookly/paiements/:
    get:
      operationId: cp_barcelone_get_paiements
      summary: Paiements Bookly (penseesbarcelone)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          description: "Ex: completed,pending,refunded"
          schema: { type: string }
        - name: type
          in: query
          description: "Ex: local,free,stripe,paypal,..."
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: payment_id
          in: query
          schema: { type: integer }
        - name: invoice_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Madrid" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /penseesbarcelone/bookly/invoices/:
    get:
      operationId: cp_barcelone_get_invoices
      summary: Invoices (via payments.invoice_id) (penseesbarcelone)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          schema: { type: string }
        - name: type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Madrid" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /penseesbarcelone/bookly/snapshot/:
    get:
      operationId: cp_barcelone_get_snapshot
      summary: Snapshot (bundle lecture seule) (penseesbarcelone)
      description: >
        Regroupe en 1 appel (therapeutes/services/rdv/disponibilites/stats/paiements).
        Utilise include=... pour contrôler la charge.
      parameters:
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: service_id
          in: query
          description: "Requis si include contient disponibilites"
          schema: { type: integer }
        - name: include
          in: query
          description: "CSV: therapeutes,services,rdv,disponibilites,stats,paiements"
          schema: { type: string }
        - name: slot_minutes
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          description: "Limite des slots disponibilites"
          schema: { type: integer, default: 500 }
        - name: payments_limit
          in: query
          schema: { type: integer, default: 200 }
        - name: payment_status
          in: query
          schema: { type: string }
        - name: payment_type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: debug_blocked
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Madrid" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Snapshot" }

  /mytberlin/bookly/therapeutes/:
    get:
      operationId: myt_berlin_get_therapeutes
      summary: Liste des thérapeutes (mytberlin)
      parameters:
        - name: active_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Staff" } }

  /mytberlin/bookly/patients/:
    get:
      operationId: myt_berlin_get_patients
      summary: Liste des patients (mytberlin)
      parameters:
        - name: q
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Patient" } }

  /mytberlin/bookly/services/:
    get:
      operationId: myt_berlin_get_services
      summary: Liste des services (mytberlin)
      parameters:
        - name: category_id
          in: query
          schema: { type: integer }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Service" } }

  /mytberlin/bookly/rdv/:
    get:
      operationId: myt_berlin_get_rendezvous
      summary: Liste des rendez-vous (mytberlin)
      parameters:
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: patient_id
          in: query
          schema: { type: integer }
        - name: status
          in: query
          description: "Ex: approved,pending,cancelled,rejected,done"
          schema: { type: string }
        - name: mode
          in: query
          schema: { type: string, enum: [future, past, range], default: future }
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: include_patient
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: include_service
          in: query
          schema: { type: integer, enum: [0, 1], default: 1 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Appointment" } }

  /mytberlin/bookly/disponibilites/:
    get:
      operationId: myt_berlin_get_disponibilites
      summary: Disponibilités (slots libres) (mytberlin)
      description: >
        Calcule les slots *acceptés par Bookly* (subset sans Locations/Custom Statuses/Waiting List) :
        horaires + breaks + holidays (+ special days si présent),
        en retirant les RDV existants (statuses bloquants: approved,pending) et en appliquant service duration + padding.
      parameters:
        - name: staff_id
          in: query
          description: "Si absent: calcule pour tous les thérapeutes"
          schema: { type: integer }
        - name: service_id
          in: query
          required: true
          schema: { type: integer }
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: slot_minutes
          in: query
          description: "Override manuel (sinon durée du service)"
          schema: { type: integer, minimum: 5, maximum: 240 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 2000, default: 500 }
        - name: debug_blocked
          in: query
          description: "Si 1: inclut aussi les slots fully_booked pour debug"
          schema: { type: integer, enum: [0, 1], default: 0 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/AvailabilitySlot" } }

  /mytberlin/bookly/stats_rdv/:
    get:
      operationId: myt_berlin_get_rdv_stats
      summary: Stats RDV (mytberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/StatsResponse" }

  /mytberlin/bookly/paiements/:
    get:
      operationId: myt_berlin_get_paiements
      summary: Paiements Bookly (mytberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          description: "Ex: completed,pending,refunded"
          schema: { type: string }
        - name: type
          in: query
          description: "Ex: local,free,stripe,paypal,..."
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: payment_id
          in: query
          schema: { type: integer }
        - name: invoice_only
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /mytberlin/bookly/invoices/:
    get:
      operationId: myt_berlin_get_invoices
      summary: Invoices (via payments.invoice_id) (mytberlin)
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: status
          in: query
          schema: { type: string }
        - name: type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: appointment_id
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: array, items: { $ref: "#/components/schemas/Payment" } }

  /mytberlin/bookly/snapshot/:
    get:
      operationId: myt_berlin_get_snapshot
      summary: Snapshot (bundle lecture seule) (mytberlin)
      description: >
        Regroupe en 1 appel (therapeutes/services/rdv/disponibilites/stats/paiements).
        Utilise include=... pour contrôler la charge.
      parameters:
        - name: from
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: to
          in: query
          required: true
          schema: { type: string, format: date-time }
        - name: staff_id
          in: query
          schema: { type: integer }
        - name: service_id
          in: query
          description: "Requis si include contient disponibilites"
          schema: { type: integer }
        - name: include
          in: query
          description: "CSV: therapeutes,services,rdv,disponibilites,stats,paiements"
          schema: { type: string }
        - name: slot_minutes
          in: query
          schema: { type: integer }
        - name: limit
          in: query
          description: "Limite des slots disponibilites"
          schema: { type: integer, default: 500 }
        - name: payments_limit
          in: query
          schema: { type: integer, default: 200 }
        - name: payment_status
          in: query
          schema: { type: string }
        - name: payment_type
          in: query
          schema: { type: string }
        - name: customer_id
          in: query
          schema: { type: integer }
        - name: debug_blocked
          in: query
          schema: { type: integer, enum: [0, 1], default: 0 }
        - name: timezone
          in: query
          schema: { type: string, default: "Europe/Berlin" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Snapshot" }
