openapi: 3.0.3
info:
  title: NovaTrek Adventures - Trip Catalog Service
  description: |
    Manages adventure trip definitions, scheduling, pricing, and availability
    for NovaTrek Adventures outdoor recreation platform.

    ## Overview
    The Trip Catalog Service is the central registry for all bookable adventure
    experiences. It handles the full lifecycle of trip definitions from draft
    creation through active availability to retirement. Each trip is associated
    with a specific activity type, difficulty level, and operating region.

    ## Cross-Service References
    - **svc-guide-management**: Guide assignments for scheduled departures
    - **svc-weather**: Weather station data linked to operating regions
    - **svc-booking**: Consumes trip availability for reservation creation
    - **svc-guest-profiles**: Validates guest certifications against trip requirements
  version: 2.4.0
  contact:
    name: NovaTrek Platform Engineering
    email: platform-eng@novatrek.example.com
  license:
    name: Proprietary
    url: https://novatrek.example.com/legal/api-license

servers:
  - url: https://api.novatrek.example.com/trips/v1
    description: Production
  - url: https://api-staging.novatrek.example.com/trips/v1
    description: Staging

tags:
  - name: Trips
    description: Trip definition management
  - name: Schedule
    description: Departure scheduling and capacity
  - name: Pricing
    description: Pricing tier configuration
  - name: Requirements
    description: Gear, certification, and fitness requirements
  - name: Reference Data
    description: Regions and activity type lookups

paths:
  /trips:
    get:
      operationId: searchTrips
      summary: Search trips with filters
      description: |
        Returns a paginated list of trips matching the specified filter criteria.
        Results are sorted by relevance unless an explicit sort parameter is provided.
      tags:
        - Trips
      parameters:
        - name: activity_type
          in: query
          description: Filter by activity type
          schema:
            $ref: '#/components/schemas/ActivityType'
        - name: difficulty
          in: query
          description: Filter by difficulty level
          schema:
            $ref: '#/components/schemas/DifficultyLevel'
        - name: region
          in: query
          description: Filter by region identifier
          schema:
            type: string
            format: uuid
        - name: date_from
          in: query
          description: Filter scheduled departures on or after this date
          schema:
            type: string
            format: date
        - name: date_to
          in: query
          description: Filter scheduled departures on or before this date
          schema:
            type: string
            format: date
        - name: min_price
          in: query
          description: Minimum base price filter (inclusive)
          schema:
            type: number
            format: double
            minimum: 0
        - name: max_price
          in: query
          description: Maximum base price filter (inclusive)
          schema:
            type: number
            format: double
            minimum: 0
        - name: status
          in: query
          description: Filter by trip status (defaults to ACTIVE)
          schema:
            $ref: '#/components/schemas/TripStatus'
        - name: sort
          in: query
          description: Sort field and direction
          schema:
            type: string
            enum:
              - name_asc
              - name_desc
              - price_asc
              - price_desc
              - difficulty_asc
              - difficulty_desc
              - created_desc
            default: name_asc
        - $ref: '#/components/parameters/PageNumber'
        - $ref: '#/components/parameters/PageSize'
      responses:
        '200':
          description: Matching trips
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TripSummary'
                  pagination:
                    $ref: '#/components/schemas/PaginationMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

    post:
      operationId: createTrip
      summary: Create a new trip definition
      description: |
        Creates a new trip in DRAFT status. The trip must be explicitly
        transitioned to ACTIVE status before it becomes bookable.
      tags:
        - Trips
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TripCreateRequest'
      responses:
        '201':
          description: Trip created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trip'
          headers:
            Location:
              description: URI of the newly created trip
              schema:
                type: string
                format: uri
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: A trip with the same name already exists in this region
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalError'

  /trips/{trip_id}:
    parameters:
      - $ref: '#/components/parameters/TripId'
    get:
      operationId: getTripById
      summary: Get trip details
      description: Returns the full trip definition including all metadata.
      tags:
        - Trips
      responses:
        '200':
          description: Trip details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trip'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

    patch:
      operationId: updateTrip
      summary: Update trip details
      description: |
        Partially updates a trip definition. Only provided fields are modified.
        Status transitions follow the lifecycle rules:
        DRAFT -> ACTIVE -> SUSPENDED <-> ACTIVE, SUSPENDED -> RETIRED.
        RETIRED trips cannot be reactivated.
      tags:
        - Trips
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TripUpdateRequest'
      responses:
        '200':
          description: Trip updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trip'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Invalid status transition or conflicting update
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalError'

  /trips/{trip_id}/schedule:
    parameters:
      - $ref: '#/components/parameters/TripId'
    get:
      operationId: getTripSchedule
      summary: Get scheduled departures
      description: |
        Returns all scheduled departures for a trip, optionally filtered
        by date range or availability status.
      tags:
        - Schedule
      parameters:
        - name: date_from
          in: query
          description: Return departures on or after this date
          schema:
            type: string
            format: date
        - name: date_to
          in: query
          description: Return departures on or before this date
          schema:
            type: string
            format: date
        - name: available_only
          in: query
          description: Only return departures with available spots
          schema:
            type: boolean
            default: false
        - $ref: '#/components/parameters/PageNumber'
        - $ref: '#/components/parameters/PageSize'
      responses:
        '200':
          description: Scheduled departures
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TripSchedule'
                  pagination:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

    post:
      operationId: addScheduledDeparture
      summary: Add a scheduled departure
      description: |
        Adds a new departure date and time for this trip. The trip must be
        in ACTIVE status. Guide assignment is validated against svc-guide-management.
      tags:
        - Schedule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TripScheduleCreateRequest'
      responses:
        '201':
          description: Departure scheduled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripSchedule'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Schedule conflict - guide already assigned or date overlap
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalError'

  /trips/{trip_id}/pricing:
    parameters:
      - $ref: '#/components/parameters/TripId'
    get:
      operationId: getTripPricing
      summary: Get pricing tiers
      description: Returns all pricing tiers configured for the specified trip.
      tags:
        - Pricing
      responses:
        '200':
          description: Pricing tiers
          content:
            application/json:
              schema:
                type: object
                properties:
                  trip_id:
                    type: string
                    format: uuid
                  base_price:
                    type: number
                    format: double
                  currency:
                    type: string
                    example: USD
                  tiers:
                    type: array
                    items:
                      $ref: '#/components/schemas/PricingTier'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

    put:
      operationId: updateTripPricing
      summary: Replace pricing tiers
      description: |
        Replaces all pricing tiers for the trip. At minimum, a STANDARD tier
        must be provided. Changes take effect for future bookings only.
      tags:
        - Pricing
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - tiers
              properties:
                currency:
                  type: string
                  default: USD
                  minLength: 3
                  maxLength: 3
                tiers:
                  type: array
                  items:
                    $ref: '#/components/schemas/PricingTier'
                  minItems: 1
      responses:
        '200':
          description: Pricing updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  trip_id:
                    type: string
                    format: uuid
                  base_price:
                    type: number
                    format: double
                  currency:
                    type: string
                  tiers:
                    type: array
                    items:
                      $ref: '#/components/schemas/PricingTier'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /trips/{trip_id}/requirements:
    parameters:
      - $ref: '#/components/parameters/TripId'
    get:
      operationId: getTripRequirements
      summary: Get trip requirements
      description: |
        Returns gear, certification, and fitness requirements for the trip.
        Used by svc-booking to validate guest eligibility at reservation time.
      tags:
        - Requirements
      responses:
        '200':
          description: Trip requirements
          content:
            application/json:
              schema:
                type: object
                properties:
                  trip_id:
                    type: string
                    format: uuid
                  age_minimum:
                    type: integer
                    minimum: 0
                  fitness_level_required:
                    $ref: '#/components/schemas/FitnessLevel'
                  certifications:
                    type: array
                    items:
                      type: string
                    description: Required certification identifiers
                    example:
                      - WILDERNESS_FIRST_AID
                      - BELAY_CERTIFIED
                  gear_requirements:
                    type: array
                    items:
                      $ref: '#/components/schemas/GearRequirement'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /regions:
    get:
      operationId: listRegions
      summary: List operating regions
      description: Returns all regions where NovaTrek operates adventure trips.
      tags:
        - Reference Data
      parameters:
        - name: country
          in: query
          description: Filter regions by country code (ISO 3166-1 alpha-2)
          schema:
            type: string
            minLength: 2
            maxLength: 2
        - name: has_active_trips
          in: query
          description: Only return regions that have at least one active trip
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: List of regions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Region'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /activity-types:
    get:
      operationId: listActivityTypes
      summary: List available activity types
      description: |
        Returns the enumerated list of supported activity types with
        display metadata and active trip counts.
      tags:
        - Reference Data
      responses:
        '200':
          description: Activity types
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        code:
                          $ref: '#/components/schemas/ActivityType'
                        display_name:
                          type: string
                          example: Rock Climbing
                        description:
                          type: string
                        icon_url:
                          type: string
                          format: uri
                        active_trip_count:
                          type: integer
                          minimum: 0
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

components:
  parameters:
    TripId:
      name: trip_id
      in: path
      required: true
      description: Unique trip identifier
      schema:
        type: string
        format: uuid

    PageNumber:
      name: page
      in: query
      description: Page number (1-based)
      schema:
        type: integer
        minimum: 1
        default: 1

    PageSize:
      name: page_size
      in: query
      description: Number of items per page
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25

  schemas:
    ActivityType:
      type: string
      enum:
        - HIKING
        - ROCK_CLIMBING
        - KAYAKING
        - RAFTING
        - CAMPING
        - MOUNTAIN_BIKING
        - CANYONEERING
        - SNOWSHOEING
        - BACKCOUNTRY_SKIING
        - WILDLIFE_SAFARI

    DifficultyLevel:
      type: string
      enum:
        - BEGINNER
        - INTERMEDIATE
        - ADVANCED
        - EXPERT

    TripStatus:
      type: string
      enum:
        - DRAFT
        - ACTIVE
        - SUSPENDED
        - RETIRED

    FitnessLevel:
      type: string
      enum:
        - LOW
        - MODERATE
        - HIGH
        - VERY_HIGH
      description: Required fitness level for trip participation

    ScheduleStatus:
      type: string
      enum:
        - OPEN
        - FULL
        - CANCELLED
        - COMPLETED

    PricingTierName:
      type: string
      enum:
        - STANDARD
        - PREMIUM
        - GROUP
        - EARLY_BIRD

    Trip:
      type: object
      required:
        - id
        - name
        - activity_type
        - difficulty_level
        - region_id
        - duration_hours
        - min_participants
        - max_participants
        - base_price
        - status
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          minLength: 3
          maxLength: 200
          example: "Zion Narrows Top-Down"
        description:
          type: string
          maxLength: 5000
          example: "A full-day adventure through the iconic Zion Narrows slot canyon, starting from Chamberlain's Ranch."
        activity_type:
          $ref: '#/components/schemas/ActivityType'
        difficulty_level:
          $ref: '#/components/schemas/DifficultyLevel'
        region_id:
          type: string
          format: uuid
          description: Reference to operating region
        duration_hours:
          type: number
          format: double
          minimum: 0.5
          maximum: 720
          example: 12
          description: Total trip duration in hours
        min_participants:
          type: integer
          minimum: 1
          maximum: 100
          example: 2
        max_participants:
          type: integer
          minimum: 1
          maximum: 200
          example: 12
        base_price:
          type: number
          format: double
          minimum: 0
          example: 189.99
          description: Base price per participant in USD
        status:
          $ref: '#/components/schemas/TripStatus'
        required_certifications:
          type: array
          items:
            type: string
          example:
            - WILDERNESS_FIRST_AID
          description: Certifications a participant must hold
        gear_requirements:
          type: array
          items:
            $ref: '#/components/schemas/GearRequirement'
        age_minimum:
          type: integer
          minimum: 0
          default: 18
          example: 16
          description: Minimum participant age in years
        fitness_level_required:
          $ref: '#/components/schemas/FitnessLevel'
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
        created_by:
          type: string
          readOnly: true
          description: User ID of the trip creator

    TripSummary:
      type: object
      description: Lightweight trip representation for list endpoints
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        activity_type:
          $ref: '#/components/schemas/ActivityType'
        difficulty_level:
          $ref: '#/components/schemas/DifficultyLevel'
        region_id:
          type: string
          format: uuid
        duration_hours:
          type: number
          format: double
        base_price:
          type: number
          format: double
        status:
          $ref: '#/components/schemas/TripStatus'
        next_departure:
          type: string
          format: date
          nullable: true
          description: Date of the next scheduled departure, null if none

    TripCreateRequest:
      type: object
      required:
        - name
        - activity_type
        - difficulty_level
        - region_id
        - duration_hours
        - min_participants
        - max_participants
        - base_price
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 200
        description:
          type: string
          maxLength: 5000
        activity_type:
          $ref: '#/components/schemas/ActivityType'
        difficulty_level:
          $ref: '#/components/schemas/DifficultyLevel'
        region_id:
          type: string
          format: uuid
        duration_hours:
          type: number
          format: double
          minimum: 0.5
          maximum: 720
        min_participants:
          type: integer
          minimum: 1
        max_participants:
          type: integer
          minimum: 1
        base_price:
          type: number
          format: double
          minimum: 0
        required_certifications:
          type: array
          items:
            type: string
          default: []
        gear_requirements:
          type: array
          items:
            $ref: '#/components/schemas/GearRequirement'
          default: []
        age_minimum:
          type: integer
          minimum: 0
          default: 18
        fitness_level_required:
          $ref: '#/components/schemas/FitnessLevel'

    TripUpdateRequest:
      type: object
      description: All fields are optional; only provided fields are updated
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 200
        description:
          type: string
          maxLength: 5000
        difficulty_level:
          $ref: '#/components/schemas/DifficultyLevel'
        duration_hours:
          type: number
          format: double
          minimum: 0.5
          maximum: 720
        min_participants:
          type: integer
          minimum: 1
        max_participants:
          type: integer
          minimum: 1
        base_price:
          type: number
          format: double
          minimum: 0
        status:
          $ref: '#/components/schemas/TripStatus'
        required_certifications:
          type: array
          items:
            type: string
        gear_requirements:
          type: array
          items:
            $ref: '#/components/schemas/GearRequirement'
        age_minimum:
          type: integer
          minimum: 0
        fitness_level_required:
          $ref: '#/components/schemas/FitnessLevel'

    TripSchedule:
      type: object
      required:
        - id
        - trip_id
        - departure_date
        - departure_time
        - available_spots
        - status
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        trip_id:
          type: string
          format: uuid
        departure_date:
          type: string
          format: date
          example: "2026-07-15"
        departure_time:
          type: string
          pattern: '^\d{2}:\d{2}$'
          example: "06:30"
          description: Local departure time in HH:mm format
        guide_id:
          type: string
          format: uuid
          nullable: true
          description: Guide identifier from svc-guide-management
        available_spots:
          type: integer
          minimum: 0
          description: Remaining spots available for booking
        total_spots:
          type: integer
          minimum: 1
          description: Total spots for this departure (equals trip max_participants)
        status:
          $ref: '#/components/schemas/ScheduleStatus'
        meeting_point:
          type: string
          maxLength: 500
          example: "Chamberlain's Ranch Trailhead parking lot"
          description: Where participants should meet
        notes:
          type: string
          maxLength: 2000
          description: Departure-specific notes or instructions
        created_at:
          type: string
          format: date-time
          readOnly: true

    TripScheduleCreateRequest:
      type: object
      required:
        - departure_date
        - departure_time
      properties:
        departure_date:
          type: string
          format: date
        departure_time:
          type: string
          pattern: '^\d{2}:\d{2}$'
          example: "07:00"
        guide_id:
          type: string
          format: uuid
          description: Guide identifier from svc-guide-management (optional at creation)
        meeting_point:
          type: string
          maxLength: 500
        notes:
          type: string
          maxLength: 2000

    PricingTier:
      type: object
      required:
        - tier_name
        - price
      properties:
        tier_name:
          $ref: '#/components/schemas/PricingTierName'
        price:
          type: number
          format: double
          minimum: 0
          example: 189.99
          description: Per-participant price for this tier
        conditions:
          type: string
          maxLength: 1000
          description: Human-readable conditions for tier eligibility
          example: "Book 30+ days in advance"
        min_group_size:
          type: integer
          minimum: 1
          description: Minimum group size for GROUP tier
        max_group_size:
          type: integer
          minimum: 1
          description: Maximum group size for GROUP tier
        valid_from:
          type: string
          format: date
          description: Start date for seasonal or promotional pricing
        valid_to:
          type: string
          format: date
          description: End date for seasonal or promotional pricing
        is_active:
          type: boolean
          default: true

    GearRequirement:
      type: object
      required:
        - item_type
        - mandatory
      properties:
        item_type:
          type: string
          maxLength: 100
          example: "Dry suit"
          description: Type of gear item
        description:
          type: string
          maxLength: 500
          example: "Full-body dry suit rated for water temperatures below 60F"
        mandatory:
          type: boolean
          description: Whether this gear item is required (true) or recommended (false)
        rental_available:
          type: boolean
          default: false
          description: Whether NovaTrek offers rental for this item
        rental_price:
          type: number
          format: double
          minimum: 0
          nullable: true
          description: Per-trip rental price in USD, null if rental not available
          example: 45.00

    Region:
      type: object
      required:
        - id
        - name
        - country
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          minLength: 2
          maxLength: 200
          example: "Zion National Park"
        description:
          type: string
          maxLength: 2000
          example: "Southwest Utah canyon country featuring slot canyons, sandstone cliffs, and the Virgin River."
        country:
          type: string
          minLength: 2
          maxLength: 2
          description: ISO 3166-1 alpha-2 country code
          example: "US"
        state:
          type: string
          maxLength: 100
          example: "Utah"
          description: State, province, or administrative region
        coordinates:
          type: object
          properties:
            latitude:
              type: number
              format: double
              minimum: -90
              maximum: 90
              example: 37.2982
            longitude:
              type: number
              format: double
              minimum: -180
              maximum: 180
              example: -113.0263
        weather_station_id:
          type: string
          nullable: true
          description: Weather station identifier from svc-weather for conditions lookup
          example: "ws-zion-001"
        timezone:
          type: string
          example: "America/Denver"
          description: IANA timezone identifier
        active_trip_count:
          type: integer
          minimum: 0
          readOnly: true

    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
          example: 1
        page_size:
          type: integer
          example: 25
        total_items:
          type: integer
          example: 142
        total_pages:
          type: integer
          example: 6

    ErrorResponse:
      type: object
      required:
        - error_code
        - message
      properties:
        error_code:
          type: string
          description: Machine-readable error code
          example: "TRIP_NOT_FOUND"
        message:
          type: string
          description: Human-readable error description
          example: "No trip found with the specified identifier"
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
                example: "base_price"
              reason:
                type: string
                example: "must be greater than or equal to 0"
          description: Field-level validation errors
        trace_id:
          type: string
          description: Correlation identifier for debugging
          example: "nt-abc123-def456"

  responses:
    BadRequest:
      description: Invalid request parameters or body
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Unauthorized:
      description: Missing or invalid authentication credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    NotFound:
      description: Requested resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    InternalError:
      description: Unexpected server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: NovaTrek platform JWT issued by svc-auth

    apiKey:
      type: apiKey
      in: header
      name: X-NovaTrek-API-Key
      description: API key for service-to-service calls

security:
  - bearerAuth: []
  - apiKey: []
