openapi: 3.0.3
info:
  title: PubfuseNebula API
  version: 0.1.0
servers:
  - url: http://localhost:8080
paths:
  /:
    get:
      operationId: rootOk
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                type: string
  /health:
    get:
      operationId: health
      responses:
        '200':
          description: Healthy
  /api/streams:
    post:
      operationId: createStream
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StreamCreateRequest'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StreamResponse'
  /api/streams/{id}:
    get:
      operationId: getStream
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Stream
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StreamResponse'
  /api/streams/{id}/tokens:
    post:
      operationId: mintToken
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
  /api/streams/webhook:
    post:
      operationId: webhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Accepted
  /ws/streams/{id}/chat:
    get:
      operationId: chatWS
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '101':
          description: Switching Protocols

  # Broadcast Management API
  /api/broadcasts:
    post:
      operationId: createBroadcast
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBroadcastRequest'
      responses:
        '200':
          description: Broadcast created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastResponse'
    get:
      operationId: getAllBroadcasts
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [pending, active, paused, completed, error, cancelled, private]
        - in: query
          name: visibility
          schema:
            type: string
            enum: [public, private]
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of broadcasts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BroadcastResponse'

  /api/broadcasts/active:
    get:
      operationId: getActiveBroadcasts
      parameters:
        - in: query
          name: visibility
          schema:
            type: string
            enum: [public, private]
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: Active broadcasts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BroadcastResponse'

  /api/broadcasts/public:
    get:
      operationId: getPublicBroadcasts
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Public broadcasts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BroadcastResponse'

  /api/broadcasts/featured:
    get:
      operationId: getFeaturedBroadcasts
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 20
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Featured broadcasts
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BroadcastResponse'

  /api/broadcasts/search:
    get:
      operationId: searchBroadcasts
      parameters:
        - in: query
          name: q
          schema:
            type: string
        - in: query
          name: tags
          schema:
            type: array
            items:
              type: string
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BroadcastResponse'

  /api/broadcasts/{id}:
    get:
      operationId: getBroadcast
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Broadcast details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastResponse'
    patch:
      operationId: updateBroadcast
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBroadcastRequest'
      responses:
        '200':
          description: Broadcast updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastResponse'
    delete:
      operationId: deleteBroadcast
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Broadcast deleted

  # Sessions API (Mobile App Compatible)
  /api/sessions:
    get:
      operationId: getSessionsDirect
      summary: Get available sessions (mobile app endpoint)
      description: Direct endpoint for mobile apps to browse available streaming sessions
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [pending, active, paused, completed, error, cancelled, private]
            default: active
        - in: query
          name: visibility
          schema:
            type: string
            enum: [public, private]
            default: public
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Available sessions for mobile apps
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalSessionResponse'
        '404':
          description: Endpoint not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: boolean
                    example: true
                  reason:
                    type: string
                    example: "Not Found"

  # External Device API
  /api/external/sessions:
    get:
      operationId: getAvailableSessions
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [pending, active, paused, completed, error, cancelled, private]
            default: active
        - in: query
          name: visibility
          schema:
            type: string
            enum: [public, private]
            default: public
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Available sessions for external devices
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalSessionResponse'

  /api/external/sessions/public:
    get:
      operationId: getPublicSessions
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Public sessions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalSessionResponse'

  /api/external/sessions/featured:
    get:
      operationId: getFeaturedSessions
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 20
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Featured sessions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalSessionResponse'

  /api/external/sessions/search:
    get:
      operationId: searchSessions
      parameters:
        - in: query
          name: q
          schema:
            type: string
        - in: query
          name: tags
          schema:
            type: array
            items:
              type: string
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExternalSessionResponse'

  /api/external/sessions/{id}/join-info:
    get:
      operationId: getJoinInfo
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Join information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JoinInfoResponse'

  # WebSocket Endpoints
  /ws/streams/{id}/watch:
    get:
      operationId: watchWS
      summary: Unified WebSocket for video streaming, chat, reactions, and ad synchronization
      description: |
        WebSocket endpoint for real-time streaming features including WebRTC video streaming, 
        chat messages, live reactions, and ad synchronization. 
        
        **Connection URL:** `ws://pubfuse.com/ws/streams/{id}/watch` or `wss://pubfuse.com/ws/streams/{id}/watch`
        
        **Message Protocol:** All messages are JSON format with the following structure:
        ```json
        {
          "type": "message-type",
          "clientId": "unique-client-identifier",
          "message": "optional-content",
          "timestamp": 1234567890.123
        }
        ```
        
        **Supported Message Types:**
        - `viewer-hello`: Join stream as viewer
        - `welcome`: Server confirmation of join
        - `chat`: Chat messages
        - `reaction`: Live reactions (heart, like, etc.)
        - `sdp-offer`: WebRTC SDP offer for video streaming
        - `sdp-answer`: WebRTC SDP answer for video streaming
        - `ice-candidate`: WebRTC ICE candidate for NAT traversal
        - `ad-trigger`: Synchronized ad trigger
        - `ping`: Heartbeat to maintain connection
        - `pong`: Heartbeat response
        
        **Usage Flow:**
        1. Connect to WebSocket endpoint with valid stream ID
        2. Send `viewer-hello` message to join stream
        3. Receive `welcome` confirmation
        4. Handle incoming messages (chat, reactions, video, etc.)
        5. Send `ping` every 30 seconds to maintain connection
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the broadcast session (obtain from /api/sessions or /api/metrics/dashboard)
      responses:
        '101':
          description: WebSocket connection established for video streaming, chat, reactions, and ad synchronization
        '400':
          description: Invalid stream ID or connection failed
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: boolean
                    example: true
                  reason:
                    type: string
                    example: "Invalid stream ID"
        '404':
          description: Stream not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: boolean
                    example: true
                  reason:
                    type: string
                    example: "Stream not found"

  # Metrics and Analytics API
  /api/metrics/dashboard:
    get:
      operationId: getDashboardMetrics
      responses:
        '200':
          description: Dashboard metrics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionMetricsResponse'

  /api/metrics/realtime:
    get:
      operationId: getRealTimeMetrics
      responses:
        '200':
          description: Real-time metrics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RealTimeMetricsResponse'

  /api/metrics/historical:
    get:
      operationId: getHistoricalMetrics
      parameters:
        - in: query
          name: startDate
          schema:
            type: string
            format: date-time
        - in: query
          name: endDate
          schema:
            type: string
            format: date-time
        - in: query
          name: granularity
          schema:
            type: string
            enum: [hour, day, week, month]
        - in: query
          name: metrics
          schema:
            type: array
            items:
              type: string
              enum: [sessions, viewers, revenue, sparks]
      responses:
        '200':
          description: Historical metrics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoricalMetricsResponse'

  /api/metrics/platform-health:
    get:
      operationId: getPlatformHealth
      responses:
        '200':
          description: Platform health status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformHealth'

components:
  schemas:
    StreamCreateRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
    StreamResponse:
      type: object
      required: [id, title]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
    TokenResponse:
      type: object
      required: [token]
      properties:
        token:
          type: string

    # Broadcast Management Schemas
    CreateBroadcastRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
        visibility:
          type: string
          enum: [public, private]
        aiIntent:
          type: string
        tags:
          type: array
          items:
            type: string
        maxViewers:
          type: integer
        metadata:
          type: object
          additionalProperties:
            type: string

    BroadcastResponse:
      type: object
      required: [id, title, userId, status, visibility, createdAt, viewerCount, maxViewers, isFeatured]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        userId:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, active, paused, completed, error, cancelled, private]
        visibility:
          type: string
          enum: [public, private]
        createdAt:
          type: string
          format: date-time
        startedAt:
          type: string
          format: date-time
        endedAt:
          type: string
          format: date-time
        viewerCount:
          type: integer
        maxViewers:
          type: integer
        rtmpUrl:
          type: string
          nullable: true
        hlsUrl:
          type: string
          nullable: true
        webrtcEndpoint:
          type: string
          nullable: true
        metadata:
          type: object
          additionalProperties:
            type: string
        aiIntent:
          type: string
        tags:
          type: array
          items:
            type: string
        isFeatured:
          type: boolean
        totalTokensEarned:
          type: integer
        totalDiamondsReceived:
          type: integer
        totalSparksReceived:
          type: integer
        totalRevenueUSD:
          type: number
        totalReactions:
          type: integer
        peakViewerCount:
          type: integer
        averageViewerCount:
          type: number
        totalWatchTimeMinutes:
          type: number
        moderationStatus:
          type: string
          enum: [pending, approved, flagged, rejected, auto_approved]
        qualityScore:
          type: number
        errorCount:
          type: integer
        maxGuests:
          type: integer
        currentGuests:
          type: integer
        subscriptionTierRequired:
          type: string
          enum: [free, basic, pro, premium]
        accessTokensRequired:
          type: integer

    UpdateBroadcastRequest:
      type: object
      properties:
        status:
          type: string
          enum: [pending, active, paused, completed, error, cancelled, private]
        viewerCount:
          type: integer
        metadata:
          type: object
          additionalProperties:
            type: string
        aiIntent:
          type: string
        tags:
          type: array
          items:
            type: string
        isFeatured:
          type: boolean

    # External Device Schemas
    ExternalSessionResponse:
      type: object
      required: [id, title, status, visibility, viewerCount, maxViewers, joinUrl, isFeatured]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        status:
          type: string
          enum: [pending, active, paused, completed, error, cancelled, private]
        visibility:
          type: string
          enum: [public, private]
        viewerCount:
          type: integer
        maxViewers:
          type: integer
        hlsUrl:
          type: string
          nullable: true
        rtmpUrl:
          type: string
          nullable: true
        webrtcEndpoint:
          type: string
          nullable: true
        joinUrl:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        tags:
          type: array
          items:
            type: string
        isFeatured:
          type: boolean
        totalSparksReceived:
          type: integer
        totalDiamondsReceived:
          type: integer
        peakViewerCount:
          type: integer

    JoinInfoResponse:
      type: object
      required: [sessionId, title, status, visibility, joinUrl, viewerCount, maxViewers, isJoinable, requiresSubscription, requiresTokens]
      properties:
        sessionId:
          type: string
          format: uuid
        title:
          type: string
        status:
          type: string
          enum: [pending, active, paused, completed, error, cancelled, private]
        visibility:
          type: string
          enum: [public, private]
        hlsUrl:
          type: string
          nullable: true
        rtmpUrl:
          type: string
          nullable: true
        webrtcEndpoint:
          type: string
          nullable: true
        joinUrl:
          type: string
        viewerCount:
          type: integer
        maxViewers:
          type: integer
        isJoinable:
          type: boolean
        requiresSubscription:
          type: boolean
        requiresTokens:
          type: boolean
        subscriptionTierRequired:
          type: string
          enum: [free, basic, pro, premium]
        accessTokensRequired:
          type: integer

    # Metrics and Analytics Schemas
    SessionMetricsResponse:
      type: object
      required: [totalSessions, activeSessions, completedSessions, errorSessions, totalViewers, peakConcurrentViewers, averageViewersPerSession, totalWatchTime, totalRevenue, totalSparks, totalDiamonds, totalReactions, sessionStatusBreakdown, hourlyStats, topSessions, platformHealth, lastUpdated]
      properties:
        totalSessions:
          type: integer
        activeSessions:
          type: integer
        completedSessions:
          type: integer
        errorSessions:
          type: integer
        totalViewers:
          type: integer
        peakConcurrentViewers:
          type: integer
        averageViewersPerSession:
          type: number
        totalWatchTime:
          type: number
        totalRevenue:
          type: number
        totalSparks:
          type: integer
        totalDiamonds:
          type: integer
        totalReactions:
          type: integer
        sessionStatusBreakdown:
          type: array
          items:
            $ref: '#/components/schemas/SessionStatusCount'
        hourlyStats:
          type: array
          items:
            $ref: '#/components/schemas/HourlyStats'
        topSessions:
          type: array
          items:
            $ref: '#/components/schemas/TopSession'
        platformHealth:
          $ref: '#/components/schemas/PlatformHealth'
        lastUpdated:
          type: string
          format: date-time

    SessionStatusCount:
      type: object
      required: [status, count, percentage]
      properties:
        status:
          type: string
        count:
          type: integer
        percentage:
          type: number

    HourlyStats:
      type: object
      required: [hour, sessions, viewers, revenue]
      properties:
        hour:
          type: string
        sessions:
          type: integer
        viewers:
          type: integer
        revenue:
          type: number

    TopSession:
      type: object
      required: [id, title, viewerCount, duration, revenue, sparks, diamonds]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        viewerCount:
          type: integer
        duration:
          type: number
        revenue:
          type: number
        sparks:
          type: integer
        diamonds:
          type: integer

    PlatformHealth:
      type: object
      required: [overallHealth, uptime, errorRate, averageResponseTime, activeConnections, databaseStatus]
      properties:
        overallHealth:
          type: string
          enum: [excellent, good, warning, critical]
        uptime:
          type: number
        errorRate:
          type: number
        averageResponseTime:
          type: number
        activeConnections:
          type: integer
        databaseStatus:
          type: string
        lastIncident:
          type: string
          format: date-time

    RealTimeMetricsResponse:
      type: object
      required: [timestamp, activeSessions, currentViewers, newSessionsLastHour, completedSessionsLastHour, revenueLastHour, sparksLastHour, diamondsLastHour, averageSessionDuration, topPerformingSessions, systemLoad]
      properties:
        timestamp:
          type: string
          format: date-time
        activeSessions:
          type: integer
        currentViewers:
          type: integer
        newSessionsLastHour:
          type: integer
        completedSessionsLastHour:
          type: integer
        revenueLastHour:
          type: number
        sparksLastHour:
          type: integer
        diamondsLastHour:
          type: integer
        averageSessionDuration:
          type: number
        topPerformingSessions:
          type: array
          items:
            $ref: '#/components/schemas/TopSession'
        systemLoad:
          $ref: '#/components/schemas/SystemLoad'

    SystemLoad:
      type: object
      required: [cpuUsage, memoryUsage, databaseConnections, websocketConnections, queueSize]
      properties:
        cpuUsage:
          type: number
        memoryUsage:
          type: number
        databaseConnections:
          type: integer
        websocketConnections:
          type: integer
        queueSize:
          type: integer

    HistoricalMetricsResponse:
      type: object
      required: [period, granularity, dataPoints, summary]
      properties:
        period:
          type: string
        granularity:
          type: string
        dataPoints:
          type: array
          items:
            $ref: '#/components/schemas/MetricsDataPoint'
        summary:
          $ref: '#/components/schemas/MetricsSummary'

    MetricsDataPoint:
      type: object
      required: [timestamp]
      properties:
        timestamp:
          type: string
          format: date-time
        sessions:
          type: integer
        viewers:
          type: integer
        revenue:
          type: number
        sparks:
          type: integer
        diamonds:
          type: integer
        reactions:
          type: integer
        watchTime:
          type: number

    MetricsSummary:
      type: object
      required: [totalSessions, totalViewers, totalRevenue, totalSparks, totalDiamonds, totalReactions, totalWatchTime, averageSessionDuration, peakConcurrentViewers, growthRate]
      properties:
        totalSessions:
          type: integer
        totalViewers:
          type: integer
        totalRevenue:
          type: number
        totalSparks:
          type: integer
        totalDiamonds:
          type: integer
        totalReactions:
          type: integer
        totalWatchTime:
          type: number
        averageSessionDuration:
          type: number
        peakConcurrentViewers:
          type: integer
        growthRate:
          type: number


