openapi: '3.1.0'

info:
  title: FleetOps API
  version: '1.0.0'
  description: |
    REST API for the FleetOps fleet management platform.

    ## Authentication

    On successful login the API sets an `HttpOnly` `token` cookie containing a signed JWT.
    Browsers send this cookie automatically on subsequent requests.
    For non-browser clients, pass the token as an `Authorization: Bearer <token>` header instead.

    Token revocation is **immediate**: calling `/auth/logout` records a revocation timestamp in
    Redis. Any token issued before that timestamp is rejected on the next request — even if the
    JWT itself has not yet expired.

    ## Roles

    | Role    | Permissions                                 |
    |---------|---------------------------------------------|
    | `user`  | Dashboard access, own profile               |
    | `admin` | All of the above + full user management     |

  contact:
    email: grootwebdev@gmail.com
  license:
    name: MIT

servers:
  - url: https://api.fleetops.grootwebdev.com
    description: Production
  - url: http://localhost:3000
    description: Local development

security:
  - cookieAuth: []
  - bearerAuth: []

tags:
  - name: System
    description: Health and contact endpoints
  - name: Auth
    description: Registration, login, logout
  - name: Users
    description: Current-user profile and token refresh
  - name: Admin – Users
    description: User management (admin role required)

components:
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: token
      description: HttpOnly JWT cookie set on login
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Pass the JWT as `Authorization: Bearer <token>`

  schemas:
    UserRole:
      type: string
      enum: [user, admin]
      description: Role assigned to a user account

    UserRecord:
      type: object
      required: [id, email, roles, disabled, created_at, updated_at]
      properties:
        id:
          type: string
          format: uuid
          example: '01930b3c-6a58-7000-b2cf-2d3e4f5a6b7c'
        email:
          type: string
          format: email
          example: 'operator@fleet.example'
        roles:
          type: array
          items:
            $ref: '#/components/schemas/UserRole'
          example: [user]
        disabled:
          type: boolean
          example: false
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    StandardEnvelope:
      type: object
      description: All successful responses are wrapped in this envelope
      properties:
        status:
          type: integer
          description: HTTP status code mirrored in the body
          example: 200
        message:
          type: string
          nullable: true
          example: OK
        data:
          description: Response payload; schema varies by endpoint

    ErrorResponse:
      type: object
      properties:
        status:
          type: integer
          example: 401
        message:
          type: string
          example: Invalid or expired token
        errors:
          description: Field-level validation errors (present only for 422 responses)
          nullable: true

    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
          example: admin@fleet.example
        password:
          type: string
          minLength: 8
          example: 'correct-horse-battery-staple'

    RegisterRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
          example: newuser@fleet.example
        password:
          type: string
          minLength: 8

    CreateUserRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
        roles:
          type: array
          items:
            $ref: '#/components/schemas/UserRole'
          default: [user]
          example: [user]

    UpdateUserRequest:
      type: object
      description: All fields are optional; only provided fields are updated
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
        roles:
          type: array
          items:
            $ref: '#/components/schemas/UserRole'
        disabled:
          type: boolean

    ContactRequest:
      type: object
      required: [name, email, message]
      properties:
        name:
          type: string
          example: Jane Smith
        email:
          type: string
          format: email
          example: jane@company.example
        message:
          type: string
          example: We are evaluating FleetOps for 40 vehicles. Can you tell us more about the Pro plan?

  responses:
    Unauthorized:
      description: Missing, invalid, or revoked authentication token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: 401
            message: Invalid or expired token
            errors: null
    Forbidden:
      description: Authenticated but lacks the required role
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: 403
            message: You do not have permission to access this resource
            errors: null
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: 404
            message: Not found
            errors: null
    ValidationError:
      description: Request body failed validation
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: 422
            message: Validation failed
            errors:
              email: [{ code: email, message: Invalid email address }]

paths:
  /health:
    get:
      summary: Health check
      description: Returns 200 if the service is running. No authentication required.
      tags: [System]
      security: []
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: OK

  /contact:
    post:
      summary: Send a contact message
      description: Sends an email to the FleetOps team. Does not require authentication.
      tags: [System]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactRequest'
      responses:
        '200':
          description: Message received and email dispatched
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardEnvelope'
        '422':
          $ref: '#/components/responses/ValidationError'

  /auth/register:
    post:
      summary: Register a new user account
      description: |
        Creates a new user with the `user` role. Passwords are hashed with Argon2id.
        Returns 422 if the email address is already registered.
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterRequest'
      responses:
        '201':
          description: Account created successfully
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/UserRecord'
        '422':
          $ref: '#/components/responses/ValidationError'

  /auth/login:
    post:
      summary: Authenticate and obtain a JWT
      description: |
        Validates credentials and issues a JWT token. On success:
        - Sets an `HttpOnly` `token` cookie (for browser clients)
        - Returns the raw token in the response body (for API clients)

        The token lifetime is controlled by the `JWT_EXPIRES_IN` server configuration
        (e.g. `7d`, `24h`, `3600`).
      tags: [Auth]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
      responses:
        '200':
          description: Authenticated successfully
          headers:
            Set-Cookie:
              description: HttpOnly JWT cookie
              schema:
                type: string
                example: 'token=eyJ...; Path=/; HttpOnly; SameSite=Lax'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          token:
                            type: string
                            description: Raw JWT string
        '401':
          description: Invalid email or password
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /auth/logout:
    post:
      summary: Revoke the current session token
      description: |
        Records a revocation timestamp in Redis for the authenticated user.
        All existing tokens issued before this timestamp — including non-expired ones —
        are rejected on subsequent requests. The `token` cookie is cleared.

        This endpoint accepts expired tokens so that clients can always log out cleanly.
      tags: [Auth]
      responses:
        '200':
          description: Logged out; token revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardEnvelope'

  /v1/users/me:
    get:
      summary: Get the currently authenticated user
      description: Returns the profile of the user associated with the current token.
      tags: [Users]
      responses:
        '200':
          description: Current user profile
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/UserRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/users/refresh:
    post:
      summary: Refresh the JWT token
      description: |
        Issues a new JWT and sets a fresh `token` cookie. The old token is implicitly
        superseded.

        This endpoint is protected by the auth middleware — revoked tokens cannot obtain
        a refresh. Call this before the current token expires (the `exp` claim indicates
        when).
      tags: [Users]
      responses:
        '200':
          description: New token issued
          headers:
            Set-Cookie:
              schema:
                type: string
                example: 'token=eyJ...; Path=/; HttpOnly; SameSite=Lax'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          token:
                            type: string
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/admin/users:
    get:
      summary: List all users
      description: Returns all user accounts. Requires the `admin` role.
      tags: [Admin – Users]
      responses:
        '200':
          description: Array of user records
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/UserRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

    post:
      summary: Create a user (admin)
      description: |
        Creates a new user account with specified roles. Passwords are hashed with Argon2id.
        Requires the `admin` role.
      tags: [Admin – Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/UserRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /v1/admin/users/{id}:
    parameters:
      - name: id
        in: path
        required: true
        description: UUID of the target user
        schema:
          type: string
          format: uuid
          example: '01930b3c-6a58-7000-b2cf-2d3e4f5a6b7c'

    patch:
      summary: Update a user (admin)
      description: |
        Partially updates a user account. All fields are optional — only provided fields
        are changed. Requires the `admin` role.
      tags: [Admin – Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUserRequest'
      responses:
        '200':
          description: Updated user record
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/StandardEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/UserRecord'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'

    delete:
      summary: Delete a user (admin)
      description: Permanently deletes a user account. Requires the `admin` role.
      tags: [Admin – Users]
      responses:
        '204':
          description: User deleted; no content returned
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
