openapi: 3.0.0
info:
  title: Meezak Framework API
  description: REST API for the Meezak PHP backend framework.
  version: 1.0.0

servers:
  - url: http://localhost:8000
    description: Local Development

tags:
  - name: Utilities
    description: Health and test endpoints
  - name: Contact Us
    description: Guest Contact Us submissions from the marketing site
  - name: Analytics
    description: Guest website visit tracking
  - name: Live Chat
    description: Guest live chat initiation and messaging
  - name: Admin Auth
    description: Admin login, logout, password reset, and current-admin profile
  - name: Admin Dashboard
    description: Admin overview metrics and recent enquiries
  - name: Admin Messages
    description: Contact form message inbox and replies
  - name: Admin Chats
    description: Live chat threads for admins
  - name: Admin Settings
    description: Public profile and chat configuration
  - name: Admin Notifications
    description: In-app admin notification feed

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Admin JWT access token from POST /api/admin/auth/login.
    ContactUsBearerAuth:
      type: http
      scheme: bearer
      description: Opaque Contact Us API token from CONTACT_US_API_TOKEN. Not a JWT.

  schemas:
    SuccessResponse:
      type: object
      required: [success, message]
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: Success
        data:
          nullable: true

    ErrorResponse:
      type: object
      required: [success, message]
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
          example: Unauthorized
        errors:
          type: object
          additionalProperties: true

    ContactRequest:
      type: object
      required: [id, first_name, last_name, email, phone, message, created_at]
      properties:
        id:
          type: integer
          example: 12
        first_name:
          type: string
          example: Ada
        last_name:
          type: string
          example: Lovelace
        email:
          type: string
          format: email
          example: ada@example.com
        phone:
          type: string
          example: "+15551234567"
        message:
          type: string
          example: I would like to learn more about your services.
        status:
          type: string
          enum: [awaiting_reply, replied]
        is_read:
          type: boolean
        is_replied:
          type: boolean
        replied_at:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
          example: "2026-09-07 16:00:00"
        updated_at:
          type: string
          format: date-time
          nullable: true

    ContactUsBody:
      type: object
      required: [first_name, last_name, email, phone, message]
      properties:
        first_name:
          type: string
          minLength: 1
          maxLength: 100
        last_name:
          type: string
          minLength: 1
          maxLength: 100
        email:
          type: string
          format: email
          maxLength: 255
        phone:
          type: string
          minLength: 7
          maxLength: 40
        message:
          type: string
          minLength: 2
          maxLength: 5000

    Admin:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Site Admin
        email:
          type: string
          format: email
          example: admin@example.com
        phone:
          type: string
          nullable: true
        avatar_url:
          type: string
          nullable: true
        role:
          type: string
          example: admin
        status:
          type: string
          example: active
        last_login_at:
          type: string
          nullable: true

    AdminSettings:
      type: object
      properties:
        profile:
          type: object
          properties:
            name:
              type: string
            contact_email:
              type: string
              nullable: true
            phone:
              type: string
              nullable: true
            address:
              type: string
              nullable: true
        chat:
          type: object
          properties:
            welcome_message:
              type: string
            require_name:
              type: boolean
            require_email:
              type: boolean
            require_phone:
              type: boolean

    PaginatedMeta:
      type: object
      properties:
        total:
          type: integer
        per_page:
          type: integer
        current_page:
          type: integer
        last_page:
          type: integer

paths:
  /api/health:
    get:
      tags: [Utilities]
      summary: Health check
      security: []
      responses:
        "200":
          description: Service is up
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok

  /api/contact-us:
    post:
      tags: [Contact Us]
      summary: Submit a guest Contact Us request
      description: |
        Authenticates with the dedicated Contact Us bearer token (not an admin JWT).
        Persists the request and queues an owner notification email. The HTTP
        response returns immediately; cron (`cron/process-crons.php`) sends the email.
      security:
        - ContactUsBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactUsBody"
      responses:
        "201":
          description: Contact request saved and notification queued
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/SuccessResponse"
                  - type: object
                    properties:
                      data:
                        $ref: "#/components/schemas/ContactRequest"
        "400":
          description: Unexpected failure (generic client-safe message)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                message: Something went wrong
        "401":
          description: Missing or invalid Contact Us bearer token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                message: Unauthorized
        "422":
          description: Validation failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (10 requests per 60 seconds)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/admin/auth/login:
    post:
      tags: [Admin Auth]
      summary: Admin login
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
      responses:
        "200":
          description: Logged in
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/SuccessResponse"
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          admin:
                            $ref: "#/components/schemas/Admin"
                          api_token:
                            type: string
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Invalid credentials or validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Too many login attempts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/admin/auth/logout:
    post:
      tags: [Admin Auth]
      summary: Admin logout (blacklist JWT)
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Logged out
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "401":
          description: Missing or invalid admin JWT
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/admin/auth/forgot-password:
    post:
      tags: [Admin Auth]
      summary: Request admin password reset code
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
      responses:
        "200":
          description: Generic success (does not reveal whether the email exists)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/auth/verify-reset-code:
    post:
      tags: [Admin Auth]
      summary: Verify admin password reset code
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [code]
              properties:
                code:
                  type: string
                email:
                  type: string
                  format: email
      responses:
        "200":
          description: Code is valid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "400":
          description: Invalid or expired code
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/admin/auth/reset-password:
    post:
      tags: [Admin Auth]
      summary: Reset admin password with OTP code
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [code, new_password, new_password_confirmation]
              properties:
                code:
                  type: string
                email:
                  type: string
                  format: email
                new_password:
                  type: string
                  minLength: 8
                new_password_confirmation:
                  type: string
      responses:
        "200":
          description: Password reset
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/auth/me:
    get:
      tags: [Admin Auth]
      summary: Current admin profile
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Admin profile
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/SuccessResponse"
                  - type: object
                    properties:
                      data:
                        $ref: "#/components/schemas/Admin"
        "401":
          description: Missing or invalid admin JWT
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/analytics/visit:
    post:
      tags: [Analytics]
      summary: Record a website visit for today
      security:
        - ContactUsBearerAuth: []
      responses:
        "200":
          description: Visit recorded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/chat/settings:
    get:
      tags: [Live Chat]
      summary: Public chat welcome message and required fields
      security:
        - ContactUsBearerAuth: []
      responses:
        "200":
          description: Chat settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/chat/start:
    post:
      tags: [Live Chat]
      summary: Start or resume a live chat thread
      description: |
        Creates or reuses a chat by unique_key (email preferred, else phone).
        Returns a session_token used for guest message history scoping.
      security:
        - ContactUsBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                phone:
                  type: string
      responses:
        "201":
          description: Chat started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "422":
          description: Validation failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/chat/messages:
    get:
      tags: [Live Chat]
      summary: List guest-session chat messages
      description: Pass session_token in body/query or X-Chat-Session header. Guest history is scoped to session_started_at.
      security:
        - ContactUsBearerAuth: []
      parameters:
        - in: header
          name: X-Chat-Session
          schema:
            type: string
        - in: query
          name: session_token
          schema:
            type: string
      responses:
        "200":
          description: Session-scoped messages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
        "401":
          description: Invalid chat session
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    post:
      tags: [Live Chat]
      summary: Send a guest chat message
      security:
        - ContactUsBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                session_token:
                  type: string
                message:
                  type: string
      responses:
        "201":
          description: Message sent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/dashboard:
    get:
      tags: [Admin Dashboard]
      summary: Overview summary and recent enquiries
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Dashboard payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/messages:
    get:
      tags: [Admin Messages]
      summary: List contact messages
      security:
        - BearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: per_page
          schema:
            type: integer
      responses:
        "200":
          description: Paginated messages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/messages/{status}:
    get:
      tags: [Admin Messages]
      summary: List contact messages filtered by status
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: status
          required: true
          schema:
            type: string
            enum: [awaiting_reply, replied, all]
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: per_page
          schema:
            type: integer
      responses:
        "200":
          description: Paginated messages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/messages/show/{id}:
    get:
      tags: [Admin Messages]
      summary: Contact message detail with reply history
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Message with history
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/messages/{id}/reply:
    post:
      tags: [Admin Messages]
      summary: Reply to a contact message
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
      responses:
        "200":
          description: Reply queued and persisted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/chats:
    get:
      tags: [Admin Chats]
      summary: List live chats
      security:
        - BearerAuth: []
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [awaiting_reply, replied, all]
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: per_page
          schema:
            type: integer
      responses:
        "200":
          description: Paginated chats
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/chats/{id}:
    get:
      tags: [Admin Chats]
      summary: Live chat detail with full message history
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Chat and messages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/chats/{id}/messages:
    post:
      tags: [Admin Chats]
      summary: Send an admin chat message
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
      responses:
        "201":
          description: Message sent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/settings:
    get:
      tags: [Admin Settings]
      summary: Get profile and chat settings
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Settings
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/SuccessResponse"
                  - type: object
                    properties:
                      data:
                        $ref: "#/components/schemas/AdminSettings"
    patch:
      tags: [Admin Settings]
      summary: Update profile and/or chat settings
      description: Never updates login admins.email. Use contact_email for public contact.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AdminSettings"
      responses:
        "200":
          description: Settings updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/notifications:
    get:
      tags: [Admin Notifications]
      summary: List notifications
      security:
        - BearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: per_page
          schema:
            type: integer
      responses:
        "200":
          description: Paginated notifications
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/notifications/read-all:
    post:
      tags: [Admin Notifications]
      summary: Mark all notifications as read
      security:
        - BearerAuth: []
      responses:
        "200":
          description: All marked read
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"

  /api/admin/notifications/{id}/read:
    post:
      tags: [Admin Notifications]
      summary: Mark one notification as read
      security:
        - BearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Notification marked read
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessResponse"
