openapi: 3.0.3
info:
  title: Quotex API
  version: "2.0.0"
  description: |
    Quotes API with search, filtering, pagination, and random selection, plus
    tags, favorites, ratings, collections, quote-of-the-day, and analytics.

    Endpoints tagged Tags/Favorites/Ratings/Collections/Analytics require SQLite
    mode; in JSON mode they return `501 not_supported` (except activity tracking,
    which silently succeeds).
servers:
  - url: https://quotes.btay.io
    description: Production
  - url: http://127.0.0.1:5000
    description: Local development

tags:
  - name: Quotes
    description: Search, fetch, and random quotes
  - name: Tags
    description: Tag listing (SQLite mode)
  - name: Favorites
    description: Per-session/user favorites (SQLite mode)
  - name: Ratings
    description: 1–5 star ratings (SQLite mode)
  - name: Collections
    description: User-defined quote collections (SQLite mode)
  - name: Analytics
    description: Activity tracking and popularity (SQLite mode)
  - name: Meta
    description: Metadata and health

components:
  schemas:
    Quote:
      type: object
      required: [ id, text, author, category ]
      properties:
        id: { type: integer, example: 141 }
        text:
          type: string
          example: How you do anything, is how you do everything.
        author: { type: string, example: Zen Buddhist Proverb }
        category: { type: string, example: Motivation }
        source: { type: string, nullable: true, example: null }
        tags:
          type: array
          items: { type: string }
          example: [ ]

    PaginatedQuotes:
      type: object
      required: [ page, per_page, total, items, has_prev, has_next ]
      properties:
        page: { type: integer, example: 1 }
        per_page: { type: integer, example: 5 }
        total: { type: integer, example: 232 }
        items:
          type: array
          items: { $ref: '#/components/schemas/Quote' }
        has_prev: { type: boolean, example: false }
        has_next: { type: boolean, example: true }

    Error:
      type: object
      required: [ error, message ]
      properties:
        error: { type: string, example: not_found }
        message: { type: string, example: Quote not found }

    Success:
      type: object
      properties:
        success: { type: boolean, example: true }

  responses:
    NotSupported:
      description: Requires SQLite mode; JSON mode returns 501
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

paths:
  /api/quotes:
    get:
      tags: [ Quotes ]
      summary: List quotes with search/filter/pagination
      parameters:
        - { in: query, name: q, schema: { type: string }, description: Case-insensitive substring across text/author/category }
        - { in: query, name: author, schema: { type: string }, description: Exact match on author (case-insensitive) }
        - { in: query, name: category, schema: { type: string }, description: Exact match on category (case-insensitive) }
        - in: query
          name: sort
          schema: { type: string, enum: [ author, category, text, random ] }
          description: Sort order
        - { in: query, name: page, schema: { type: integer, minimum: 1, default: 1 } }
        - { in: query, name: per_page, schema: { type: integer, minimum: 1, maximum: 100, default: 50 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PaginatedQuotes' }

  /api/quotes/{id}:
    get:
      tags: [ Quotes ]
      summary: Get a quote by ID
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Quote' }
        '404':
          description: Not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/random:
    get:
      tags: [ Quotes ]
      summary: Get a random quote (optionally filtered)
      parameters:
        - { in: query, name: author, schema: { type: string } }
        - { in: query, name: category, schema: { type: string } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Quote' }
        '404':
          description: No quotes found for given filter
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/quote-of-the-day:
    get:
      tags: [ Quotes ]
      summary: Get the quote of the day (deterministic by date)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Quote' }
        '404':
          description: No quotes available
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/authors:
    get:
      tags: [ Quotes ]
      summary: List authors and counts
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  counts:
                    type: object
                    additionalProperties: { type: integer }
                    example: { Charlie Munger: 18, Ray Dalio: 6, Unknown: 120 }
                  values:
                    type: array
                    items: { type: string }
                    example: [ "Charlie Munger", "Ray Dalio", "Unknown" ]

  /api/categories:
    get:
      tags: [ Quotes ]
      summary: List categories and counts
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  counts:
                    type: object
                    additionalProperties: { type: integer }
                    example: { General: 22, Motivation: 12, Knowledge: 8 }
                  values:
                    type: array
                    items: { type: string }
                    example: [ "General", "Motivation", "Knowledge" ]

  /api/tags:
    get:
      tags: [ Tags ]
      summary: List all tags with counts (SQLite mode)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  tags:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: integer, example: 1 }
                        name: { type: string, example: motivation }
                        count: { type: integer, example: 12 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/quotes/{id}/favorite:
    post:
      tags: [ Favorites ]
      summary: Add a quote to favorites (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: Favorited
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  favorited: { type: boolean, example: true }
        '400':
          description: Already favorited or quote not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '501': { $ref: '#/components/responses/NotSupported' }
    delete:
      tags: [ Favorites ]
      summary: Remove a quote from favorites (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: Unfavorited
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  favorited: { type: boolean, example: false }
        '400':
          description: Not in favorites
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/favorites:
    get:
      tags: [ Favorites ]
      summary: List favorite quotes for the current session/user (SQLite mode)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  favorites:
                    type: array
                    items: { $ref: '#/components/schemas/Quote' }
                  count: { type: integer, example: 3 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/quotes/{id}/rate:
    post:
      tags: [ Ratings ]
      summary: Rate a quote 1–5 (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ rating ]
              properties:
                rating: { type: integer, minimum: 1, maximum: 5, example: 5 }
      responses:
        '200':
          description: Rated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  rating: { type: integer, example: 5 }
                  average_rating: { type: number, format: float, example: 4.3 }
        '400':
          description: Rating must be between 1 and 5
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/quotes/{id}/rating:
    get:
      tags: [ Ratings ]
      summary: Get current user + average rating for a quote (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_rating: { type: integer, nullable: true, example: 4 }
                  average_rating: { type: number, format: float, example: 4.3 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/quotes/top-rated:
    get:
      tags: [ Ratings ]
      summary: List top-rated quotes (SQLite mode)
      parameters:
        - { in: query, name: limit, schema: { type: integer, default: 10, maximum: 100 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  quotes:
                    type: array
                    items: { $ref: '#/components/schemas/Quote' }
                  count: { type: integer, example: 10 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/collections:
    get:
      tags: [ Collections ]
      summary: List collections for the current session/user (SQLite mode)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  collections: { type: array, items: { type: object } }
                  count: { type: integer, example: 2 }
        '501': { $ref: '#/components/responses/NotSupported' }
    post:
      tags: [ Collections ]
      summary: Create a collection (SQLite mode)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ name ]
              properties:
                name: { type: string, example: Favorites for Monday }
                description: { type: string, nullable: true }
                is_public: { type: boolean, default: false }
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  collection_id: { type: integer, example: 7 }
        '400':
          description: Collection name is required
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/collections/{id}:
    get:
      tags: [ Collections ]
      summary: Get a collection with its quotes (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  collection: { type: object }
                  quotes:
                    type: array
                    items: { $ref: '#/components/schemas/Quote' }
        '404':
          description: Collection not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '501': { $ref: '#/components/responses/NotSupported' }
    delete:
      tags: [ Collections ]
      summary: Delete a collection (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '404':
          description: Collection not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/collections/{id}/quotes/{quote_id}:
    post:
      tags: [ Collections ]
      summary: Add a quote to a collection (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
        - { in: path, name: quote_id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: Added
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '400':
          description: Already in collection or invalid IDs
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '501': { $ref: '#/components/responses/NotSupported' }
    delete:
      tags: [ Collections ]
      summary: Remove a quote from a collection (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
        - { in: path, name: quote_id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: Removed
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '400':
          description: Quote not in collection
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/quotes/{id}/track:
    post:
      tags: [ Analytics ]
      summary: Track activity on a quote (view/copy/share); no-op in JSON mode
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                type: { type: string, default: view, example: view }
      responses:
        '200':
          description: Tracked
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Success' }

  /api/quotes/{id}/stats:
    get:
      tags: [ Analytics ]
      summary: Get activity stats for a quote (SQLite mode)
      parameters:
        - { in: path, name: id, required: true, schema: { type: integer, minimum: 1 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { type: object }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/analytics/popular:
    get:
      tags: [ Analytics ]
      summary: Most popular quotes over a window (SQLite mode)
      parameters:
        - { in: query, name: days, schema: { type: integer, default: 7 } }
        - { in: query, name: limit, schema: { type: integer, default: 10, maximum: 100 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  quotes:
                    type: array
                    items: { $ref: '#/components/schemas/Quote' }
                  count: { type: integer, example: 10 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /api/analytics/trending:
    get:
      tags: [ Analytics ]
      summary: Trending quotes (SQLite mode)
      parameters:
        - { in: query, name: limit, schema: { type: integer, default: 10, maximum: 100 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  quotes:
                    type: array
                    items: { $ref: '#/components/schemas/Quote' }
                  count: { type: integer, example: 10 }
        '501': { $ref: '#/components/responses/NotSupported' }

  /healthz:
    get:
      tags: [ Meta ]
      summary: Health check
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean, example: true }
                  count: { type: integer, example: 232 }
