openapi: 3.0.3
info:
  title: SpaceDB API
  description: >
    A REST API providing data on stars, planets, moons, and spacecraft.
    All endpoints require a bearer token. Read tokens grant GET access;
    write tokens grant full CRUD access.
  version: 1.0.0
  contact:
    url: https://spacedb.cloud

servers:
  - url: https://spacedb.cloud/api/v1
    description: Production
  - url: http://spacedb.test/api/v1
    description: Local development

security:
  - bearerAuth: []

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

  schemas:
    Star:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Sol
        type:
          type: string
          enum: [main_sequence, red_giant, white_dwarf, neutron, supergiant]
          example: main_sequence
        mass_solar:
          type: number
          format: float
          nullable: true
          example: 1.0
        luminosity_solar:
          type: number
          format: float
          nullable: true
          example: 1.0
        temperature_k:
          type: integer
          nullable: true
          example: 5778
        distance_ly:
          type: number
          format: float
          nullable: true
          example: 0.0
        constellation:
          type: string
          nullable: true
          example: null
        discovered_at:
          type: string
          format: date
          nullable: true
          example: null
        planet_count:
          type: integer
          example: 9
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Planet:
      type: object
      properties:
        id:
          type: integer
          example: 3
        name:
          type: string
          example: Earth
        type:
          type: string
          enum: [terrestrial, gas_giant, ice_giant, dwarf]
          example: terrestrial
        star_id:
          type: integer
          nullable: true
          example: 1
        mass_earth:
          type: number
          format: float
          nullable: true
          example: 1.0
        radius_km:
          type: number
          format: float
          nullable: true
          example: 6371.0
        orbital_period_days:
          type: number
          format: float
          nullable: true
          example: 365.25
        distance_au:
          type: number
          format: float
          nullable: true
          example: 1.0
        has_rings:
          type: boolean
          example: false
        discovered_at:
          type: string
          format: date
          nullable: true
          example: null
        moon_count:
          type: integer
          example: 1
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Moon:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Luna
        planet_id:
          type: integer
          nullable: true
          example: 3
        mass_kg:
          type: number
          format: float
          nullable: true
          example: 7.342e+22
        radius_km:
          type: number
          format: float
          nullable: true
          example: 1737.4
        orbital_period_days:
          type: number
          format: float
          nullable: true
          example: 27.32
        discovered_at:
          type: string
          format: date
          nullable: true
          example: null
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Spaceship:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: ISS
        type:
          type: string
          enum: [spacecraft, probe, station, rocket]
          example: station
        operator:
          type: string
          nullable: true
          example: NASA / Roscosmos
        launch_date:
          type: string
          format: date
          nullable: true
          example: "1998-11-20"
        status:
          type: string
          enum: [active, retired, planned, lost]
          example: active
        crew_capacity:
          type: integer
          nullable: true
          example: 7
        mass_kg:
          type: number
          format: float
          nullable: true
          example: 419725.0
        description:
          type: string
          nullable: true
          example: International Space Station
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Pagination:
      type: object
      properties:
        links:
          type: object
          properties:
            first:
              type: string
              example: "http://spacedb.test/api/v1/stars?page=1"
            last:
              type: string
              example: "http://spacedb.test/api/v1/stars?page=1"
            prev:
              type: string
              nullable: true
              example: null
            next:
              type: string
              nullable: true
              example: null
        meta:
          type: object
          properties:
            current_page:
              type: integer
              example: 1
            per_page:
              type: integer
              example: 25
            total:
              type: integer
              example: 8

    ValidationError:
      type: object
      properties:
        message:
          type: string
          example: The name field is required.
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string

paths:
  # ─── Stars ────────────────────────────────────────────────────────────────

  /stars:
    get:
      summary: List all stars
      tags: [Stars]
      parameters:
        - name: page
          in: query
          schema: { type: integer, example: 1 }
        - name: per_page
          in: query
          schema: { type: integer, example: 25, maximum: 100 }
      responses:
        '200':
          description: Paginated list of stars
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Star'
                  - $ref: '#/components/schemas/Pagination'
        '401':
          description: Unauthenticated
    post:
      summary: Create a star
      tags: [Stars]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type]
              properties:
                name:
                  type: string
                type:
                  type: string
                  enum: [main_sequence, red_giant, white_dwarf, neutron, supergiant]
                mass_solar:
                  type: number
                luminosity_solar:
                  type: number
                temperature_k:
                  type: integer
                distance_ly:
                  type: number
                constellation:
                  type: string
                discovered_at:
                  type: string
                  format: date
      responses:
        '201':
          description: Star created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Star'
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden — token lacks write ability
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'

  /stars/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: Get a star
      tags: [Stars]
      responses:
        '200':
          description: Star details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Star'
        '401':
          description: Unauthenticated
        '404':
          description: Not found
    put:
      summary: Replace a star
      tags: [Stars]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Star'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    patch:
      summary: Partially update a star
      tags: [Stars]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Star'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    delete:
      summary: Delete a star
      tags: [Stars]
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found

  # ─── Planets ──────────────────────────────────────────────────────────────

  /planets:
    get:
      summary: List all planets
      tags: [Planets]
      parameters:
        - name: page
          in: query
          schema: { type: integer }
        - name: per_page
          in: query
          schema: { type: integer, maximum: 100 }
      responses:
        '200':
          description: Paginated list of planets
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Planet'
                  - $ref: '#/components/schemas/Pagination'
        '401':
          description: Unauthenticated
    post:
      summary: Create a planet
      tags: [Planets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type]
              properties:
                name:
                  type: string
                type:
                  type: string
                  enum: [terrestrial, gas_giant, ice_giant, dwarf]
                star_id:
                  type: integer
                  nullable: true
                mass_earth:
                  type: number
                radius_km:
                  type: number
                orbital_period_days:
                  type: number
                distance_au:
                  type: number
                has_rings:
                  type: boolean
                discovered_at:
                  type: string
                  format: date
      responses:
        '201':
          description: Planet created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Planet'
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '422':
          description: Validation error

  /planets/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: Get a planet
      tags: [Planets]
      responses:
        '200':
          description: Planet details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Planet'
        '401':
          description: Unauthenticated
        '404':
          description: Not found
    put:
      summary: Replace a planet
      tags: [Planets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Planet'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    patch:
      summary: Partially update a planet
      tags: [Planets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Planet'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    delete:
      summary: Delete a planet
      tags: [Planets]
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found

  # ─── Moons ────────────────────────────────────────────────────────────────

  /moons:
    get:
      summary: List all moons
      tags: [Moons]
      parameters:
        - name: page
          in: query
          schema: { type: integer }
        - name: per_page
          in: query
          schema: { type: integer, maximum: 100 }
      responses:
        '200':
          description: Paginated list of moons
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Moon'
                  - $ref: '#/components/schemas/Pagination'
        '401':
          description: Unauthenticated
    post:
      summary: Create a moon
      tags: [Moons]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                planet_id:
                  type: integer
                  nullable: true
                mass_kg:
                  type: number
                radius_km:
                  type: number
                orbital_period_days:
                  type: number
                discovered_at:
                  type: string
                  format: date
      responses:
        '201':
          description: Moon created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Moon'
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '422':
          description: Validation error

  /moons/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: Get a moon
      tags: [Moons]
      responses:
        '200':
          description: Moon details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Moon'
        '401':
          description: Unauthenticated
        '404':
          description: Not found
    put:
      summary: Replace a moon
      tags: [Moons]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Moon'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    patch:
      summary: Partially update a moon
      tags: [Moons]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Moon'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    delete:
      summary: Delete a moon
      tags: [Moons]
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found

  # ─── Spaceships ───────────────────────────────────────────────────────────

  /spaceships:
    get:
      summary: List all spaceships
      tags: [Spaceships]
      parameters:
        - name: page
          in: query
          schema: { type: integer }
        - name: per_page
          in: query
          schema: { type: integer, maximum: 100 }
      responses:
        '200':
          description: Paginated list of spaceships
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Spaceship'
                  - $ref: '#/components/schemas/Pagination'
        '401':
          description: Unauthenticated
    post:
      summary: Create a spaceship
      tags: [Spaceships]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type, status]
              properties:
                name:
                  type: string
                type:
                  type: string
                  enum: [spacecraft, probe, station, rocket]
                operator:
                  type: string
                  nullable: true
                launch_date:
                  type: string
                  format: date
                  nullable: true
                status:
                  type: string
                  enum: [active, retired, planned, lost]
                crew_capacity:
                  type: integer
                  nullable: true
                mass_kg:
                  type: number
                  nullable: true
                description:
                  type: string
                  nullable: true
      responses:
        '201':
          description: Spaceship created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Spaceship'
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '422':
          description: Validation error

  /spaceships/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: Get a spaceship
      tags: [Spaceships]
      responses:
        '200':
          description: Spaceship details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Spaceship'
        '401':
          description: Unauthenticated
        '404':
          description: Not found
    put:
      summary: Replace a spaceship
      tags: [Spaceships]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Spaceship'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    patch:
      summary: Partially update a spaceship
      tags: [Spaceships]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Spaceship'
      responses:
        '200':
          description: Updated
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
        '422':
          description: Validation error
    delete:
      summary: Delete a spaceship
      tags: [Spaceships]
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthenticated
        '403':
          description: Forbidden
        '404':
          description: Not found
