openapi: 3.0.3
info:
  title: PUP - Reward API v1
  description: >
    PUP is the universal player profile for padel. This API provides reward catalog management functionality.
  version: 1.0.0

servers:
  - url: https://api.gopup.app
    description: Development server

x-tagGroups:
  - name: Reward
    tags:
      - rewards

tags:
  - name: rewards
    x-displayName: "Rewards"
    description: Operations related to reward catalog management

paths:
  /reward/v1/reward/new:
    post:
      tags:
        - rewards
      summary: Create a new reward
      description: Creates a new loyalty reward with the provided information
      operationId: createReward
      security:
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRewardRequest'
      responses:
        '201':
          description: Reward created successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                properties:
                  id:
                    type: string
                    example: "550e8400-e29b-41d4-a716-446655440000"
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Unprocessable entity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /reward/v1/reward/search:
    get:
      tags:
        - rewards
      summary: Search rewards
      description: Search for loyalty rewards with optional filters
      operationId: searchRewards
      parameters:
        - in: query
          name: term
          example: Premium paddle
          schema:
            type: string
        - in: query
          name: price_lower_than
          example: 5000
          schema:
            type: integer
            format: int32
        - in: query
          name: price_greater_than
          example: 1000
          schema:
            type: integer
            format: int32
        - in: query
          name: offset
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
        - in: query
          name: limit
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 20
      security:
        - OAuth2:
            - urn:pup:reward:read
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      responses:
        '200':
          description: The search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchRewardsResponse'
        '401':
          description: Unauthorized
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /reward/v1/reward/{id}:
    get:
      tags:
        - rewards
      summary: Get reward by ID
      description: Returns the reward information for the specified ID
      operationId: getRewardById
      parameters:
        - in: path
          name: id
          required: true
          description: Reward identifier
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:reward:read
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      responses:
        '200':
          description: The reward information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RewardResponse'
        '404':
          description: Reward not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "Reward not found"
        '401':
          description: Unauthorized
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      tags:
        - rewards
      summary: Update a reward
      description: Updates an existing reward with the provided fields. Only included fields are updated.
      operationId: updateReward
      parameters:
        - in: path
          name: id
          required: true
          description: Reward identifier
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRewardRequest'
      responses:
        '204':
          description: Reward updated successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Reward not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "Reward not found"
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /reward/v1/reward/{id}/picture:
    post:
      tags:
        - rewards
      summary: Add picture to reward gallery
      description: Adds a new picture to the reward's gallery
      operationId: addPictureToReward
      parameters:
        - in: path
          name: id
          required: true
          description: Reward identifier
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                picture:
                  type: string
                  format: binary
                  description: The picture file to upload (JPG, PNG, WebP)
              required:
                - picture
      responses:
        '204':
          description: Picture added successfully
        '400':
          description: Invalid input or file too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Reward not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Adding picture failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /reward/v1/reward/{id}/picture/{picture_id}:
    delete:
      tags:
        - rewards
      summary: Remove picture from reward gallery
      description: Removes a picture from the reward's gallery
      operationId: removePictureFromReward
      parameters:
        - in: path
          name: id
          required: true
          description: Reward identifier
          schema:
            type: string
        - in: path
          name: picture_id
          required: true
          description: Picture identifier
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      responses:
        '204':
          description: Picture removed successfully
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Reward or picture not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /reward/v1/reward/{id}/main-picture:
    put:
      tags:
        - rewards
      summary: Elect main picture
      description: Sets a picture from the gallery as the main reward picture
      operationId: electMainPicture
      parameters:
        - in: path
          name: id
          required: true
          description: Reward identifier
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:reward:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - picture_id
              properties:
                picture_id:
                  type: string
                  description: ID of the picture to elect as main
      responses:
        '204':
          description: Main picture elected successfully
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden - insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Reward or picture not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          example: "An error message"
      description: Generic error response

    CreateRewardRequest:
      type: object
      required:
        - title
        - description
        - price
        - details
      properties:
        title:
          type: string
          minLength: 3
          maxLength: 100
          example: "Premium Paddle"
        description:
          type: string
          minLength: 10
          maxLength: 500
          example: "A high-quality paddle for professional players"
        price:
          type: integer
          format: int32
          minimum: 0
          example: 2500
          description: Price in cents
        details:
          type: string
          minLength: 10
          maxLength: 2000
          example: "Made from carbon fiber with a soft touch surface"
        claim_instructions:
          type: string
          nullable: true
          description: Optional instructions for how to claim the reward after purchase
          example: "Pick up the racket at the front desk at HOME Padel"
    UpdateRewardRequest:
      type: object
      properties:
        title:
          type: string
          minLength: 3
          maxLength: 100
        description:
          type: string
          minLength: 10
          maxLength: 500
        price:
          type: integer
          format: int32
          minimum: 0
          description: Price in cents
        details:
          type: string
          minLength: 10
          maxLength: 2000
        claim_instructions:
          type: string
          nullable: true
          description: Optional instructions for how to claim the reward after purchase

    Picture:
      type: object
      required:
        - id
        - url
      properties:
        id:
          type: string
          description: Picture ID (file identifier)
          example: "ACV7XKDQWA63CSDEPLW3QLY6HFWRSGORBSYOJK5XLLUEMGMWI6DJ5S4FTJVMW6EZ"
        url:
          type: string
          format: uri
          example: "https://cdn.example.com/pictures/123.jpg"

    Reward:
      type: object
      required:
        - id
        - title
        - description
        - price
        - details
        - gallery
      properties:
        id:
          type: string
          example: "550e8400-e29b-41d4-a716-446655440000"
        title:
          type: string
          example: "Premium Paddle"
        description:
          type: string
          example: "A high-quality paddle for professional players"
        price:
          type: integer
          format: int32
          example: 2500
          description: Price in cents
        details:
          type: string
          example: "Made from carbon fiber with a soft touch surface"
        gallery:
          type: array
          items:
            $ref: '#/components/schemas/Picture'
        main_picture:
          allOf:
            - $ref: '#/components/schemas/Picture'
          nullable: true
        claim_instructions:
          type: string
          nullable: true
          description: Optional instructions for how to claim the reward after purchase
          example: "Show this code at the front desk: CODE-123"

    RewardResponse:
      type: object
      required:
        - reward
      properties:
        reward:
          $ref: '#/components/schemas/Reward'

    ResponseMetaMany:
      type: object
      required:
        - pagination
      properties:
        pagination:
          type: object
          required:
            - total
            - returned
            - offset
            - limit
          properties:
            total:
              type: integer
              format: int64
              description: Total number of items
              example: 145
            returned:
              type: integer
              format: int32
              description: Number of items in this response
              example: 20
            offset:
              type: integer
              format: int32
              description: Offset used in the request
              example: 0
            limit:
              type: integer
              format: int32
              description: Limit used in the request
              example: 20
            next:
              type: string
              nullable: true
              description: Relative path to next page
              example: "/reward/v1/reward/search?offset=20&limit=20"
            previous:
              type: string
              nullable: true
              description: Relative path to previous page
              example: "/reward/v1/reward/search?offset=0&limit=20"

    SearchRewardsResponse:
      type: object
      required:
        - hits
        - meta
      properties:
        hits:
          type: array
          items:
            $ref: '#/components/schemas/Reward'
        meta:
          $ref: '#/components/schemas/ResponseMetaMany'

  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://oauth2.gopup.app/oauth2/auth
          tokenUrl: https://oauth2.gopup.app/oauth2/token
          scopes:
            urn:pup:reward:read: Read loyalty rewards
            urn:pup:reward:manage: Manage loyalty rewards

    PAT:
      type: http
      scheme: bearer
      bearerFormat: PAT
      description: Personal Access Token
