openapi: 3.0.3
info:
  title: NovaTrek Media Gallery Service
  description: |
    Manages trip photos, videos, and media content captured during NovaTrek adventures.
    Supports uploads from guides, guests, and drone footage systems. Provides shareable
    links and gallery views tied to reservations and trips.
  version: 1.0.2
  contact:
    name: NovaTrek Digital Experience Team
    email: digital@novatrek.example.com

servers:
  - url: https://api.novatrek.example.com/media/v1
    description: Production

security:
  - BearerAuth: []

paths:
  /media:
    post:
      operationId: uploadMedia
      summary: Upload a media item
      description: |
        Uploads a new photo, video, or panorama. The media is associated with a
        reservation (from svc-reservations) and optionally a specific trip.
      tags: [Media]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MediaUploadRequest'
      responses:
        '201':
          description: Media uploaded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaItem'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          description: Media file exceeds size limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    get:
      operationId: listMedia
      summary: List media by reservation or trip
      tags: [Media]
      parameters:
        - name: reservation_id
          in: query
          schema:
            type: string
            format: uuid
          description: Reference to svc-reservations reservation
        - name: trip_id
          in: query
          schema:
            type: string
            format: uuid
        - name: media_type
          in: query
          schema:
            $ref: '#/components/schemas/MediaType'
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of media items
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/MediaItem'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'

  /media/{media_id}:
    get:
      operationId: getMedia
      summary: Get media item details
      tags: [Media]
      parameters:
        - $ref: '#/components/parameters/MediaIdPath'
      responses:
        '200':
          description: Media item details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaItem'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
    delete:
      operationId: deleteMedia
      summary: Delete a media item
      description: Soft-deletes the media item. Underlying storage is purged after 30 days.
      tags: [Media]
      parameters:
        - $ref: '#/components/parameters/MediaIdPath'
      responses:
        '204':
          description: Media item deleted
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /media/{media_id}/share:
    post:
      operationId: createShareLink
      summary: Create a shareable link for a media item
      description: Generates a time-limited, tokenized URL for sharing with non-authenticated users.
      tags: [Sharing]
      parameters:
        - $ref: '#/components/parameters/MediaIdPath'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShareLinkRequest'
      responses:
        '201':
          description: Share link created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShareLink'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  parameters:
    MediaIdPath:
      name: media_id
      in: path
      required: true
      schema:
        type: string
        format: uuid

  schemas:
    MediaItem:
      type: object
      required: [id, reservation_id, uploader_type, media_type, url, captured_at]
      properties:
        id:
          type: string
          format: uuid
        reservation_id:
          type: string
          format: uuid
          description: Reference to svc-reservations reservation
        trip_id:
          type: string
          format: uuid
        uploader_type:
          $ref: '#/components/schemas/UploaderType'
        uploader_id:
          type: string
          format: uuid
          description: Guide or guest ID who uploaded
        media_type:
          $ref: '#/components/schemas/MediaType'
        url:
          type: string
          format: uri
          example: "https://cdn.novatrek.example.com/media/2026/02/IMG_4821.jpg"
        thumbnail_url:
          type: string
          format: uri
          example: "https://cdn.novatrek.example.com/media/2026/02/thumb/IMG_4821.jpg"
        file_size_bytes:
          type: integer
        captured_at:
          type: string
          format: date-time
        location:
          $ref: '#/components/schemas/GeoLocation'
        tags:
          type: array
          items:
            type: string
          example: ["summit", "group-photo", "wildlife"]
        created_at:
          type: string
          format: date-time

    MediaUploadRequest:
      type: object
      required: [reservation_id, uploader_type, media_type, url, captured_at]
      properties:
        reservation_id:
          type: string
          format: uuid
        trip_id:
          type: string
          format: uuid
        uploader_type:
          $ref: '#/components/schemas/UploaderType'
        uploader_id:
          type: string
          format: uuid
        media_type:
          $ref: '#/components/schemas/MediaType'
        url:
          type: string
          format: uri
        captured_at:
          type: string
          format: date-time
        location:
          $ref: '#/components/schemas/GeoLocation'
        tags:
          type: array
          items:
            type: string

    UploaderType:
      type: string
      enum: [GUIDE, GUEST, DRONE]

    MediaType:
      type: string
      enum: [PHOTO, VIDEO, PANORAMA]

    GeoLocation:
      type: object
      required: [latitude, longitude]
      properties:
        latitude:
          type: number
          format: double
          minimum: -90
          maximum: 90
        longitude:
          type: number
          format: double
          minimum: -180
          maximum: 180
        altitude_meters:
          type: number
          format: double

    ShareLink:
      type: object
      required: [media_id, token, share_url, expires_at]
      properties:
        media_id:
          type: string
          format: uuid
        token:
          type: string
          example: "shr_a8f3b2c1d4e5"
        share_url:
          type: string
          format: uri
          example: "https://share.novatrek.example.com/m/shr_a8f3b2c1d4e5"
        expires_at:
          type: string
          format: date-time
        download_count:
          type: integer
          default: 0
        max_downloads:
          type: integer
          nullable: true

    ShareLinkRequest:
      type: object
      properties:
        expires_in_hours:
          type: integer
          default: 168
          minimum: 1
          maximum: 8760
          description: Link validity in hours (default 7 days)
        max_downloads:
          type: integer
          nullable: true
          description: Maximum number of downloads (null for unlimited)

    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
        message:
          type: string
        details:
          type: object

  responses:
    BadRequest:
      description: Invalid request payload
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid JWT token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
