openapi: 3.0.3
info:
  title: Customa API
  version: 1.1.0
  description: |
    Canonical OpenAPI bundle for Customa docs.
    Includes auth, examples, and standardized error payloads.
servers:
  - url: https://example2-doc.sudharmika.com
security:
  - bearerAuth: []
tags:
  - name: Auth
  - name: Reservation
  - name: Coupon
  - name: Order
  - name: Admin
paths:
  /api/mobile/v1/auth/login:
    post:
      tags: [Auth]
      summary: Login mobile user
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/LoginRequest' }
            examples:
              valid:
                value: { email: user@customa.id, password: secret }
      responses:
        '200':
          description: Login success
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthTokenResponse' }
              examples:
                success:
                  value:
                    success: true
                    data: { access_token: token-abc, token_type: Bearer, expires_in: 3600 }
        '422':
          $ref: '#/components/responses/ValidationError'

  /api/mobile/v1/reservations:
    post:
      tags: [Reservation]
      summary: Create reservation
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ReservationRequest' }
            examples:
              dinner:
                value: { branch_id: 1, date: '2026-03-12', time: '19:00', pax: 4 }
      responses:
        '201':
          description: Reservation created
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReservationResponse' }
        '409':
          $ref: '#/components/responses/StateConflict'
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/mobile/v1/coupons/apply:
    post:
      tags: [Coupon]
      summary: Apply coupon code
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CouponApplyRequest' }
            examples:
              valid:
                value: { code: HEMAT10, order_subtotal: 150000 }
      responses:
        '200':
          description: Coupon accepted
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CouponApplyResponse' }
        '409':
          $ref: '#/components/responses/StateConflict'
        '422':
          $ref: '#/components/responses/ValidationError'

  /api/mobile/v1/orders:
    post:
      tags: [Order]
      summary: Create order
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OrderRequest' }
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OrderResponse' }
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /api/admin/v1/orders/{id}/status:
    patch:
      tags: [Admin]
      summary: Update order status (admin)
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/OrderStatusRequest' }
      responses:
        '200':
          description: Status updated
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OrderResponse' }
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          $ref: '#/components/responses/ValidationError'

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

  responses:
    UnauthorizedError:
      description: Missing or invalid bearer token
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            success: false
            error: { code: UNAUTHENTICATED, message: Token invalid }
    ForbiddenError:
      description: Role not allowed
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            success: false
            error: { code: FORBIDDEN, reason_code: ROLE_NOT_ALLOWED, message: Access denied }
    ValidationError:
      description: Invalid payload
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ValidationErrorResponse' }
    StateConflict:
      description: Business state conflict
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example:
            success: false
            error:
              code: RESERVATION_CONFLICT
              reason_code: SLOT_UNAVAILABLE
              message: Selected slot no longer available
              correlation_id: req-7f81

  schemas:
    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email: { type: string, format: email }
        password: { type: string }
    AuthTokenResponse:
      type: object
      properties:
        success: { type: boolean, example: true }
        data:
          type: object
          properties:
            access_token: { type: string }
            token_type: { type: string, example: Bearer }
            expires_in: { type: integer, example: 3600 }
    ReservationRequest:
      type: object
      required: [branch_id, date, time, pax]
      properties:
        branch_id: { type: integer }
        date: { type: string, format: date }
        time: { type: string, example: '19:00' }
        pax: { type: integer, minimum: 1 }
    ReservationResponse:
      type: object
      properties:
        success: { type: boolean, example: true }
        data:
          type: object
          properties:
            reservation_id: { type: string, example: RSV-1001 }
            status: { type: string, example: confirmed }
    CouponApplyRequest:
      type: object
      required: [code, order_subtotal]
      properties:
        code: { type: string }
        order_subtotal: { type: number }
    CouponApplyResponse:
      type: object
      properties:
        success: { type: boolean, example: true }
        data:
          type: object
          properties:
            discount_amount: { type: number, example: 15000 }
            final_total: { type: number, example: 135000 }
    OrderRequest:
      type: object
      required: [branch_id, items]
      properties:
        branch_id: { type: integer }
        items:
          type: array
          items:
            type: object
            required: [menu_id, qty]
            properties:
              menu_id: { type: integer }
              qty: { type: integer, minimum: 1 }
    OrderResponse:
      type: object
      properties:
        success: { type: boolean }
        data:
          type: object
          properties:
            order_id: { type: string, example: ORD-1001 }
            status: { type: string, example: processing }
    OrderStatusRequest:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [new, paid, processing, completed, cancelled]
    ErrorResponse:
      type: object
      required: [success, error]
      properties:
        success: { type: boolean, example: false }
        error:
          type: object
          properties:
            code: { type: string }
            reason_code: { type: string }
            message: { type: string }
            correlation_id: { type: string }
    ValidationErrorResponse:
      type: object
      properties:
        message: { type: string, example: The given data was invalid. }
        errors:
          type: object
          additionalProperties:
            type: array
            items: { type: string }
