openapi: 3.0.3
info:
  title: BDL Picks API
  version: "1.0"
  description: |
    Free sports picks competition API. Make predictions on upcoming games, track
    your record, and compete on the public leaderboard.

    **Authentication:** Include your BallDontLie API key via the
    `Authorization: Bearer <API_KEY>` header. Some endpoints are public and do
    not require authentication.

    **Base URL:** `https://api.balldontlie.io`

servers:
  - url: https://api.balldontlie.io

paths:
  /picks/v1/games:
    get:
      summary: Get upcoming games with odds
      description: |
        Returns upcoming games with current odds rendered as SVG strings. If
        authenticated, includes the user's existing picks for each game.
      tags: [Games]
      security:
        - BearerAuth: []
        - {}
      parameters:
        - name: sport
          in: query
          schema:
            type: string
            default: nba
          description: Sport key (e.g. `nba`)
      responses:
        "200":
          description: List of upcoming games
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PicksGame"

  /picks/v1/picks:
    post:
      summary: Create a pick
      description: |
        Submit a prediction for a game. Odds and line values are snapshotted at
        the time of submission. One pick per game per market.
      tags: [Picks]
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [game_id, market, picked_side]
              properties:
                game_id:
                  type: integer
                  description: The game ID from the games endpoint
                market:
                  type: string
                  enum: [spread, moneyline, over_under]
                picked_side:
                  type: string
                  enum: [home, away, over, under]
                  description: |
                    `home` or `away` for spread/moneyline; `over` or `under` for
                    over_under.
                sport:
                  type: string
                  default: nba
      responses:
        "201":
          description: Pick created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Pick"
        "400":
          description: Validation error
        "401":
          description: Authentication required
        "409":
          description: Duplicate pick (already picked this game + market)

    get:
      summary: Get pick history
      description: Returns the authenticated user's picks with optional filters.
      tags: [Picks]
      security:
        - BearerAuth: []
      parameters:
        - name: sport
          in: query
          schema:
            type: string
        - name: result
          in: query
          schema:
            type: string
            enum: [win, loss, push, pending]
        - name: per_page
          in: query
          schema:
            type: integer
            default: 25
        - name: cursor
          in: query
          schema:
            type: integer
          description: Cursor for pagination (pick ID)
      responses:
        "200":
          description: Paginated list of picks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Pick"
                  meta:
                    $ref: "#/components/schemas/CursorMeta"
        "401":
          description: Authentication required

  /picks/v1/picks/summary:
    get:
      summary: Get pick summary
      description: Aggregate win/loss/push/pending stats for the authenticated user.
      tags: [Picks]
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Pick summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PicksSummary"
        "401":
          description: Authentication required

  /picks/v1/leaderboard:
    get:
      summary: Get leaderboard
      description: |
        Global leaderboard ranked by win percentage. Users need at least 15
        resolved picks to qualify.
      tags: [Leaderboard]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 25
            maximum: 100
      responses:
        "200":
          description: Paginated leaderboard
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaderboardEntry"
                  meta:
                    $ref: "#/components/schemas/PageMeta"

  /picks/v1/profile:
    get:
      summary: Get own profile
      description: Returns the authenticated user's profile and stats.
      tags: [Profile]
      security:
        - BearerAuth: []
      responses:
        "200":
          description: User profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PicksProfile"
        "401":
          description: Authentication required

    put:
      summary: Update username
      description: Change the authenticated user's display name.
      tags: [Profile]
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username]
              properties:
                username:
                  type: string
                  minLength: 3
                  maxLength: 20
                  pattern: "^[a-zA-Z0-9_-]+$"
      responses:
        "200":
          description: Updated profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PicksProfile"
        "400":
          description: Invalid username
        "401":
          description: Authentication required
        "409":
          description: Username taken

  /picks/v1/profiles/{username}:
    get:
      summary: Get public profile
      description: |
        View another user's profile, stats, and recent resolved picks.
      tags: [Profile]
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Public profile with recent picks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    allOf:
                      - $ref: "#/components/schemas/PicksProfile"
                      - type: object
                        properties:
                          picks:
                            type: array
                            items:
                              $ref: "#/components/schemas/Pick"
        "404":
          description: User not found

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: BallDontLie API key

  schemas:
    PicksTeam:
      type: object
      properties:
        abbreviation:
          type: string
          example: LAL
        name:
          type: string
          example: Lakers
        city:
          type: string
          example: Los Angeles

    PicksGameOdds:
      type: object
      description: SVG-rendered odds strings for each market side
      properties:
        spread_home:
          type: string
        spread_away:
          type: string
        moneyline_home:
          type: string
        moneyline_away:
          type: string
        total_over:
          type: string
        total_under:
          type: string

    PicksGame:
      type: object
      properties:
        id:
          type: integer
        home_team:
          $ref: "#/components/schemas/PicksTeam"
        visitor_team:
          $ref: "#/components/schemas/PicksTeam"
        datetime:
          type: string
          format: date-time
        odds:
          $ref: "#/components/schemas/PicksGameOdds"
        picks:
          type: object
          nullable: true
          description: |
            Present only when authenticated. Contains the user's existing picks
            for this game, keyed by market.
          properties:
            spread:
              $ref: "#/components/schemas/UserGamePick"
            moneyline:
              $ref: "#/components/schemas/UserGamePick"
            over_under:
              $ref: "#/components/schemas/UserGamePick"

    UserGamePick:
      type: object
      nullable: true
      properties:
        id:
          type: integer
        picked_side:
          type: string
        picked_odds:
          type: integer
        line_value:
          type: string
          nullable: true
        odds_svg:
          type: string

    Pick:
      type: object
      properties:
        id:
          type: integer
        sport:
          type: string
        market:
          type: string
          enum: [spread, moneyline, over_under]
        picked_side:
          type: string
        line_value:
          type: string
          nullable: true
        picked_odds:
          type: integer
        result:
          type: string
          nullable: true
          enum: [win, loss, push, null]
        created_at:
          type: string
          format: date-time
        game:
          type: object
          nullable: true
          properties:
            home_team:
              type: object
              properties:
                abbreviation:
                  type: string
            visitor_team:
              type: object
              properties:
                abbreviation:
                  type: string
            datetime:
              type: string
              format: date-time

    PicksSummary:
      type: object
      properties:
        total_picks:
          type: integer
        wins:
          type: integer
        losses:
          type: integer
        pushes:
          type: integer
        pending:
          type: integer
        win_rate:
          type: number
          nullable: true
          description: Wins / (wins + losses + pushes). Null if no resolved picks.

    LeaderboardEntry:
      type: object
      properties:
        rank:
          type: integer
        username:
          type: string
        wins:
          type: integer
        losses:
          type: integer
        pushes:
          type: integer
        win_rate:
          type: number

    PicksProfile:
      type: object
      properties:
        username:
          type: string
        summary:
          $ref: "#/components/schemas/PicksSummary"

    CursorMeta:
      type: object
      properties:
        next_cursor:
          type: integer
          nullable: true

    PageMeta:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer
