openapi: 3.0.3
info:
  title: XMPP Chat Backend REST API
  description: |
    REST API for user management, authentication, groups, presence, and messaging in the Aoba chat backend.

    ## Authentication

    Most endpoints require authentication via JWT token. The token is provided:
    - In an HTTP-only cookie (`auth_token`) after login
    - Or via query parameter (`?token=`) for mobile apps

    ## Rate Limits

    - Standard endpoints: 100 requests/minute
    - Batch endpoints (metadata, presence): 10 requests/minute
    - Search: 30 requests/minute
  version: 2.0.0
  contact:
    name: Aoba Development Team

servers:
  - url: http://localhost:8080/api/v1
    description: Development server
  - url: https://api.aoba.example.com/api/v1
    description: Production server

tags:
  - name: Authentication
    description: User registration and authentication operations
  - name: Users
    description: User management and search operations
  - name: Metadata
    description: User metadata lookup (for JID resolution)
  - name: Groups
    description: Group chat management operations
  - name: Invitations
    description: Group invitation management
  - name: Presence
    description: User online/offline status
  - name: Messaging
    description: REST-based message operations
  - name: System
    description: Health and metrics endpoints

# Default security - JWT auth required unless overridden with empty security: []
security:
  - bearerAuth: []

paths:
  # ============================================
  # Authentication
  # ============================================
  /signup:
    post:
      tags:
        - Authentication
      summary: Register a new user account
      description: Creates a new user account with username and password
      operationId: signup
      security: []  # Public endpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignupRequest'
      responses:
        '201':
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '400':
          description: Invalid request (username/password validation failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Username already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /login:
    post:
      tags:
        - Authentication
      summary: Login to the system
      description: Authenticates user credentials and returns a JWT token
      operationId: login
      security: []  # Public endpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
      responses:
        '200':
          description: Login successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /logout:
    post:
      tags:
        - Authentication
      summary: Logout from the system
      description: Invalidates the current session token
      operationId: logout
      security:
        - bearerAuth: []
      responses:
        '204':
          description: Logout successful
        '401':
          description: Unauthorized (invalid or expired token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Users
  # ============================================
  /users/me:
    get:
      tags:
        - Users
      summary: Get current user information
      description: Retrieves information about the authenticated user
      operationId: getCurrentUser
      security:
        - bearerAuth: []
      responses:
        '200':
          description: User information retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Users
      summary: Delete current user account
      description: Deletes the authenticated user's account
      operationId: deleteCurrentUser
      security:
        - bearerAuth: []
      responses:
        '204':
          description: User account deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /users/me/name:
    patch:
      tags:
        - Users
      summary: Update username
      description: Changes the current user's username
      operationId: updateUsername
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNameRequest'
      responses:
        '200':
          description: Username updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '400':
          description: Invalid username format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Username already taken
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /users/me/password:
    patch:
      tags:
        - Users
      summary: Update password
      description: Changes the current user's password
      operationId: updatePassword
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePasswordRequest'
      responses:
        '200':
          description: Password updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdatePasswordResponse'
        '400':
          description: Invalid password (too short)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Current password incorrect
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /users/{username}:
    get:
      tags:
        - Users
      summary: Get user information by username
      description: Retrieves public information about a user
      operationId: getUserByUsername
      security:
        - bearerAuth: []
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          description: Username to retrieve
      responses:
        '200':
          description: User information retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /users/search:
    get:
      tags:
        - Users
      summary: Search for users
      description: Search for users by username with pagination
      operationId: searchUsers
      security:
        - bearerAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 100
          description: Search query (minimum 2 characters)
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Page number
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Results per page (max 100)
        - name: exclude
          in: query
          schema:
            type: string
          description: Comma-separated UUIDs to exclude (max 100)
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserSearchResponse'
        '400':
          description: Invalid search query
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Metadata
  # ============================================
  /users/metadata/{userId}:
    get:
      tags:
        - Metadata
      summary: Get user metadata by UUID
      description: Retrieves user metadata for JID resolution. Used to display usernames from UUIDs in XMPP messages.
      operationId: getUserMetadata
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: User UUID
      responses:
        '200':
          description: User metadata retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserMetadata'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /users/metadata/batch:
    post:
      tags:
        - Metadata
      summary: Batch get user metadata
      description: Retrieves metadata for multiple users in a single request (max 100)
      operationId: batchGetUserMetadata
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchMetadataRequest'
      responses:
        '200':
          description: Batch metadata retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchMetadataResponse'
        '400':
          description: Too many IDs requested (max 100)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Groups
  # ============================================
  /groups:
    post:
      tags:
        - Groups
      summary: Create a new group
      description: Creates a new group chat. Creator becomes owner. Optionally invite initial members.
      operationId: createGroup
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateGroupRequest'
      responses:
        '201':
          description: Group created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateGroupResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}:
    get:
      tags:
        - Groups
      summary: Get group information
      description: Retrieves information about a group including members
      operationId: getGroup
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      responses:
        '200':
          description: Group information retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupResponse'
        '404':
          description: Group not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not a group member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Groups
      summary: Delete a group
      description: Permanently deletes a group. Only the owner can delete.
      operationId: deleteGroup
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      responses:
        '204':
          description: Group deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only owner can delete group
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}/admin-info:
    get:
      tags:
        - Groups
      summary: Get group admin information
      description: Retrieves detailed group info for administration purposes
      operationId: getGroupAdminInfo
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      responses:
        '200':
          description: Admin info retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupAdminInfoResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not a group member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}/leave:
    post:
      tags:
        - Groups
      summary: Leave a group
      description: Leave a group. Owners must transfer ownership first.
      operationId: leaveGroup
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      responses:
        '204':
          description: Left group successfully
        '400':
          description: Owner cannot leave without transferring ownership
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group not found or not a member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}/owner:
    patch:
      tags:
        - Groups
      summary: Transfer group ownership
      description: Transfer ownership to another member. Only current owner can do this.
      operationId: transferOwnership
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferOwnershipRequest'
      responses:
        '200':
          description: Ownership transferred
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransferOwnershipResponse'
        '400':
          description: New owner is not a member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only owner can transfer ownership
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}/members:
    get:
      tags:
        - Groups
      summary: List group members
      description: Get all members of a group with their roles and online status
      operationId: listGroupMembers
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      responses:
        '200':
          description: Members list retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not a group member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - Groups
      summary: Add a member to group
      description: Directly add a member to a group. Only admins and owners can add members.
      operationId: addGroupMember
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddMemberRequest'
      responses:
        '201':
          description: Member added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupMemberResponse'
        '400':
          description: User already a member or group is full
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only admin or owner can add members
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group or user not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /groups/{groupId}/members/{username}:
    delete:
      tags:
        - Groups
      summary: Remove a member from group
      description: Remove a member from a group. Only admins and owners can remove members.
      operationId: removeGroupMember
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
        - name: username
          in: path
          required: true
          schema:
            type: string
          description: Username to remove
      responses:
        '204':
          description: Member removed successfully
        '400':
          description: Cannot remove owner
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only admin or owner can remove members
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group or member not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Groups
      summary: Update member role
      description: Update a member's role. Only owner can change roles.
      operationId: updateMemberRole
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
        - name: username
          in: path
          required: true
          schema:
            type: string
          description: Username to update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMemberRoleRequest'
      responses:
        '204':
          description: Role updated successfully
        '400':
          description: Cannot change owner's role
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only owner can change roles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group or member not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Invitations
  # ============================================
  /groups/{groupId}/invitations:
    post:
      tags:
        - Invitations
      summary: Invite user to group
      description: Send a group invitation to a user. Any member can invite.
      operationId: inviteToGroup
      security:
        - bearerAuth: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Group ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InviteRequest'
      responses:
        '201':
          description: Invitation sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvitationResponse'
        '400':
          description: User already a member or already invited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not a group member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Group or user not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /invitations:
    get:
      tags:
        - Invitations
      summary: List pending invitations
      description: Get all pending invitations for the current user
      operationId: listInvitations
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Invitations list retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvitationListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /invitations/{invitationId}/accept:
    post:
      tags:
        - Invitations
      summary: Accept invitation
      description: Accept a group invitation and join the group
      operationId: acceptInvitation
      security:
        - bearerAuth: []
      parameters:
        - name: invitationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Invitation ID
      responses:
        '200':
          description: Invitation accepted, joined group
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AcceptInvitationResponse'
        '400':
          description: Invitation already responded to
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not the invitee
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Invitation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /invitations/{invitationId}/deny:
    post:
      tags:
        - Invitations
      summary: Deny invitation
      description: Decline a group invitation
      operationId: denyInvitation
      security:
        - bearerAuth: []
      parameters:
        - name: invitationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Invitation ID
      responses:
        '200':
          description: Invitation denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DenyInvitationResponse'
        '400':
          description: Invitation already responded to
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Not the invitee
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Invitation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Presence
  # ============================================
  /presence/{username}:
    get:
      tags:
        - Presence
      summary: Get user presence
      description: Get online/offline status for a user by username
      operationId: getPresence
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          description: Username
      responses:
        '200':
          description: Presence retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetPresenceResponse'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /presence/batch:
    post:
      tags:
        - Presence
      summary: Batch get presence by username
      description: Get presence for multiple users by username (max 100)
      operationId: batchGetPresence
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchPresenceRequest'
      responses:
        '200':
          description: Batch presence retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchPresenceResponse'
        '400':
          description: Too many usernames (max 100)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /presence/id/{userId}:
    get:
      tags:
        - Presence
      summary: Get user presence by UUID
      description: Get online/offline status for a user by UUID
      operationId: getPresenceById
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: User UUID
      responses:
        '200':
          description: Presence retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetPresenceByIdResponse'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /presence/batch/id:
    post:
      tags:
        - Presence
      summary: Batch get presence by UUID
      description: Get presence for multiple users by UUID (max 100)
      operationId: batchGetPresenceById
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchPresenceByIdRequest'
      responses:
        '200':
          description: Batch presence retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchPresenceByIdResponse'
        '400':
          description: Too many IDs (max 100)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # Messaging
  # ============================================
  /messages:
    post:
      tags:
        - Messaging
      summary: Send a message via REST
      description: Send a message to a user or group via REST API (alternative to XMPP)
      operationId: sendMessage
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageRequest'
      responses:
        '201':
          description: Message sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
        '400':
          description: Invalid message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Recipient not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /messages/history:
    get:
      tags:
        - Messaging
      summary: Get conversation history
      description: Retrieve message history with pagination
      operationId: getMessageHistory
      security:
        - bearerAuth: []
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Number of messages to retrieve
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
          description: Offset for pagination
      responses:
        '200':
          description: History retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConversationHistoryResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # ============================================
  # System
  # ============================================
  /health:
    get:
      tags:
        - System
      summary: Health check
      description: Check if the service is healthy
      operationId: healthCheck
      security: []  # Public endpoint
      responses:
        '200':
          description: Service is healthy
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '503':
          description: Service is unhealthy
          content:
            text/plain:
              schema:
                type: string
                example: 'UNHEALTHY: database connection failed'

  /metrics:
    get:
      tags:
        - System
      summary: Prometheus metrics
      description: Get Prometheus-formatted metrics for monitoring
      operationId: getMetrics
      security: []  # Public endpoint for Prometheus scraping
      responses:
        '200':
          description: Metrics in Prometheus format
          content:
            text/plain:
              schema:
                type: string
                description: Prometheus text format metrics

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

  schemas:
    # ============================================
    # Authentication Schemas
    # ============================================
    SignupRequest:
      type: object
      required:
        - username
        - password
      properties:
        username:
          type: string
          minLength: 3
          maxLength: 255
          pattern: '^[a-zA-Z0-9_-]+$'
          description: Username (alphanumeric with _ or -)
          example: alice
        password:
          type: string
          minLength: 8
          description: Password (at least 8 characters)
          example: password123

    LoginRequest:
      type: object
      required:
        - username
        - password
      properties:
        username:
          type: string
          example: alice
        password:
          type: string
          example: password123

    LoginResponse:
      type: object
      properties:
        token:
          type: string
          description: JWT authentication token
          example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
        expires_at:
          type: string
          format: date-time
          description: Token expiration timestamp
          example: '2025-11-28T10:30:00Z'
        user:
          $ref: '#/components/schemas/UserResponse'

    # ============================================
    # User Schemas
    # ============================================
    UserResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: User ID
        username:
          type: string
          description: Username
          example: alice
        created_at:
          type: string
          format: date-time
          description: Account creation timestamp
          example: '2025-11-27T10:30:00Z'

    UpdateNameRequest:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          minLength: 3
          maxLength: 255
          pattern: '^[a-zA-Z0-9_-]+$'
          description: New username
          example: alice_new

    UpdatePasswordRequest:
      type: object
      required:
        - current_password
        - new_password
      properties:
        current_password:
          type: string
          description: Current password for verification
        new_password:
          type: string
          minLength: 8
          description: New password (at least 8 characters)

    UpdatePasswordResponse:
      type: object
      properties:
        message:
          type: string
          example: Password updated successfully

    UserSearchResponse:
      type: object
      properties:
        users:
          type: array
          items:
            $ref: '#/components/schemas/UserSearchResult'
        total:
          type: integer
          description: Total matching users
          example: 42
        page:
          type: integer
          description: Current page
          example: 1
        limit:
          type: integer
          description: Results per page
          example: 20
        total_pages:
          type: integer
          description: Total number of pages
          example: 3

    UserSearchResult:
      type: object
      properties:
        id:
          type: string
          format: uuid
        username:
          type: string
          example: alice

    # ============================================
    # Metadata Schemas
    # ============================================
    UserMetadata:
      type: object
      properties:
        id:
          type: string
          format: uuid
        username:
          type: string
          example: alice
        domain:
          type: string
          description: XMPP domain
          example: aoba.chat
        displayName:
          type: string
          nullable: true
          description: Display name (if set)
        avatarUrl:
          type: string
          format: uri
          nullable: true
          description: Avatar URL (if set)

    BatchMetadataRequest:
      type: object
      required:
        - userIds
      properties:
        userIds:
          type: array
          items:
            type: string
            format: uuid
          maxItems: 100
          description: List of user UUIDs (max 100)

    BatchMetadataResponse:
      type: object
      properties:
        users:
          type: array
          items:
            $ref: '#/components/schemas/UserMetadata'

    # ============================================
    # Group Schemas
    # ============================================
    CreateGroupRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Group name
          example: Engineering Team
        initial_members:
          type: array
          items:
            type: string
          description: Usernames to invite (will receive invitations)
          example: ["bob", "charlie"]

    CreateGroupResponse:
      type: object
      properties:
        group:
          $ref: '#/components/schemas/GroupSummary'
        invitations_sent:
          type: array
          items:
            $ref: '#/components/schemas/InvitationSentStatus'
          description: Status of invitations sent to initial_members

    GroupResponse:
      type: object
      properties:
        group_id:
          type: string
          format: uuid
          description: Group ID
        name:
          type: string
          description: Group name
          example: Engineering Team
        owner_username:
          type: string
          description: Group owner username
          example: alice
        members:
          type: array
          items:
            $ref: '#/components/schemas/GroupMemberResponse'
        created_at:
          type: string
          format: date-time
          description: Group creation timestamp
          example: '2025-11-27T11:00:00Z'

    GroupSummary:
      type: object
      properties:
        group_id:
          type: string
          format: uuid
        name:
          type: string
          example: Engineering Team
        member_count:
          type: integer
          example: 5
        created_at:
          type: string
          format: date-time

    GroupMemberResponse:
      type: object
      properties:
        username:
          type: string
          example: alice
        role:
          type: string
          enum: [owner, admin, member]
          example: owner
        joined_at:
          type: string
          format: date-time

    GroupMemberWithStatus:
      type: object
      properties:
        user_id:
          type: string
          format: uuid
        username:
          type: string
          example: alice
        role:
          type: string
          enum: [owner, admin, member]
          example: member
        joined_at:
          type: string
          format: date-time
        is_online:
          type: boolean
          description: Whether the user is currently online

    MemberListResponse:
      type: object
      properties:
        members:
          type: array
          items:
            $ref: '#/components/schemas/GroupMemberWithStatus'
        total_count:
          type: integer
          example: 5

    AddMemberRequest:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          description: Username to add
          example: bob
        role:
          type: string
          enum: [admin, member]
          default: member
          description: Role for the new member

    UpdateMemberRoleRequest:
      type: object
      required:
        - role
      properties:
        role:
          type: string
          enum: [admin, member]
          description: New role (owner cannot be set this way)

    GroupAdminInfoResponse:
      type: object
      properties:
        group:
          $ref: '#/components/schemas/GroupSummary'
        members:
          type: array
          items:
            $ref: '#/components/schemas/GroupMemberWithStatus'
        currentUserRole:
          type: string
          enum: [owner, admin, member]
        ownerCount:
          type: integer
          example: 1
        adminCount:
          type: integer
          example: 2

    TransferOwnershipRequest:
      type: object
      required:
        - newOwner
      properties:
        newOwner:
          type: string
          description: Username of new owner

    TransferOwnershipResponse:
      type: object
      properties:
        message:
          type: string
          example: Ownership transferred successfully
        previousOwner:
          type: string
          example: alice
        newOwner:
          type: string
          example: bob

    # ============================================
    # Invitation Schemas
    # ============================================
    InviteRequest:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          description: Username to invite

    InvitationResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
        group_id:
          type: string
          format: uuid
        group_name:
          type: string
        inviter_username:
          type: string
        invitee_username:
          type: string
        status:
          type: string
          enum: [pending, accepted, denied]
        created_at:
          type: string
          format: date-time
        responded_at:
          type: string
          format: date-time
          nullable: true

    InvitationListResponse:
      type: object
      properties:
        invitations:
          type: array
          items:
            $ref: '#/components/schemas/InvitationResponse'
        count:
          type: integer

    InvitationSentStatus:
      type: object
      properties:
        username:
          type: string
        status:
          type: string
          enum: [pending, failed]
        error:
          type: string
          description: Error message if failed

    AcceptInvitationResponse:
      type: object
      properties:
        message:
          type: string
          example: Invitation accepted
        group:
          $ref: '#/components/schemas/GroupSummary'
        membership:
          $ref: '#/components/schemas/MembershipResponse'

    DenyInvitationResponse:
      type: object
      properties:
        message:
          type: string
          example: Invitation denied
        invitation_id:
          type: string
          format: uuid

    MembershipResponse:
      type: object
      properties:
        role:
          type: string
          enum: [owner, admin, member]
        joined_at:
          type: string
          format: date-time

    # ============================================
    # Presence Schemas
    # ============================================
    PresenceInfo:
      type: object
      properties:
        status:
          type: string
          enum: [online, offline]
        lastSeen:
          type: string
          format: date-time
          nullable: true
          description: Last seen timestamp (null if online)

    GetPresenceResponse:
      type: object
      properties:
        username:
          type: string
        status:
          type: string
          enum: [online, offline]
        lastSeen:
          type: string
          format: date-time
          nullable: true

    GetPresenceByIdResponse:
      type: object
      properties:
        userId:
          type: string
          format: uuid
        status:
          type: string
          enum: [online, offline]
        lastSeen:
          type: string
          format: date-time
          nullable: true

    BatchPresenceRequest:
      type: object
      required:
        - usernames
      properties:
        usernames:
          type: array
          items:
            type: string
          maxItems: 100

    BatchPresenceResponse:
      type: object
      properties:
        presence:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/PresenceInfo'
          description: Map of username to presence info

    BatchPresenceByIdRequest:
      type: object
      required:
        - userIds
      properties:
        userIds:
          type: array
          items:
            type: string
            format: uuid
          maxItems: 100

    BatchPresenceByIdResponse:
      type: object
      properties:
        presence:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/PresenceInfo'
          description: Map of userId to presence info

    # ============================================
    # Messaging Schemas
    # ============================================
    SendMessageRequest:
      type: object
      required:
        - to
        - content
      properties:
        to:
          type: string
          description: Recipient username or group ID
          example: bob
        content:
          type: string
          maxLength: 10240
          description: Message content (max 10KB)
        type:
          type: string
          enum: [chat, groupchat]
          default: chat
          description: Message type

    MessageResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
        from_id:
          type: string
          format: uuid
          description: Sender UUID
        to_id:
          type: string
          format: uuid
          description: Recipient UUID
        from:
          type: string
          description: Sender username
        to:
          type: string
          description: Recipient username/group
        content:
          type: string
        type:
          type: string
          enum: [chat, groupchat]
        timestamp:
          type: string
          format: date-time
        status:
          type: string
          enum: [sent, delivered, read]

    ConversationHistoryResponse:
      type: object
      properties:
        messages:
          type: array
          items:
            $ref: '#/components/schemas/MessageResponse'
        total:
          type: integer
          description: Total messages in conversation
        offset:
          type: integer
        limit:
          type: integer

    # ============================================
    # Common Schemas
    # ============================================
    Error:
      type: object
      properties:
        error:
          type: string
          description: HTTP status text
          example: Unauthorized
        message:
          type: string
          description: Detailed error message
          example: Invalid username or password
