openapi: 3.0.3
info:
  title: PUP - Engagement API v1
  description: >
    PUP is the universal player profile for padel. This API provides engagement functionality
    including challenge bookmarks, likes, and user interactions.
  version: 1.0.0

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

x-tagGroups:
  - name: Engagement
    tags:
      - bookmarks
      - participation
      - verification

tags:
  - name: bookmarks
    x-displayName: "Bookmarks"
    description: Operations related to challenge bookmarks
  - name: participation
    x-displayName: "Participation"
    description: Operations related to challenge participation
  - name: verification
    x-displayName: "Verification"
    description: Operations for club owners to verify challenge participations

paths:
  /engagement/v1/bookmark:
    post:
      tags:
        - bookmarks
      summary: Bookmark a challenge
      description: Creates a bookmark for a challenge for the authenticated user
      operationId: bookmarkChallenge
      security:
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BookmarkRequest'
      responses:
        '204':
          description: Bookmark created successfully
        '400':
          description: Bad request - Invalid challenge ID or request format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '404':
          description: Challenge not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/bookmark/{challenge_id}:
    delete:
      tags:
        - bookmarks
      summary: Remove a bookmark
      description: Removes a bookmark for a challenge for the authenticated user
      operationId: unbookmarkChallenge
      parameters:
        - in: path
          name: challenge_id
          description: Challenge identifier
          example: "550e8400-e29b-41d4-a716-446655440000"
          required: true
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      responses:
        '204':
          description: Bookmark removed successfully
        '400':
          description: Bad request - Invalid challenge ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '404':
          description: Bookmark not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/bookmarks:
    get:
      tags:
        - bookmarks
      summary: List bookmarks
      description: Returns all bookmarks for the authenticated user with pagination
      operationId: listBookmarks
      parameters:
        - in: query
          name: offset
          example: 0
          description: Results offset for pagination
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
        - in: query
          name: limit
          example: 20
          description: Maximum number of results per page
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 20
      security:
        - OAuth2:
            - urn:pup:engagement:read
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      responses:
        '200':
          description: List of bookmarks with pagination metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListBookmarksResponse'
        '400':
          description: Bad request - Invalid query parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/participate:
    post:
      tags:
        - participation
      summary: Start challenge participation
      description: Creates a participation record when a player starts a challenge
      operationId: startParticipation
      security:
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ParticipateRequest'
      responses:
        '204':
          description: Participation started successfully
        '400':
          description: Bad request - Invalid challenge ID or request format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '404':
          description: Challenge not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict - User already participating in this challenge
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/participate/{challenge_id}:
    get:
      tags:
        - participation
      summary: Get challenge participation
      description: Retrieves participation details for the authenticated user and specified challenge
      operationId: getParticipation
      parameters:
        - in: path
          name: challenge_id
          description: Challenge identifier
          required: true
          schema:
            type: string
          example: "550e8400-e29b-41d4-a716-446655440000"
      security:
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      responses:
        '200':
          description: Participation details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetParticipationResponse'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '404':
          description: Participation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/participate/{challenge_id}/complete:
    post:
      tags:
        - participation
      summary: Submit challenge completion
      description: Marks a challenge participation as completed by the player, awaiting verification
      operationId: submitCompletion
      parameters:
        - in: path
          name: challenge_id
          description: Challenge identifier
          example: "550e8400-e29b-41d4-a716-446655440000"
          required: true
          schema:
            type: string
      security:
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitCompletionRequest'
      responses:
        '200':
          description: Completion submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubmitCompletionResponse'
        '400':
          description: Bad request - Invalid challenge ID or request format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '404':
          description: Participation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict - Invalid participation status (not active or already completed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/participations:
    get:
      tags:
        - participation
      summary: Search user participations
      description: Returns challenge participations for the authenticated user with filtering and pagination
      operationId: searchParticipations
      parameters:
        - in: query
          name: challenge_id
          description: Filter by specific challenge
          schema:
            type: string
          example: "550e8400-e29b-41d4-a716-446655440000"
        - in: query
          name: status
          description: Filter by participation status
          schema:
            type: string
            enum: [active, completed, verified, rejected, abandoned]
          example: "active"
        - in: query
          name: search
          description: Search by challenge title or club name
          schema:
            type: string
          example: "padel"
        - in: query
          name: offset
          description: Results offset for pagination
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
          example: 0
        - in: query
          name: limit
          description: Maximum number of results per page
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 20
          example: 20
      security:
        - OAuth2:
            - urn:pup:engagement:read
        - OAuth2:
            - urn:pup:engagement:manage
        - PAT: []
      responses:
        '200':
          description: List of participations with pagination metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchParticipationsResponse'
        '400':
          description: Bad request - Invalid query parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/verify:
    post:
      tags:
        - verification
      summary: Verify challenge participation
      description: Verifies a completed challenge participation. Only club owners who own the challenge can verify.
      operationId: verifyParticipation
      security:
        - OAuth2:
            - urn:pup:engagement:verify
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyParticipationRequest'
      responses:
        '204':
          description: Participation verified successfully
        '400':
          description: Bad request - Invalid verification ID or request format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '403':
          description: Forbidden - User is not authorized to verify this participation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Verification not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict - Participation is not in a verifiable state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/verify/{verification_id}:
    get:
      tags:
        - verification
      summary: Get participation by verification ID
      description: Retrieves participation details for verification. Only club owners who own the challenge can view.
      operationId: getParticipationByVerificationId
      security:
        - OAuth2:
            - urn:pup:engagement:verify
        - PAT: []
      parameters:
        - name: verification_id
          in: path
          required: true
          schema:
            type: string
          description: The verification ID
          example: "650e8400-e29b-41d4-a716-446655440000"
      responses:
        '200':
          description: Participation retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetParticipationByVerificationIdResponse'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '403':
          description: Forbidden - User is not authorized to view this participation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Verification not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /engagement/v1/reject:
    post:
      tags:
        - verification
      summary: Reject challenge participation
      description: Rejects a completed challenge participation that does not meet requirements. Only club owners who own the challenge can reject.
      operationId: rejectParticipation
      security:
        - OAuth2:
            - urn:pup:engagement:verify
        - PAT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RejectParticipationRequest'
      responses:
        '204':
          description: Participation rejected successfully
        '400':
          description: Bad request - Invalid verification ID or request format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Missing or invalid authentication
        '403':
          description: Forbidden - User is not authorized to reject this participation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Verification not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict - Participation is not in a rejectable state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: Generic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth2 authentication
      flows:
        authorizationCode:
          authorizationUrl: https://oauth2.gopup.app/oauth2/auth
          tokenUrl: https://oauth2.gopup.app/oauth2/token
          scopes:
            urn:pup:engagement:read: Read engagement data
            urn:pup:engagement:manage: Manage engagement
            urn:pup:engagement:verify: Verify challenge participations (club owners)

    PAT:
      type: http
      scheme: bearer
      bearerFormat: PAT
      description: Personal Access Token
  schemas:
    BookmarkRequest:
      type: object
      required:
        - challenge_id
      properties:
        challenge_id:
          type: string
          description: The ID of the challenge to bookmark
          example: "550e8400-e29b-41d4-a716-446655440000"

    ParticipateRequest:
      type: object
      required:
        - challenge_id
      properties:
        challenge_id:
          type: string
          description: The ID of the challenge to participate in
          example: "550e8400-e29b-41d4-a716-446655440000"

    SubmitCompletionRequest:
      type: object
      properties:
        evidence:
          type: string
          description: Optional evidence or proof of challenge completion (e.g., screenshot URL, description)
          maxLength: 2000
          example: "Completed 10 matches. Screenshots: https://example.com/proof.jpg"

    SubmitCompletionResponse:
      type: object
      required:
        - verification_id
      properties:
        verification_id:
          type: string
          description: Unique identifier for this completion submission attempt
          example: "550e8400-e29b-41d4-a716-446655440000"

    VerifyParticipationRequest:
      type: object
      required:
        - verification_id
      properties:
        verification_id:
          type: string
          description: The verification ID generated when player submitted completion
          example: "650e8400-e29b-41d4-a716-446655440000"
        notes:
          type: string
          description: Optional notes from the verifier
          maxLength: 500
          example: "Great work! All requirements met."

    RejectParticipationRequest:
      type: object
      required:
        - verification_id
        - reason
      properties:
        verification_id:
          type: string
          description: The verification ID generated when player submitted completion
          example: "650e8400-e29b-41d4-a716-446655440000"
        reason:
          type: string
          description: Required reason for rejecting the participation
          minLength: 1
          maxLength: 500
          example: "Evidence does not meet challenge requirements"

    GetParticipationByVerificationIdResponse:
      type: object
      required:
        - participation
      properties:
        participation:
          $ref: '#/components/schemas/ParticipationDetail'

    GetParticipationResponse:
      type: object
      required:
        - participation
      properties:
        participation:
          $ref: '#/components/schemas/ParticipationInfo'

    ParticipationInfo:
      type: object
      required:
        - participation_id
        - user_id
        - challenge_id
        - status
        - attempt_count
        - max_attempts
      properties:
        participation_id:
          type: string
          description: The unique identifier for this participation
          example: "user1-challenge1"
        user_id:
          type: string
          description: The ID of the user participating
          example: "550e8400-e29b-41d4-a716-446655440000"
        challenge_id:
          type: string
          description: The ID of the challenge
          example: "650e8400-e29b-41d4-a716-446655440000"
        status:
          type: string
          description: Current status of the participation
          enum: [active, completed, verified, cancelled]
          example: "active"
        attempt_count:
          type: integer
          format: int32
          description: Number of attempts made
          example: 1
        max_attempts:
          type: integer
          format: int32
          description: Maximum allowed attempts
          example: 3
        started_at:
          type: string
          format: date-time
          nullable: true
          description: When the participation started
          example: "2024-01-15T10:30:00Z"
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: When completion was submitted (if applicable)
          example: "2024-01-20T15:45:00Z"
        verified_at:
          type: string
          format: date-time
          nullable: true
          description: When verification occurred (if applicable)
          example: "2024-01-21T09:00:00Z"
        verified_by:
          type: string
          nullable: true
          description: ID of user who verified the participation
          example: "950e8400-e29b-41d4-a716-446655440000"
        last_evidence:
          type: string
          nullable: true
          description: Last evidence submitted for completion
          example: "Screenshots: https://example.com/proof.jpg"
        verification_id:
          type: string
          nullable: true
          description: The verification ID for this participation
          example: "850e8400-e29b-41d4-a716-446655440000"

    ParticipationDetail:
      type: object
      required:
        - participation_id
        - user_id
        - challenge_id
        - challenge_title
        - challenge_club_id
        - status
        - started_at
        - verification_id
      properties:
        participation_id:
          type: string
          description: The unique identifier for this participation
          example: "user1-challenge1"
        user_id:
          type: string
          description: The ID of the user participating
          example: "550e8400-e29b-41d4-a716-446655440000"
        challenge_id:
          type: string
          description: The ID of the challenge
          example: "650e8400-e29b-41d4-a716-446655440000"
        challenge_title:
          type: string
          description: The title of the challenge
          example: "Complete 10 matches"
        challenge_club_id:
          type: string
          description: The ID of the club that owns the challenge
          example: "750e8400-e29b-41d4-a716-446655440000"
        status:
          type: string
          description: Current status of the participation
          enum: [active, completed, verified, rejected, abandoned]
          example: "completed"
        started_at:
          type: string
          format: date-time
          description: When the participation started
          example: "2024-01-15T10:30:00Z"
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: When completion was submitted (if applicable)
          example: "2024-01-20T15:45:00Z"
        verified_at:
          type: string
          format: date-time
          nullable: true
          description: When verification occurred (if applicable)
          example: "2024-01-21T09:00:00Z"
        verification_id:
          type: string
          description: The verification ID for this participation
          example: "850e8400-e29b-41d4-a716-446655440000"

    Bookmark:
      type: object
      required:
        - bookmark_id
        - user_id
        - challenge_id
        - challenge_title
        - challenge_description
        - reward
        - bookmarked_at
        - challenge_start_date
        - challenge_end_date
        - challenge_active
      properties:
        bookmark_id:
          type: string
          description: The unique identifier for this bookmark
          example: "650e8400-e29b-41d4-a716-446655440000"
        user_id:
          type: string
          description: The ID of the user who created the bookmark
          example: "750e8400-e29b-41d4-a716-446655440000"
        challenge_id:
          type: string
          description: The ID of the bookmarked challenge
          example: "550e8400-e29b-41d4-a716-446655440000"
        challenge_title:
          type: string
          description: The title of the challenge
          example: "Summer Padel Challenge"
        challenge_description:
          type: string
          description: Description of the challenge
          example: "Complete 10 matches this summer"
        reward:
          type: integer
          format: int32
          description: Reward points for completing the challenge
          example: 100
        bookmarked_at:
          type: string
          format: date-time
          description: When the bookmark was created
          example: "2024-01-15T10:30:00Z"
        challenge_start_date:
          type: string
          format: date-time
          description: When the challenge starts
          example: "2024-06-01T00:00:00Z"
        challenge_end_date:
          type: string
          format: date-time
          description: When the challenge ends
          example: "2024-08-31T23:59:59Z"
        challenge_active:
          type: boolean
          description: Whether the challenge is currently active
          example: true

    ListBookmarksResponse:
      type: object
      required:
        - hits
        - meta
      properties:
        hits:
          type: array
          description: Array of bookmark results
          items:
            $ref: '#/components/schemas/Bookmark'
        meta:
          $ref: '#/components/schemas/ResponseMetaMany'

    Participation:
      type: object
      required:
        - participation_id
        - user_id
        - challenge_id
        - challenge_title
        - club_name
        - status
      properties:
        participation_id:
          type: string
          description: Composite participation ID
          example: "user-123-challenge-456"
        user_id:
          type: string
          description: User who is participating
          example: "user-123"
        challenge_id:
          type: string
          description: Challenge ID
          example: "challenge-456"
        challenge_title:
          type: string
          description: Title of the challenge
          example: "Summer Padel Challenge"
        club_name:
          type: string
          description: Name of the club offering the challenge
          example: "Madrid Padel Club"
        status:
          type: string
          enum: [active, completed, verified, rejected, abandoned]
          description: Current participation status
          example: "active"
        started_at:
          type: string
          format: date-time
          nullable: true
          description: When participation started
          example: "2024-01-16T14:20:00Z"
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: When completion was submitted
          example: "2024-01-20T16:45:00Z"
        verified_at:
          type: string
          format: date-time
          nullable: true
          description: When participation was verified
          example: "2024-01-21T09:15:00Z"

    SearchParticipationsResponse:
      type: object
      required:
        - hits
        - meta
      properties:
        hits:
          type: array
          description: Array of participation results
          items:
            $ref: '#/components/schemas/Participation'
        meta:
          $ref: '#/components/schemas/ResponseMetaMany'

    ResponseMetaMany:
      type: object
      required:
        - pagination
      properties:
        pagination:
          $ref: '#/components/schemas/Pagination'

    Pagination:
      type: object
      required:
        - total
        - returned
        - offset
        - limit
      properties:
        total:
          type: integer
          format: int64
          description: Total number of bookmarks matching the criteria
          example: 145
        returned:
          type: integer
          format: int32
          description: Number of bookmarks returned in this response
          example: 20
        offset:
          type: integer
          format: int32
          description: Offset used in this request
          example: 0
        limit:
          type: integer
          format: int32
          description: Limit used in this request
          example: 20
        next:
          type: string
          nullable: true
          description: Relative path to the next page of results (null if no next page)
          example: "/engagement/v1/bookmarks?offset=20&limit=20"
        previous:
          type: string
          nullable: true
          description: Relative path to the previous page of results (null if no previous page)
          example: null

    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
          example: "Invalid request"
