openapi: 3.1.0
info:
  title: LetsBookings API
  version: 0.2.0
  description: |
    Agent-native stay search across Airbnb and Booking.com (live via RapidAPI)
    plus optional sandbox. Ranks by **total price** when fee breakdown is available.

    ## Flow
    1. POST /api/agent-access/request → verify → Bearer token
    2. POST /api/search (NL or structured; sources: airbnb, booking)
    3. GET /api/results/{search_id} until status=completed

    ## Live inventory
    Set RAPIDAPI_KEY and subscribe to airbnb13 + booking-com15 on RapidAPI.
    GET /api/sources shows readiness.
  license:
    name: MIT

servers:
  - url: http://localhost:8787
    description: Local sandbox
  - url: https://letsbookings.co
    description: Production (planned)

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: Token from /api/agent-access/verify

  schemas:
    PriceBreakdown:
      type: object
      required: [nightly, total, total_per_night]
      properties:
        nightly: { type: number }
        cleaning_fee: { type: number }
        service_fee: { type: number }
        taxes: { type: number }
        total: { type: number, description: All-in total for the stay }
        total_per_night: { type: number }
        currency: { type: string, example: EUR }

    StayOffer:
      type: object
      properties:
        id: { type: string }
        title: { type: string }
        source:
          type: string
          enum: [airbnb, booking, vrbo, direct, sandbox, other]
        check_in: { type: string, format: date }
        check_out: { type: string, format: date }
        nights: { type: integer }
        guests: { type: integer }
        price: { $ref: "#/components/schemas/PriceBreakdown" }
        rating: { type: number }
        reviews: { type: integer }
        amenities:
          type: array
          items: { type: string }
        property_type: { type: string }
        url:
          type: string
          nullable: true
          description: Null until unlock

    SearchStartRequest:
      type: object
      properties:
        query:
          type: string
          description: Natural language query
        location: { type: string }
        check_in: { type: string, format: date }
        check_out: { type: string, format: date }
        guests: { type: integer, default: 1 }
        currency: { type: string, default: EUR }
        max_total_price: { type: number }
        max_total_per_night: { type: number }
        amenities:
          type: array
          items: { type: string }
        limit: { type: integer, default: 50 }
        sort:
          type: string
          enum: [total, total_per_night, rating]
          default: total

    SearchStartResponse:
      type: object
      properties:
        search_id: { type: string }
        status: { type: string, example: searching }
        poll_url: { type: string }
        poll_interval_seconds: { type: integer }
        parsed: { type: object, additionalProperties: true }

    SearchResultsResponse:
      type: object
      properties:
        search_id: { type: string }
        status:
          type: string
          enum: [searching, completed, expired, error]
        progress:
          type: object
          properties:
            checked: { type: integer }
            total: { type: integer }
            found: { type: integer }
        total_results: { type: integer }
        cheapest_total: { type: number }
        currency: { type: string }
        offers:
          type: array
          items: { $ref: "#/components/schemas/StayOffer" }
        sources_queried:
          type: array
          items: { type: string }
        sources_ok:
          type: array
          items: { type: string }
        source_errors:
          type: object
          additionalProperties: { type: string }
        mode:
          type: string
          enum: [live, sandbox]

paths:
  /api/sources:
    get:
      summary: Connector readiness (Airbnb, Booking.com, sandbox)
      responses:
        "200":
          description: Status map
  /health:
    get:
      security: []
      summary: Health check
      responses:
        "200":
          description: OK

  /api/agent-access/request:
    post:
      security: []
      summary: Start agent registration challenge
      responses:
        "200":
          description: Challenge issued

  /api/agent-access/verify:
    post:
      security: []
      summary: Verify challenge and receive Bearer token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tweet_url, challenge_signed]
              properties:
                tweet_url: { type: string }
                challenge_signed: { type: string }
      responses:
        "200":
          description: Token issued

  /api/search:
    post:
      summary: Start stay search (NL or structured)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchStartRequest"
            examples:
              natural_language:
                value:
                  query: "Barcelona apartment 2 guests Jul 10-14 2026 under 200 total"
              structured:
                value:
                  location: Barcelona
                  check_in: "2026-07-10"
                  check_out: "2026-07-14"
                  guests: 2
                  currency: EUR
      responses:
        "200":
          description: Search started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchStartResponse"
        "422":
          description: Needs clarification

  /api/results/{search_id}:
    get:
      summary: Poll search results
      parameters:
        - name: search_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Current status / offers
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchResultsResponse"
        "404":
          description: Unknown or expired search
