openapi: "3.0.3"
info:
  title: wortort API
  description: Geolocation codec that encodes coordinates as human-readable word codes.
  version: "1.0.0"
  contact:
    url: https://github.com/karte-bayern/wortort

servers:
  - url: http://localhost:8080
    description: Local development

security: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
      required: [error]

    EncodeRequest:
      type: object
      properties:
        lat:
          type: number
          format: double
          description: Latitude in WGS84
        lon:
          type: number
          format: double
          description: Longitude in WGS84
        namespace:
          type: string
          description: Optional namespace. Defaults to the server default namespace.
      required: [lat, lon]

    EncodeResponse:
      type: object
      properties:
        code:
          type: string
          description: Canonical prefixless four-word code.
          example: "word.word.word.word"
      required: [code]

    DecodeRequest:
      type: object
      properties:
        code:
          type: string
          description: Four-word code. Optional input prefixes `wo:`, `wortort:`, and legacy `eu1:` are accepted.
          example: "word.word.word.word"
      required: [code]

    Location:
      type: object
      properties:
        namespace:
          type: string
        lat:
          type: number
          format: double
        lon:
          type: number
          format: double
        index:
          type: integer
          format: int64
      required: [namespace, lat, lon, index]

    CheckRequest:
      type: object
      properties:
        code:
          type: string
          description: Four-word code. Optional input prefixes `wo:`, `wortort:`, and legacy `eu1:` are accepted.
      required: [code]

    Suggestion:
      type: object
      properties:
        code:
          type: string
        score:
          type: number
          format: double
        reason:
          type: string

    CheckResponse:
      type: object
      properties:
        valid:
          type: boolean
        reason:
          type: string
        location:
          $ref: "#/components/schemas/Location"
        suggestions:
          type: array
          items:
            $ref: "#/components/schemas/Suggestion"
      required: [valid]

    BatchEncodeRequest:
      type: object
      properties:
        locations:
          type: array
          maxItems: 1000
          items:
            type: object
            properties:
              lat:
                type: number
                format: double
              lon:
                type: number
                format: double
            required: [lat, lon]
        namespace:
          type: string
      required: [locations]

    BatchEncodeResponse:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              code:
                type: string
              error:
                type: string

    BatchDecodeRequest:
      type: object
      properties:
        codes:
          type: array
          maxItems: 1000
          items:
            type: string
      required: [codes]

    BatchDecodeResponse:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              namespace:
                type: string
              lat:
                type: number
                format: double
              lon:
                type: number
                format: double
              error:
                type: string

    HealthResponse:
      type: object
      properties:
        ok:
          type: boolean
        namespaces:
          type: array
          items:
            type: string
      required: [ok, namespaces]

    ReadyResponse:
      type: object
      properties:
        ok:
          type: boolean
        namespace:
          type: string
      required: [ok, namespace]

    VersionResponse:
      type: object
      properties:
        version:
          type: string
        commit:
          type: string
        date:
          type: string
        info:
          type: string
      required: [version, commit, date, info]

    MetricsResponse:
      type: object
      properties:
        total_requests:
          type: integer
          format: int64
        error_count:
          type: integer
          format: int64
        uptime_seconds:
          type: integer
          format: int64
      required: [total_requests, error_count, uptime_seconds]

    GeoJSONFeatureCollection:
      type: object
      properties:
        type:
          type: string
          enum: [FeatureCollection]
        features:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum: [Feature]
              geometry:
                type: object
                properties:
                  type:
                    type: string
                    enum: [Polygon]
                  coordinates:
                    type: array
              properties:
                type: object
                properties:
                  code:
                    type: string
                  label:
                    type: string
                  index:
                    type: integer
                    format: int64
        total:
          type: integer
          format: int64
        truncated:
          type: boolean
        meta:
          type: object

paths:
  /health:
    get:
      summary: Health check
      operationId: getHealth
      tags: [System]
      responses:
        "200":
          description: Server is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

  /livez:
    get:
      summary: Liveness probe
      operationId: getLivez
      tags: [System]
      responses:
        "200":
          description: Process is alive
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                required: [ok]

  /readyz:
    get:
      summary: Readiness probe
      operationId: getReadyz
      tags: [System]
      responses:
        "200":
          description: Bundle is loaded and the API is ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadyResponse"
        "503":
          description: API is not ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /version:
    get:
      summary: Build version
      operationId: getVersion
      tags: [System]
      responses:
        "200":
          description: Build metadata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VersionResponse"

  /metrics:
    get:
      summary: Server metrics
      operationId: getMetrics
      tags: [System]
      security:
        - BearerAuth: []
        - {}
      responses:
        "200":
          description: Current server metrics
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MetricsResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/encode:
    post:
      summary: Encode coordinates to a word code
      operationId: encode
      tags: [Codec]
      security:
        - BearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EncodeRequest"
      responses:
        "200":
          description: Encoded word code
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EncodeResponse"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/decode:
    post:
      summary: Decode a word code to coordinates
      operationId: decode
      tags: [Codec]
      security:
        - BearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DecodeRequest"
      responses:
        "200":
          description: Decoded location
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Location"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/check:
    post:
      summary: Validate a word code and get suggestions for near-misses
      operationId: check
      tags: [Codec]
      security:
        - BearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CheckRequest"
      responses:
        "200":
          description: Check result with optional suggestions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/batch/encode:
    post:
      summary: Batch encode up to 1000 coordinate pairs
      operationId: batchEncode
      tags: [Batch]
      security:
        - BearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchEncodeRequest"
      responses:
        "200":
          description: Batch encode results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchEncodeResponse"
        "400":
          description: Bad request (e.g. batch size exceeded)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/batch/decode:
    post:
      summary: Batch decode up to 1000 word codes
      operationId: batchDecode
      tags: [Batch]
      security:
        - BearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchDecodeRequest"
      responses:
        "200":
          description: Batch decode results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchDecodeResponse"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/grid:
    get:
      summary: Return GeoJSON grid cells for a bounding box
      operationId: getGrid
      tags: [Grid]
      security:
        - BearerAuth: []
        - {}
      parameters:
        - name: minLon
          in: query
          required: true
          schema:
            type: number
            format: double
        - name: minLat
          in: query
          required: true
          schema:
            type: number
            format: double
        - name: maxLon
          in: query
          required: true
          schema:
            type: number
            format: double
        - name: maxLat
          in: query
          required: true
          schema:
            type: number
            format: double
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 400
            maximum: 2000
        - name: namespace
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: GeoJSON FeatureCollection of grid cells
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GeoJSONFeatureCollection"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
