openapi: 3.0.3
info:
  title: KamTeswa API Documentation
  version: 1.1.5
  description: |
    Mobile/public REST API. Responses use the project envelope:
    `{ key, message, status, data? }`.

    Auth rules:
    - Client and provider identities are selected explicitly by `userType`.
    - OTP values and passwords are never returned.
    - Password reset requires a short-lived, one-time reset token issued only after OTP verification.
    - Client and provider bearer credentials are shown separately in the Authorize dialog.
servers:
  - url: /api
    description: Same origin as this documentation page
  - url: https://dashboard.kam-teswa.4hoste.com/api
    description: Production

tags:
  - name: Client Auctions
    description: Client participation, atomic bidding, and winner settlement operations.
  - name: Provider Auctions
    description: Provider-owned Auction creation, configuration, lifecycle, and winner decisions.
  - name: Shared Auctions
    description: >
      Role-aware Auction reads shared by Client and Provider. Choose exactly one
      bearer token with the per-operation token switcher in Swagger.
  - name: Client Auth
    description: Client registration and client-capable authentication flows.
  - name: Client Home
    description: Guest/client home-screen data for mobile and web.
  - name: Client Products
    description: Guest/client product discovery and similar-product catalogue operations.
  - name: Client Profile
    description: Client profile and account settings.
  - name: Client Favorites
    description: Client favorite-product list, add, and remove operations.
  - name: Client Wallet
    x-figma-ref: Wallet
    description: Client-only wallet balance and charging operations.
  - name: Provider Auth
    description: Provider registration and provider-capable authentication flows.
  - name: Provider Home
    description: Provider home-screen, statistics, and availability operations.
  - name: Provider Products
    description: Provider-owned product creation, inventory reads, editing, visibility, and soft deletion.
  - name: Shared Products
    description: >
      Authenticated product list and details reads shared by clients and providers.
      The provider receives the existing owner DTO; the client receives the public
      storefront DTO with favorite, chat, details, and AI-pricing actions.
  - name: Provider Profile
    description: Provider profile and account settings.
  - name: Provider Settlements
    description: Provider due-financial and settlement operations.
  - name: Provider Notifications
    description: Provider-initiated broadcast notification operations.
  - name: Shared Auth
    description: Authentication flows shared by client and provider accounts.
  - name: Account Roles
    description: Authenticated role-add operations under one AccountIdentity.
  - name: Shared Profile
    description: Profile and account-security operations shared by clients and providers.
  - name: Notifications
    x-figma-ref: Notifications
    description: Notification operations shared by authenticated clients and providers.
  - name: Chat
    externalDocs:
      description: Open Chat Tester
      url: /api-docs/chat-socket-events.html#socket-tester
    x-socketio:
      path: /socket.io/
      acknowledgements: false
      handshakeErrorEvent: auction:error
      handshakeQuery: [userId, lang, userType, deviceType, deviceId]
      clientEvents: [chat:enter, chat:message, chat:exit]
      serverEvents:
        - connected
        - chat:participant-joined
        - chat:message-received
        - chat:notification-status
        - chat:participant-left
        - chat:participants-updated
        - chat:error
      documentation: /api-docs/chat-socket-events.html#socket-tester
  - name: Common
    description: Shared public and authenticated operations used by both applications.
  - name: Shared Package
    description: >
      AI pricing package catalogue, AI subscription, and coupon preview
      (`PATCH /apply-coupon`) for client and provider.
  - name: AI Pricing
    description: >
      AI product pricing requests, pricing library list, and pricing request details
      for authenticated clients and providers.
  - name: Provider Package
    description: >
      Premium package catalogue, premium subscription (optional coupon fields),
      and provider-only flows.
  - name: Lookups
    description: Public lookup catalogues used by client and provider applications.
  - name: Client Order
    description: Client-only purchase-order actions, including creating, paying for, cancelling, and confirming receipt of an order.
  - name: Provider Order
    description: Provider-only purchase-order actions, including accepting, rejecting, and marking an order as delivered.
  - name: Shared Orders
    description: Order operations shared by client and provider (owner-scoped by the bearer token) — list and order details.

# Default template for authenticated endpoints. Every operation also requires SecretKeyAuth.
security:
  - SecretKeyAuth: []
    ClientBearerAuth: []
  - SecretKeyAuth: []
    ProviderBearerAuth: []

paths:
  /client/signup:
    $ref: './paths/auth.yaml#/client-signup'
  /provider/signup:
    $ref: './paths/auth.yaml#/provider-signup'
  /signin:
    $ref: './paths/auth.yaml#/signin'
  /activate:
    $ref: './paths/auth.yaml#/activate'
  /change-phone/verify-password:
    $ref: './paths/auth.yaml#/change-phone-verify-password'
  /change-phone:
    $ref: './paths/auth.yaml#/change-phone'
  /update-password:
    $ref: './paths/auth.yaml#/update-password'
  /location:
    $ref: './paths/auth.yaml#/location'
  /send-code:
    $ref: './paths/auth.yaml#/send-code'
  /change-password:
    $ref: './paths/auth.yaml#/change-password'
  /signout:
    $ref: './paths/auth.yaml#/signout'
  /delete-account:
    $ref: './paths/auth.yaml#/delete-account'
  /profile:
    $ref: './paths/auth.yaml#/profile'
  /client/profile:
    $ref: './paths/auth.yaml#/client-profile-update'
  /provider/profile:
    $ref: './paths/auth.yaml#/provider-profile-update'
  /account/provider-request:
    $ref: './paths/account.yaml#/provider-request'
  /account/client-role:
    $ref: './paths/account.yaml#/client-role'
  /account/switch-mode:
    $ref: './paths/account.yaml#/switch-mode'
  /rates:
    $ref: './paths/rates.yaml#/rates'
  /client/home:
    $ref: './paths/home.yaml#/client-home'
  /client/favorites:
    $ref: './paths/favorites.yaml#/client-favorites'
  /provider/home:
    $ref: './paths/home.yaml#/provider-home'
  /provider/products/non-premium:
    $ref: './paths/lookups.yaml#/provider-products-non-premium'
  /products:
    $ref: './paths/products.yaml#/products'
  /products/similar:
    $ref: './paths/products.yaml#/products-similar'
  /product-report-reasons:
    $ref: './paths/product-reports.yaml#/product-report-reasons'
  /products/report:
    $ref: './paths/product-reports.yaml#/product-report-submit'
  /products/details:
    $ref: './paths/products.yaml#/products-details'
  /products/visibility:
    $ref: './paths/products.yaml#/products-visibility'
  /products/premium:
    $ref: './paths/products.yaml#/products-premium'
  /isAvailable:
    $ref: './paths/home.yaml#/provider-availability'
  /wallet:
    $ref: './paths/wallet.yaml#/wallet'
  /wallet/charge:
    $ref: './paths/wallet.yaml#/wallet-charge'
  /auctions:
    $ref: './paths/auctions.yaml#/auctions'
  /auction:
    $ref: './paths/auctions.yaml#/auction-create'
  /auctions/details:
    $ref: './paths/auctions.yaml#/auction-details'
  /auctions/update:
    $ref: './paths/auctions.yaml#/auction-update'
  /auctions/join:
    $ref: './paths/auctions.yaml#/auction-join'
  /auctions/cancel:
    $ref: './paths/auctions.yaml#/auction-cancel'
  /auctions/end:
    $ref: './paths/auctions.yaml#/auction-end'
  /auctions/accept-winner:
    $ref: './paths/auctions.yaml#/auction-accept-winner'
  /auctions/reject-winner:
    $ref: './paths/auctions.yaml#/auction-reject-winner'
  /auctions/bids:
    $ref: './paths/auctions.yaml#/auctions-bids'
  /auctions/winner-payment:
    $ref: './paths/auctions.yaml#/auction-winner-payment'
  /about:
    $ref: './paths/info.yaml#/about'
  /fqs:
    $ref: './paths/info.yaml#/fqs'
  /privacy:
    $ref: './paths/info.yaml#/privacy'
  /terms:
    $ref: './paths/info.yaml#/terms'
  /intros:
    $ref: './paths/info.yaml#/intros'
  /language:
    $ref: './paths/settings.yaml#/language'
  /contact:
    $ref: './paths/support.yaml#/contact'
  /complaint:
    $ref: './paths/support.yaml#/complaint'
  /complaint/details:
    $ref: './paths/support.yaml#/complaint-details'
  /notifications:
    $ref: './paths/notifications.yaml#/notifications'
  /notification:
    $ref: './paths/notifications.yaml#/notification-single'
  /notifications/count:
    $ref: './paths/notifications.yaml#/notifications-count'
  /notifications/toggle:
    $ref: './paths/notifications.yaml#/notifications-toggle'
  /notifications/broadcast:
    $ref: './paths/notifications.yaml#/notifications-broadcast'
  /notification-keys:
    $ref: './paths/notifications.yaml#/notification-keys'
  /due-financials:
    $ref: './paths/settlements.yaml#/due-financials'
  /settlements:
    $ref: './paths/settlements.yaml#/settlements'
  /settlement:
    $ref: './paths/settlements.yaml#/settlement-details'
  /attributes:
    $ref: './paths/support.yaml#/attributes'
  /attributes/colors:
    $ref: './paths/support.yaml#/attribute-colors'
  /attributes/sizes:
    $ref: './paths/support.yaml#/attribute-sizes'
  /ai-packegs:
    $ref: './paths/ai-packages.yaml#/ai-packegs'
  /premuim-packeges:
    $ref: './paths/premium-packages.yaml#/premuim-packeges'
  /subscription:
    $ref: './paths/ai-subscription.yaml#/subscription'
  /premuim-subscription:
    $ref: './paths/premium-subscription.yaml#/premuim-subscription'
  /apply-coupon:
    $ref: './paths/apply-coupon.yaml#/apply-coupon'
  /pricing-request:
    $ref: './paths/pricing/create.yaml#/pricing-request-create'
  /pricing-library:
    $ref: './paths/pricing/library.yaml#/pricing-library'
  /pricing-library/details:
    $ref: './paths/pricing/details.yaml#/pricing-library-details'
  /pricing-library/delete:
    $ref: './paths/pricing/delete.yaml#/pricing-library-delete'
  /countries:
    $ref: './paths/lookups.yaml#/countries'
  /cities:
    $ref: './paths/lookups.yaml#/cities'
  /departments:
    $ref: './paths/departments/departments.yaml#/departments'
  /subdepartments:
    $ref: './paths/departments/subdepartments.yaml#/subdepartments'
  /departments-list:
    $ref: './paths/departments/departments-list.yaml#/departments-list'
  /reasons:
    $ref: './paths/lookups.yaml#/reasons'
  /payments:
    $ref: './paths/lookups.yaml#/payments'
  /setting:
    $ref: './paths/lookups.yaml#/setting'
  /chats:
    $ref: './paths/chat.yaml#/chats'
  /chat/messages:
    $ref: './paths/chat.yaml#/chat-messages'
  /chat/upload:
    $ref: './paths/chat.yaml#/chat-upload'
  /orders:
    $ref: './paths/order.yaml#/orders'
  /order:
    $ref: './paths/order.yaml#/order'
  /order/payment:
    $ref: './paths/order.yaml#/order-payment'
  /order/cancel:
    $ref: './paths/order.yaml#/order-cancel'
  /order/received:
    $ref: './paths/order.yaml#/client-confirm-received'
  /order/accept:
    $ref: './paths/order.yaml#/order-accept'
  /order/reject:
    $ref: './paths/order.yaml#/order-reject'
  /order/delivered:
    $ref: './paths/order.yaml#/provider-mark-delivered'
  /rate:
    $ref: './paths/rates.yaml#/rate'
  /return-request:
    $ref: './paths/return-request.yaml#/return-request-create'
  /return-request/delivered:
    $ref: './paths/return-request.yaml#/return-request-delivered'
  /return-request/accept:
    $ref: './paths/return-request.yaml#/return-request-accept'
  /return-request/reject:
    $ref: './paths/return-request.yaml#/return-request-reject'
  /return-request/received:
    $ref: './paths/return-request.yaml#/return-request-received'

components:
  securitySchemes:
    SecretKeyAuth:
      type: apiKey
      in: header
      name: secretkey
      description: >
        Application secret-key header required on every /api request by the
        mounted API router. Click Authorize once and enter a non-production
        test value. The backend reads the header name `secretkey`; using
        `x-secret-key` would not match the current route validation.
    ClientBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        Client JWT only. Its userType claim is client. Never use it on a
        provider-only operation.
    ProviderBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        Provider JWT only. Its userType claim is provider. Never use it on a
        client-only operation.

  parameters:
    LangHeader:
      name: lang
      in: header
      required: false
      schema:
        type: string
        enum: [ar, en]
        default: ar
        description: '`ar`: Arabic responses (default). `en`: English responses.'
      description: |
        Response language:
        - `ar`: Arabic responses (default).
        - `en`: English responses.
    ProductIdQuery:
      name: productId
      in: query
      required: true
      schema:
        type: string
        pattern: '^[a-fA-F0-9]{24}$'
        example: "665f1c2a9b4e1d0012ab34d0"
      description: |
        Required MongoDB ObjectId of the product to act on (24-hex).
        Used by Edit (`PATCH /products`), delete, details, visibility, and premium.
        Ownership is derived from the provider bearer token — do not send a foreign id.
        This is a **query** parameter on `/api/products`, not a path `/api/products/{id}`.

  responses:
    ValidationError:
      description: Request validation failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
          examples:
            invalidInput:
              value: { key: fail, message: "البيانات المرسلة غير صحيحة", status: 400 }
    UnauthorizedError:
      description: Authentication is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedErrorResponse'
          example: { key: unauthorized, message: "غير مصرح", status: 401 }
    NotFoundError:
      description: The requested resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/NotFoundErrorResponse'
          example: { key: notFound, message: "العنصر المطلوب غير موجود", status: 404 }
    AuctionInvalidStateError:
      description: Auction validation or lifecycle transition failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
          examples:
            invalidAuctionState:
              value: { key: fail, message: "لا يمكن تنفيذ الإجراء في حالة المزاد الحالية", status: 400 }
    AuctionUnauthorizedError:
      description: Provider authentication is missing, invalid, or does not own the Auction.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedErrorResponse'
          example: { key: unauthorized, message: "غير مصرح", status: 419 }
    AuctionNotFoundError:
      description: The owned Auction was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/NotFoundErrorResponse'
          example: { key: notFound, message: "المزاد غير موجود", status: 404 }
    ServerError:
      description: Unexpected server error without a stack trace or sensitive data.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example: { key: exception, message: "حدث خطأ، برجاء التواصل مع الدعم", status: 500 }

  schemas:
    ClientSignupRequest:
      type: object
      required: [name, countryCode, phone, password, confirmPassword]
      properties:
        name: { type: string, minLength: 2, maxLength: 80, example: "محمد" }
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0512345678" }
        email: { type: string, format: email, example: "user@example.com" }
        password:
          type: string
          format: password
          minLength: 8
          maxLength: 128
          example: "ExamplePass1!"
        confirmPassword:
          type: string
          format: password
          example: "ExamplePass1!"
        avatar:
          type: string
          format: binary
          description: "Optional profile image. Allowed formats (validated by file signature): jpg, jpeg, png, webp."

    ClientSignupSuccessResponse:
      type: object
      description: Signup response. It contains no password, OTP, or token.
      required: [key, message, status, data]
      properties:
        key:
          type: string
          enum: [needActive]
          description: '`needActive`: the client was created and must complete OTP activation.'
          example: needActive
        message: { type: string, example: "تم انشاء الحساب بنجاح" }
        status: { type: integer, example: 200 }
        data:
          $ref: '#/components/schemas/AuthUser'

    ClientSignupResponse:
      allOf:
        - $ref: '#/components/schemas/ClientSignupSuccessResponse'

    ProviderSignupRequest:
      type: object
      required:
        [name, countryCode, phone, password, confirmPassword, city, whatsappNumber]
      properties:
        avatar:
          type: string
          format: binary
          description: "Optional profile image. Allowed types: jpg, jpeg, png, webp."
        name:
          type: string
          minLength: 2
          maxLength: 80
          example: "Example Provider"
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0551234567" }
        email:
          type: string
          format: email
          example: "provider@example.com"
        password:
          type: string
          format: password
          minLength: 8
          maxLength: 128
          example: "ExamplePass1!"
        confirmPassword:
          type: string
          format: password
          minLength: 8
          maxLength: 128
          example: "ExamplePass1!"
        nationalId:
          type: string
          description: Optional provider national or residency ID.
          example: "1000000000"
        city:
          type: string
          description: Active and visible City document ID.
          example: "665f1c2a9b4e1d0012ab34cf"
        commercialRegisterImage:
          type: array
          maxItems: 3
          items:
            type: string
            format: binary
          description: "Optional commercial-register files. Maximum 3. Allowed types: jpg, jpeg, png, pdf."
        whatsappCountryCode:
          type: string
          default: "+966"
          example: "+966"
        whatsappNumber:
          type: string
          example: "0551234567"
    ProviderSignupData:
      allOf:
        - $ref: '#/components/schemas/ProfileData'
        - type: object
          description: |
            Safe provider DTO returned by the current signup service. Password,
            OTP, reset credentials, and internal file paths are absent.
          required: [approvalStatus]
          properties:
            userType: { type: string, enum: [provider], example: provider }
            approvalStatus: { type: string, enum: [wait], example: wait }
            active: { type: boolean, enum: [false], example: false }
    ProviderSignupSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [needActive], example: needActive }
        message:
          type: string
          example: "تم إرسال طلب إنشاء حساب مقدم الخدمة بنجاح"
        status: { type: integer, enum: [200], example: 200 }
        data:
          $ref: '#/components/schemas/ProviderSignupData'

    ProviderSignupResponse:
      allOf:
        - $ref: '#/components/schemas/ProviderSignupSuccessResponse'

    ProviderRoleRequest:
      type: object
      additionalProperties: false
      required: [city, whatsappNumber]
      description: |
        Provider business fields only. Account name, country code, phone, email,
        password, passwordHash, and AccountIdentity IDs are not accepted.
      properties:
        avatar:
          type: string
          format: binary
          description: "Optional image validated by file signature: jpg, jpeg, png, webp."
        nationalId:
          type: string
          example: "1000000000"
        city:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          example: "665f1c2a9b4e1d0012ab34cf"
        commercialRegisterImage:
          type: array
          maxItems: 3
          items:
            type: string
            format: binary
          description: "Optional files validated by signature: jpg, jpeg, png, pdf."
        whatsappCountryCode:
          type: string
          default: "+966"
          example: "+966"
        whatsappNumber:
          type: string
          example: "0551234567"

    AccountMode:
      type: object
      additionalProperties: false
      required:
        - activeMode
        - roles
        - availableModes
        - canSwitchToClient
        - canSwitchToProvider
        - providerStatus
        - clientId
        - providerId
      properties:
        activeMode:
          type: string
          enum: [client, provider_pending, provider]
        roles:
          type: array
          uniqueItems: true
          items: { type: string, enum: [client, provider] }
        availableModes:
          type: array
          uniqueItems: true
          items: { type: string, enum: [client, provider] }
        canSwitchToClient: { type: boolean }
        canSwitchToProvider: { type: boolean }
        providerStatus:
          type: string
          enum: [none, pending, accepted, rejected, disabled, blocked]
        clientId:
          type: string
          nullable: true
        providerId:
          type: string
          nullable: true

    AccountRoleAddResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: object
          additionalProperties: false
          required: [accountMode]
          properties:
            providerRequestId:
              type: string
              nullable: true
            clientId:
              type: string
              nullable: true
            accountMode:
              $ref: '#/components/schemas/AccountMode'

    SwitchModeRequest:
      type: object
      additionalProperties: false
      required: [mode]
      properties:
        mode:
          type: string
          enum: [client, provider]
          example: provider
          description: |
            Target mode only. No password is accepted. Provider mode is available
            only for an accepted, active, linked Provider profile.

    SwitchModeResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم تغيير وضع الحساب بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          description: |
            The safe active profile DTO itself. It is a Provider DTO when
            currentRole=provider and a Client DTO when currentRole=client.
            No client/provider wrapper is returned. A fresh access token for
            the selected profile is returned and must replace the caller's
            current session token.
          allOf:
            - $ref: '#/components/schemas/ProfileData'
            - type: object
              required: [currentRole, token, tokenType]
              properties:
                currentRole:
                  type: string
                  enum: [client, provider]
                  description: Always matches userType and accountMode.activeMode.
                token:
                  type: string
                  description: Fresh access token for the selected active profile.
                  example: "<account-access-token>"
                tokenType:
                  type: string
                  enum: [access]
                  example: access

    AuthActorRequest:
      type: object
      required: [phone, countryCode, userType]
      properties:
        phone:
          type: string
          description: Valid phone number for countryCode; normalized before lookup.
          example: "0512345678"
        countryCode:
          type: string
          description: Valid international calling code.
          example: "+966"
        userType:
          type: string
          enum: [client, provider]
          description: |
            User type; selects the exact identity model:
            - `client`: mobile client/customer.
            - `provider`: service provider/store owner.
          example: client

    LoginRequest:
      allOf:
        - $ref: '#/components/schemas/AuthActorRequest'
        - type: object
          required: [password, deviceId, deviceType]
          properties:
            password:
              type: string
              format: password
              minLength: 8
              maxLength: 128
              example: "123456789@aA"
            deviceId:
              type: string
              example: "device-installation-id"
            deviceType:
              type: string
              enum: [android, ios, web]
              description: |
                Device platform:
                - `android`: Android application.
                - `ios`: iOS application.
                - `web`: Web client.
              example: android

    SendCodeRequest:
      allOf:
        - $ref: '#/components/schemas/AuthActorRequest'
        - type: object
          required: [purpose]
          properties:
            purpose:
              type: string
              enum: [activation, forgot_password]
              description: |
                OTP purpose:
                - `activation`: إرسال كود لتفعيل حساب غير مفعّل.
                - `forgot_password`: إرسال كود لاستعادة كلمة المرور.
              example: forgot_password

    ActivateRequest:
      type: object
      required: [code, purpose]
      additionalProperties: false
      properties:
        userType:
          type: string
          enum: [client, provider]
          description: Required for activation and forgot_password; omitted for authenticated change_phone.
        countryCode:
          type: string
          description: Current country code for public flows; pending new country code for change_phone.
          example: "+966"
        phone:
          type: string
          description: |
            Required for activation and forgot_password. For authenticated
            change_phone, send the new phone and it must match the pending phone
            saved by PATCH /change-phone.
          example: "0500000000"
        code:
          type: string
          pattern: '^(?:[0-9]{4}|[0-9]{6})$'
          writeOnly: true
          description: Verification code sent by SMS. It is never returned in an API response.
        purpose:
          type: string
          enum: [activation, forgot_password, change_phone]
          description: |
            - `activation`: public account activation; deviceId and deviceType are required.
            - `forgot_password`: public recovery verification and reset-session creation.
            - `change_phone`: authenticated pending-phone verification.
        deviceId:
          type: string
          description: Required only when purpose is activation.
          example: "device-installation-id"
        deviceType:
          type: string
          enum: [android, ios, web]
          description: Required only when purpose is activation.
          example: android

    ChangePasswordRequest:
      allOf:
        - $ref: '#/components/schemas/AuthActorRequest'
        - type: object
          required: [resetToken, password, confirmPassword]
          properties:
            resetToken:
              type: string
              format: password
              writeOnly: true
              minLength: 32
              maxLength: 512
              description: Opaque, short-lived, one-time token returned by forgot_password OTP verification.
            password:
              type: string
              format: password
              minLength: 8
              maxLength: 128
              example: "NewPassw0rd!"
            confirmPassword:
              type: string
              format: password
              minLength: 8
              maxLength: 128
              example: "NewPassw0rd!"

    LocationRequest:
      type: object
      required: [longitude, latitude]
      properties:
        countryCode:
          type: string
          description: Required with `phone` only when no bearer token is selected (pending-provider flow).
          example: "+966"
        phone:
          type: string
          description: Required with `countryCode` only when no bearer token is selected (pending-provider flow).
          example: "0512345678"
        longitude:
          type: number
          format: double
          minimum: -180
          maximum: 180
          description: Longitude of the selected location.
          example: 46.6753
        latitude:
          type: number
          format: double
          minimum: -90
          maximum: 90
          description: Latitude of the selected location.
          example: 24.7136
        titleAr:
          type: string
          description: Optional Arabic short label supplied by the mobile application.
          example: "الموقع الرئيسي"
        titleEn:
          type: string
          description: Optional English short label supplied by the mobile application.
          example: "Main location"
        descriptionAr:
          type: string
          description: Optional Arabic human-readable location details supplied by the mobile application.
          example: "الرياض، المملكة العربية السعودية"
        descriptionEn:
          type: string
          description: Optional English human-readable location details supplied by the mobile application.
          example: "Riyadh, Saudi Arabia"

    LocationData:
      type: object
      required: [title, description, longitude, latitude]
      properties:
        title:
          type: string
          description: Localized title selected according to the `lang` request header.
          example: "الموقع الرئيسي"
        description:
          type: string
          description: Localized description selected according to the `lang` request header.
          example: "الرياض، المملكة العربية السعودية"
        longitude: { type: number, format: double, example: 46.6753 }
        latitude: { type: number, format: double, example: 24.7136 }

    LocationClientData:
      type: object
      description: Complete safe client DTO returned by `returnObject.client` after saving the location.
      required:
        - id
        - name
        - avatar
        - countryCode
        - phone
        - fullPhone
        - email
        - userType
        - status
        - statusText
        - notifyCount
        - isNotify
        - active
        - updatedPhone
        - updatedCountryCode
        - balance
        - address
        - location
      properties:
        id:
          type: string
          example: "665f1c2a9b4e1d0012ab34cd"
        name: { type: string, example: "محمد أحمد" }
        avatar:
          type: string
          format: uri
          example: "https://dashboard.example.com/assets/uploads/users/clients/665f1c2a9b4e1d0012ab34cd/avatar.png"
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0512345678" }
        fullPhone: { type: string, example: "+9660512345678" }
        email: { type: string, format: email, example: "client@example.com" }
        userType:
          type: string
          enum: [client]
          example: client
        status:
          type: string
          enum: [active, block, delete]
          example: active
        statusText: { type: string, example: "نشط" }
        notifyCount: { type: integer, example: 0 }
        isNotify: { type: boolean, example: true }
        active: { type: boolean, example: true }
        updatedPhone: { type: string, example: "" }
        updatedCountryCode: { type: string, example: "" }
        balance: { type: number, format: double, example: 0 }
        address: { type: string, example: "الرياض" }
        location:
          $ref: '#/components/schemas/LocationData'

    LocationProviderData:
      type: object
      description: Complete safe provider DTO returned by `returnObject.provider` after saving the location.
      required:
        - id
        - avatar
        - name
        - countryCode
        - phone
        - fullPhone
        - city
        - userType
        - status
        - statusText
        - notifyCount
        - isNotify
        - active
        - updatedPhone
        - updatedCountryCode
        - nationalId
        - rating
        - isAvailable
        - isAvailableText
        - commercialRegisterImages
        - approvalStatus
        - balance
        - location
        - premiumSubscription
        - whatsappNumber
        - whatsappCountryCode
      properties:
        id:
          type: string
          example: "665f1c2a9b4e1d0012ab34ce"
        avatar:
          type: string
          format: uri
          example: "https://dashboard.example.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34ce/avatar.png"
        name: { type: string, example: "متجر الرياض" }
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0551234567" }
        fullPhone: { type: string, example: "+9660551234567" }
        city:
          type: object
          required: [id, name]
          properties:
            id: { type: string, example: "665f1c2a9b4e1d0012ab3401" }
            name: { type: string, example: "الرياض" }
        userType:
          type: string
          enum: [provider]
          example: provider
        status:
          type: string
          enum: [active, block, delete]
          example: active
        statusText: { type: string, example: "نشط" }
        notifyCount: { type: integer, example: 0 }
        isNotify: { type: boolean, example: true }
        active: { type: boolean, example: true }
        updatedPhone: { type: string, example: "" }
        updatedCountryCode: { type: string, example: "" }
        nationalId: { type: string, example: "1012345678" }
        rating: { type: number, format: double, example: 4.8 }
        isAvailable: { type: boolean, example: true }
        isAvailableText: { type: string, example: "متاح" }
        commercialRegisterImages:
          type: array
          items: { type: string, format: uri }
          example:
            - "https://dashboard.example.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34ce/register-1.pdf"
        approvalStatus:
          type: string
          enum: [wait, accept, reject, cancelled]
          example: accept
        balance: { type: number, format: double, example: 0 }
        location:
          $ref: '#/components/schemas/LocationData'
        premiumSubscription: { type: string, example: "" }
        whatsappNumber: { type: string, example: "0551234567" }
        whatsappCountryCode: { type: string, example: "+966" }

    LocationResultData:
      description: Complete safe client or provider DTO selected by `userType`.
      oneOf:
        - $ref: '#/components/schemas/LocationClientData'
        - $ref: '#/components/schemas/LocationProviderData'
      discriminator:
        propertyName: userType
        mapping:
          client: '#/components/schemas/LocationClientData'
          provider: '#/components/schemas/LocationProviderData'

    LocationSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم حفظ الموقع بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          $ref: '#/components/schemas/LocationResultData'

    LocationPendingApprovalResponse:
      type: object
      description: Provider location was saved, but no usable access token is issued until administrator approval.
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [wait_approval], example: wait_approval }
        message: { type: string, example: "تم حفظ موقعك، حسابك قيد المراجعة من قبل الإدارة" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          $ref: '#/components/schemas/LocationProviderData'

    AuthUser:
      type: object
      description: Safe Auth DTO. Password and OTP fields are intentionally absent.
      required: [accountMode]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34cd" }
        name: { type: string, example: "Muhammed Mustafa" }
        avatar: { type: string, format: uri }
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0512345678" }
        fullPhone: { type: string, example: "+9660512345678" }
        userType:
          type: string
          enum: [client, provider]
          description: |
            Authenticated actor type:
            - `client`: mobile client/customer.
            - `provider`: service provider/store owner.
          example: client
        status:
          type: string
          enum: [active, block, delete]
          description: |
            Account status:
            - `active`: account may use the API subject to activation/approval checks.
            - `block`: account is blocked.
            - `delete`: account is soft-deleted and cannot authenticate.
          example: active
        active: { type: boolean, example: true }
        approvalStatus:
          type: string
          enum: [wait, accept, reject, cancelled]
          description: |
            Provider approval status:
            - `wait`: waiting for administrator review.
            - `accept`: approved to sign in.
            - `reject`: rejected by an administrator.
            - `cancelled`: approval request was cancelled.
          example: accept
        token:
          type: string
          description: JWT returned only for a successful login/account activation.
          example: "<access-token>"
        tokenType:
          type: string
          enum: [access]
          description: "access: normal client/provider bearer credential."
          example: access
        accountMode:
          description: Always present. AccountIdentity-backed actors receive the safe mode object; legacy actors receive null.
          nullable: true
          example: null
          allOf:
            - $ref: '#/components/schemas/AccountMode'

    AuthSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key:
          type: string
          enum: [success]
          description: '`success`: authentication completed successfully.'
          example: success
        message: { type: string, example: "تم تسجيل الدخول بنجاح" }
        status: { type: integer, example: 200 }
        data:
          $ref: '#/components/schemas/AuthUser'

    NeedActivationResponse:
      type: object
      description: Account exists but still needs activation. No bearer token or OTP is returned.
      required: [key, message, status, data]
      properties:
        key:
          type: string
          enum: [needActive]
          description: '`needActive`: the account still requires OTP activation.'
          example: needActive
        message: { type: string, example: "تفعيل الحساب مطلوب" }
        status: { type: integer, example: 200 }
        data:
          $ref: '#/components/schemas/AuthUser'

    SendCodeSuccessResponse:
      type: object
      description: OTP was accepted by the configured SMS provider. The OTP is never returned.
      required: [key, message, status]
      properties:
        key:
          type: string
          enum: [needActive]
          description: '`needActive`: an OTP was sent and the verification step is required.'
          example: needActive
        message: { type: string, example: "تم ارسال كود التحقق بنجاح" }
        status: { type: integer, example: 200 }

    ResetSessionResponse:
      type: object
      description: |
        Safe password-recovery verification response metadata. The one-time reset
        credential returned by the live API is intentionally omitted from the
        documentation schema and examples.
      required: [key, message, status, data]
      properties:
        key:
          type: string
          enum: [success]
          description: '`success`: OTP verification created a valid reset session.'
          example: success
        message: { type: string, example: "كود التحقق صحيح" }
        status: { type: integer, example: 200 }
        data:
          type: object
          required: [userType, expiresIn, purpose]
          properties:
            userType:
              type: string
              enum: [client, provider]
              description: |
                Owner of this reset session:
                - client: mobile client/customer.
                - provider: service provider/store owner.
              example: client
            expiresIn: { type: integer, example: 600 }
            purpose:
              type: string
              enum: [forgot_password]
              description: '`forgot_password`: this reset session can only be used to change a forgotten password.'
              example: forgot_password

    SuccessResponse:
      type: object
      required: [key, message, status]
      properties:
        key:
          type: string
          enum: [success]
          description: '`success`: the requested Auth operation completed successfully.'
          example: success
        message: { type: string, example: "تم تغيير كلمة المرور بنجاح" }
        status: { type: integer, example: 200 }

    SuccessEnvelope:
      allOf:
        - $ref: '#/components/schemas/SuccessResponse'
      description: Shared successful response envelope.

    PaginateMeta:
      type: object
      additionalProperties: false
      required: [currentPage, lastPage, perPage, total]
      properties:
        currentPage: { type: integer, minimum: 1, example: 1 }
        lastPage: { type: integer, minimum: 1, example: 1 }
        perPage: { type: integer, minimum: 1, example: 10 }
        total: { type: integer, minimum: 0, example: 1 }

    LocationDto:
      type: object
      additionalProperties: false
      required: [title, description, lat, lng]
      properties:
        title: { type: string, example: "الرياض" }
        description: { type: string, example: "المملكة العربية السعودية" }
        lat: { type: number, format: double, example: 24.7136 }
        lng: { type: number, format: double, example: 46.6753 }

    DeleteAccountBlockReasonEnum:
      type: string
      description: Stable reason code explaining why account deletion is blocked.
      enum:
        - wallet_balance_exists
        - active_paid_order_exists
        - active_or_upcoming_auction_exists
        - auction_participation_exists
        - auction_won_paid_not_received
        - provider_balance_exists
        - provider_pending_financial_transaction_exists
        - provider_active_business_operation_exists

    DeleteAccountBlockReason:
      type: object
      required: [code, message]
      additionalProperties: false
      properties:
        code:
          $ref: '#/components/schemas/DeleteAccountBlockReasonEnum'
        message:
          type: string
          description: Localized explanation selected by the `lang` header.
          example: "لا يمكن حذف الحساب لوجود رصيد في المحفظة"

    DeleteAccountBlockedResponse:
      type: object
      required: [key, message, status, data]
      additionalProperties: false
      properties:
        key: { type: string, enum: [fail], example: fail }
        message:
          type: string
          example: "لا يمكن حذف الحساب لوجود عمليات نشطة أو مستحقات مالية"
        status: { type: integer, enum: [400], example: 400 }
        data:
          type: object
          required: [canDelete, reasons]
          additionalProperties: false
          properties:
            canDelete: { type: boolean, enum: [false], example: false }
            reasons:
              type: array
              minItems: 1
              items:
                $ref: '#/components/schemas/DeleteAccountBlockReason'

    DeleteAccountSuccessResponse:
      type: object
      required: [key, message, status, data]
      additionalProperties: false
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم حذف الحساب بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: object
          required: [deleted]
          additionalProperties: false
          properties:
            deleted: { type: boolean, enum: [true], example: true }

    ProfileData:
      type: object
      description: Safe client/provider profile DTO. Provider-only and client-only fields are optional.
      required: [id, name, avatar, countryCode, phone, fullPhone, userType, status, active, accountMode]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34cd" }
        name: { type: string, example: "Example User" }
        avatar: { type: string, format: uri }
        countryCode: { type: string, example: "+966" }
        phone: { type: string, example: "0512345678" }
        fullPhone: { type: string, example: "+9660512345678" }
        email: { type: string, format: email, example: "user@example.com" }
        userType:
          type: string
          enum: [client, provider]
          description: "client: customer account. provider: service-provider account."
        status: { type: string, enum: [active, block, delete], example: active }
        statusText: { type: string, example: "نشط" }
        notifyCount: { type: integer, example: 0 }
        isNotify: { type: boolean, example: true }
        active: { type: boolean, example: true }
        updatedPhone: { type: string, example: "" }
        updatedCountryCode: { type: string, example: "" }
        balance: { type: number, example: 0 }
        address: { type: string, example: "" }
        location:
          type: object
          properties:
            title: { type: string, example: "" }
            description: { type: string, example: "" }
            longitude: { type: number, format: double, example: 46.6753 }
            latitude: { type: number, format: double, example: 24.7136 }
        city:
          type: object
          properties:
            id: { type: string, example: "665f1c2a9b4e1d0012ab34cf" }
            name: { type: string, example: "الرياض" }
        nationalId: { type: string, example: "1000000000" }
        rating: { type: number, format: double, example: 4.8 }
        isAvailable: { type: boolean, example: true }
        isAvailableText: { type: string, example: "متاح" }
        commercialRegisterImages:
          type: array
          maxItems: 3
          items: { type: string, format: uri }
        approvalStatus: { type: string, enum: [wait, accept, reject, cancelled], example: accept }
        premiumSubscription: { type: string, example: "" }
        whatsappNumber: { type: string, example: "0551234567" }
        whatsappCountryCode: { type: string, example: "+966" }
        token:
          type: string
          description: Current bearer token echoed by the existing profile response; example is not a real token.
          example: "<access-token>"
        tokenType: { type: string, enum: [access], example: access }
        accountMode:
          description: Always present. AccountIdentity-backed actors receive the safe mode object; legacy actors receive null.
          nullable: true
          example: null
          allOf:
            - $ref: '#/components/schemas/AccountMode'

    ProfileSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الملف الشخصي" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ProfileData' }

    ClientProfileResponse:
      allOf:
        - $ref: '#/components/schemas/ProfileSuccessResponse'
      description: Successful client profile response.

    ProviderProfileResponse:
      allOf:
        - $ref: '#/components/schemas/ProfileSuccessResponse'
      description: Successful provider profile response.

    ClientFavoriteProvider:
      type: object
      additionalProperties: false
      required: [id, name, location]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34ce" }
        name: { type: string, example: "أحمد محمد" }
        location:
          $ref: '#/components/schemas/LocationDto'

    ClientFavoriteActions:
      type: object
      additionalProperties: false
      required: [details, chat, hasAiPricing, aiPricing]
      properties:
        details: { type: boolean, example: true }
        chat: { type: boolean, example: true }
        hasAiPricing:
          type: boolean
          description: True exactly when `aiPricing` is a non-null object.
          example: true
        aiPricing:
          type: object
          nullable: true
          description: >
            AI pricing data linked to this Product itself, or null when the
            Product has no linked `aiPricingRequest`.
          required: [suggestedPrice, priceRangeMin, priceRangeMax]
          properties:
            suggestedPrice: { type: number, example: 1350 }
            priceRangeMin: { type: number, example: 1250 }
            priceRangeMax: { type: number, example: 1450 }

    ClientFavoriteItem:
      type: object
      additionalProperties: false
      required: [id, name, image, total, currency, provider, isFavorite, isFeatured, actions]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
        name: { type: string, example: "سماعة سوني" }
        image: { type: string, format: uri, example: "https://example.com/product.jpg" }
        total: { type: number, example: 1400 }
        currency: { type: string, example: "\uFDFC" }
        provider:
          $ref: '#/components/schemas/ClientFavoriteProvider'
        isFavorite: { type: boolean, enum: [true], example: true }
        isFeatured: { type: boolean, example: true }
        actions:
          $ref: '#/components/schemas/ClientFavoriteActions'

    SimilarProductCard:
      type: object
      additionalProperties: false
      required: [id, name, image, total, currency, provider, isFavorite, isFeatured, actions]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
        name: { type: string, example: "سماعة سوني" }
        image: { type: string, format: uri, example: "https://example.com/product.jpg" }
        total: { type: number, example: 1400 }
        currency: { type: string, enum: [SAR], example: "SAR" }
        provider:
          $ref: '#/components/schemas/ClientFavoriteProvider'
        isFavorite: { type: boolean, example: false }
        isFeatured: { type: boolean, example: true }
        actions:
          $ref: '#/components/schemas/ClientFavoriteActions'

    SimilarProductsResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم جلب الإعلانات المشابهة بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: object
          additionalProperties: false
          required: [items, paginate]
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/SimilarProductCard'
            paginate:
              $ref: '#/components/schemas/PaginateMeta'

    ClientFavoritesResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: object
          additionalProperties: false
          required: [title, displayType, items]
          properties:
            title: { type: string, example: "منتجاتك المفضلة" }
            displayType: { type: string, enum: [list], example: list }
            items:
              type: array
              items:
                $ref: '#/components/schemas/ClientFavoriteItem'
        paginate:
          $ref: '#/components/schemas/PaginateMeta'

    ClientFavoriteStateData:
      type: object
      additionalProperties: false
      required: [productId, isFavorite]
      properties:
        productId: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
        isFavorite: { type: boolean }

    AddClientFavoriteResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required: [data]
          properties:
            data:
              allOf:
                - $ref: '#/components/schemas/ClientFavoriteStateData'
                - type: object
                  properties:
                    isFavorite: { type: boolean, enum: [true], example: true }

    RemoveClientFavoriteResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required: [data]
          properties:
            data:
              allOf:
                - $ref: '#/components/schemas/ClientFavoriteStateData'
                - type: object
                  properties:
                    isFavorite: { type: boolean, enum: [false], example: false }

    ProviderNonPremiumProductItem:
      type: object
      additionalProperties: false
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34a1" }
        name: { type: string, example: "محراث زراعي يدوي" }

    ProviderNonPremiumProductsResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم جلب المنتجات غير المميزة بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items:
            $ref: '#/components/schemas/ProviderNonPremiumProductItem'

    NotificationData:
      type: object
      description: Safe notification DTO. Localized text; no password, OTP, or internal paths.
      required: [id, title, message, type]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34aa" }
        title: { type: string, example: "طلب جديد" }
        message: { type: string, example: "لديك طلب جديد رقم 1024" }
        receiver: { type: string, description: Recipient user id (always the authenticated user)., example: "665f1c2a9b4e1d0012ab34cd" }
        sender: { type: string, description: Sender user/admin id., example: "665f1c2a9b4e1d0012ab34ce" }
        type:
          type: string
          enum: [admin, order, product, phone, block, delete, support, profile, home, global, subscription, advertisement, auction, advertisementCommissionPaid, settlementDebit, message, call, complaint, contact]
          example: order
        itemId: { type: string, description: Id of the entity referenced by `type` (order/auction/product/...); empty when not applicable., example: "665f1c2a9b4e1d0012ab34ff" }
        productNumber: { type: string, description: Human-facing product number for product-related notifications; empty when not applicable., example: "1042" }
        order: { type: string, description: Backward-compatible alias of `itemId` for order notifications., example: "665f1c2a9b4e1d0012ab34ff" }
        timeAdd: { type: string, description: "Relative creation time, localized.", example: "منذ 3 ساعات" }

    NotificationsListResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الاشعارات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/NotificationData' }
        paginate:
          type: object
          properties:
            currentPage: { type: integer, example: 1 }
            lastPage: { type: integer, example: 3 }
            perPage: { type: integer, example: 20 }
            total: { type: integer, example: 45 }

    NotificationsCountResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "عدد الاشعارات" }
        status: { type: integer, enum: [200], example: 200 }
        data: { type: integer, description: Unread notifications count for the authenticated user., example: 4 }

    NotificationKeyItem:
      type: object
      required: [key, action]
      properties:
        key: { type: string, example: order }
        service: { type: string, description: Client-side route the key navigates to; empty for non-navigating keys., example: "/client/order" }
        action: { type: string, enum: [click, out], example: click }
        field: { type: string, example: "" }

    NotificationKeysResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "مفاتيح الاشعارات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/NotificationKeyItem' }

    BroadcastNotificationResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم إرسال الإشعار للمستخدمين النشطين بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: object
          required: [recipients, sent, failed]
          properties:
            recipients: { type: integer, example: 42, description: Active users targeted (excluding sender). }
            sent: { type: integer, example: 42, description: Recipients processed successfully. }
            failed: { type: integer, example: 0, description: Recipients that failed during fan-out. }

    ProviderRateListItem:
      type: object
      required: [id, name, date, rate, comment]
      description: Figma "تقييماتي" card; `name` is the client who rated.
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34e1" }
        name: { type: string, example: "أحمد الدوسري", description: Client who left the rating. }
        date: { type: string, example: "٢٠٢٤/٠١/٠٨", description: "`YYYY/MM/DD`; digits follow request `lang`." }
        rate: { type: number, example: 5, description: Stars from providerRate (type=user) or productRate (type=product). }
        comment: { type: string, example: "تعامل ممتاز وخدمة سريعة واحترافية." }

    ProviderRatesListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "جميع التقييمات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/ProviderRateListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    AddOrderRateRequest:
      type: object
      required: [orderId, productRate, providerRate]
      additionalProperties: false
      properties:
        orderId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Finished order id owned by the authenticated client.
          example: "665f1c2a9b4e1d0012ab34ce"
        productRate:
          type: number
          minimum: 1
          maximum: 5
          description: Product stars (1–5).
          example: 4
        productComment:
          type: string
          maxLength: 1000
          description: Optional free-text comment for the product only.
        providerRate:
          type: number
          minimum: 1
          maximum: 5
          description: Store/provider stars (1–5).
          example: 5
        providerComment:
          type: string
          maxLength: 1000
          description: Optional free-text comment for the store only.

    OrderRateItem:
      type: object
      properties:
        id: { type: string }
        orderId: { type: string }
        orderNumber: { type: integer }
        productRate: { type: number }
        providerRate: { type: number }
        date: { type: string, description: YYYY-MM-DD }
        product:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
            image: { type: string }
            rate: { type: number }
            comment: { type: string }
        provider:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
            image: { type: string }
            rating: { type: number }
            rate: { type: number }
            comment: { type: string }

    AddOrderRateResponse:
      allOf:
        - $ref: '#/components/schemas/OrderActionResponse'
      description: Message-only success envelope after rating a finished order.

    WalletChargeRequest:
      type: object
      required: [price]
      additionalProperties: false
      properties:
        price:
          type: number
          format: double
          exclusiveMinimum: true
          minimum: 0
          description: Amount to add to the authenticated client wallet. Must be greater than zero.
          example: 250

    WalletBalanceData:
      type: object
      required: [balance, balanceText, currency, currencyCode, currencySymbol]
      properties:
        balance:
          type: number
          format: double
          description: Current numeric wallet balance.
          example: 250
        balanceText:
          type: string
          description: Formatted balance including the configured rial sign.
          example: "250 ﷼"
        currency:
          type: string
          description: Rial sign represented by `U+FDFC`.
          example: "﷼"
        currencyCode:
          type: string
          enum: [SAR]
          description: ISO 4217 currency code.
          example: SAR
        currencySymbol:
          type: string
          description: Rial sign represented by `U+FDFC`.
          example: "﷼"

    WalletBalanceResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "المحفظة" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/WalletBalanceData' }

    SettlementRequest:
      type: object
      required: [bankName, accountName, accountNumber, iban]
      additionalProperties: false
      properties:
        bankName:
          type: string
          minLength: 3
          maxLength: 50
          description: Provider bank name.
          example: "الراجحي"
        accountName:
          type: string
          minLength: 3
          maxLength: 50
          description: Bank account holder name.
          example: "متجر زفييرا"
        accountNumber:
          type: string
          pattern: '^\d{9,18}$'
          description: Bank account number (digits only).
          example: "1234567890"
        iban:
          type: string
          pattern: '^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$'
          description: IBAN for the provider bank account.
          example: "SA0380000000608010167519"

    SettlementFinancialItem:
      type: object
      additionalProperties: false
      required:
        [id, orderNumber, orderNumberTranslated, orderTime, orderTimeTranslated, price, priceNum, vatPrice, appCommission, total]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d1" }
        orderNumberTranslated: { type: string, example: "رقم الطلب" }
        orderNumber: { type: string, example: "#12315" }
        orderTimeTranslated: { type: string, example: "وقت الطلب" }
        orderTime: { type: string, example: "20:00" }
        priceText: { type: string, example: "قيمة الطلب" }
        price: { type: string, example: "300 ر.س" }
        priceNum: { type: number, format: double, example: 300 }
        vatPriceTextTranslated: { type: string, example: "القيمة المضافة" }
        vatPriceText: { type: string, example: "0 ر.س" }
        vatPrice: { type: number, format: double, example: 0 }
        appCommissionTextTranslated: { type: string, example: "العمولة" }
        appCommissionText: { type: string, example: "50 ر.س" }
        appCommission: { type: number, format: double, example: 50 }
        totalTranslated: { type: string, example: "إجمالي المستحق" }
        totalText: { type: string, example: "250 ر.س" }
        total: { type: number, format: double, example: 250 }

    SettlementFinancialSummary:
      type: object
      required:
        [id, totalPrice, totalAppCommission, totalVatPrice, total, currency, financials]
      properties:
        id: { type: string, example: "" }
        totalPriceTranslated: { type: string, example: "إجمالي الطلبات" }
        totalPriceText: { type: string, example: "300 ر.س" }
        totalPrice: { type: number, format: double, example: 300 }
        totalAppCommissionTranslated: { type: string, example: "إجمالي العمولة" }
        totalAppCommissionText: { type: string, example: "50 ر.س" }
        totalAppCommission: { type: number, format: double, example: 50 }
        totalVatPriceTranslated: { type: string, example: "إجمالي القيمة المضافة" }
        totalVatPriceText: { type: string, example: "0 ر.س" }
        totalVatPrice: { type: number, format: double, example: 0 }
        totalTranslated: { type: string, example: "إجمالي المستحق" }
        totalText: { type: string, example: "250 ر.س" }
        total: { type: number, format: double, example: 250 }
        currency: { type: string, example: "ر.س" }
        financials:
          type: array
          items: { $ref: '#/components/schemas/SettlementFinancialItem' }
        settelmentDebitBtn:
          type: boolean
          description: Only returned on due financials; omitted from settlement details.
          example: true

    SettlementListItem:
      type: object
      required:
        [
          id,
          orderNumberTranslated,
          orderNumber,
          settlementNumber,
          orderTimeTranslated,
          orderTime,
          priceText,
          price,
          priceNum,
          appCommissionTextTranslated,
          appCommissionText,
          appCommission,
          vatPriceTextTranslated,
          vatPriceText,
          vatPrice,
          totalTranslated,
          totalText,
          total,
          amount,
          status,
          statusText,
          createdAt,
          createdAtIso,
          processedAt,
        ]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
        orderNumberTranslated: { type: string, example: "رقم الطلب" }
        orderNumber: { type: string, example: "#15" }
        settlementNumber: { type: integer, example: 15 }
        orderTimeTranslated: { type: string, example: "وقت الطلب" }
        orderTime: { type: string, example: "20:00" }
        priceText: { type: string, example: "قيمة الطلب" }
        price: { type: string, example: "300 ر.س" }
        priceNum: { type: number, format: double, example: 300 }
        appCommissionTextTranslated: { type: string, example: "العمولة" }
        appCommissionText: { type: string, example: "50 ر.س" }
        appCommission: { type: number, format: double, example: 50 }
        vatPriceTextTranslated: { type: string, example: "القيمة المضافة" }
        vatPriceText: { type: string, example: "0 ر.س" }
        vatPrice: { type: number, format: double, example: 0 }
        totalTranslated: { type: string, example: "إجمالي المستحق" }
        totalText: { type: string, example: "250 ر.س" }
        total: { type: number, format: double, example: 250 }
        amount: { type: number, format: double, example: 250 }
        status: { type: string, enum: [pending, accept, reject], example: pending }
        statusText: { type: string, example: "قيد المراجعة" }
        createdAt:
          type: string
          description: Existing localized relative time field retained for compatibility.
          example: "منذ دقيقة"
        createdAtIso:
          type: string
          format: date-time
          nullable: true
          example: "2026-07-23T10:44:37.414Z"
        processedAt:
          type: string
          format: date-time
          nullable: true
          example: null

    SettlementPagination:
      allOf:
        - $ref: '#/components/schemas/PaginateMeta'

    DueFinancialsData:
      allOf:
        - $ref: '#/components/schemas/SettlementFinancialSummary'
        - type: object
          required: [settelmentDebitBtn]
          properties:
            settelmentDebitBtn: { type: boolean, example: true }

    DueFinancialsResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "المعاملات المالية" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/DueFinancialsData' }
        paginate: { $ref: '#/components/schemas/SettlementPagination' }

    SettlementCreatedResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم إرسال طلب التسوية بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/SettlementListItem' }

    SettlementListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "التسويات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/SettlementListItem' }
        paginate: { $ref: '#/components/schemas/SettlementPagination' }

    SettlementDetailsData:
      allOf:
        - $ref: '#/components/schemas/SettlementFinancialSummary'
        - type: object
          description: >
            Same summary cards + financials list shape as due financials (المستحقات),
            plus status banner and accept/reject extras (transfer image / rejection reason).
            Omits settelmentDebitBtn and settlement list/meta fields (settlementNumber, amount,
            createdAt, paymentMethod, …).
          required:
            [status, statusText, alertMessage]
          properties:
            status: { type: string, enum: [pending, accept, reject], example: accept }
            statusText: { type: string, example: "منتهي" }
            alertMessage:
              type: string
              description: Status banner text (pending / accept / reject).
              example: "تم قبول الطلب"
            transferTitle:
              type: string
              description: Shown when status=accept (الحوالة). Empty otherwise.
              example: "الحوالة"
            image:
              type: string
              description: Transfer proof URL when status=accept; empty string otherwise.
              example: "https://example.com/uploads/settlements/transfer.png"
            rejectionReasonsTitle:
              type: string
              description: Shown when status=reject (اسباب الرفض). Empty otherwise.
              example: ""
            reason:
              type: string
              description: Rejection reason text when status=reject.
              example: ""

    SettlementDetailsResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تفاصيل التسوية" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/SettlementDetailsData' }

    SettlementConflictResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [fail], example: fail }
        message: { type: string, example: "لا يمكن إرسال طلب تسوية حاليًا" }
        status: { type: integer, enum: [409], example: 409 }
        data:
          type: object
          required: [reason]
          properties:
            reason:
              type: string
              enum:
                [no_due_amount, pending_settlement_exists, no_eligible_financial_transactions]
              example: pending_settlement_exists

    AboutData:
      type: object
      required: [description]
      properties:
        description: { type: string, example: "نبذة عن المنصة" }

    TermsData:
      type: object
      required: [terms]
      properties:
        terms: { type: string, example: "الشروط والأحكام الخاصة باستخدام المنصة" }

    FaqItem:
      type: object
      required: [id, question, answer]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
        question: { type: string, example: "كيف يمكنني استخدام التطبيق؟" }
        answer: { type: string, example: "يمكنك إنشاء حساب ثم تسجيل الدخول." }

    IntroPage:
      type: object
      required: [id, title, description, image]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d1" }
        title: { type: string, example: "اكتشف الخدمات" }
        description: { type: string, example: "تصفح الخدمات المتاحة بسهولة." }
        image:
          type: string
          format: uri
          example: "https://example.com/assets/uploads/intros/intro-01.png"

    AboutSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "من نحن" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/AboutData' }

    TermsSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الشروط والأحكام" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/TermsData' }

    PrivacySuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "سياسة الخصوصية" }
        status: { type: integer, enum: [200], example: 200 }
        data: { type: string, example: "سياسة الخصوصية الخاصة بالمنصة" }

    FaqsSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الأسئلة الشائعة" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/FaqItem' }

    IntrosSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الصفحات الافتتاحية" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/IntroPage' }

    IntroListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الصفحات الافتتاحية" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/IntroPage' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ChatListItem:
      type: object
      required: [id, lastMessage, avatar, name, time]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34c1" }
        lastMessage: { type: string, example: "مرحبا" }
        avatar: { type: string, example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34b1/avatar.png" }
        name: { type: string, example: "متجر المثال" }
        time: { type: string, example: "03:21 PM" }

    ChatListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "محادثات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/ChatListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ChatMessageItem:
      type: object
      required: [id, type, message, senderPath, senderId, senderName, senderAvatar, date]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d1" }
        type: { type: string, enum: [text, image], example: text }
        message: { type: string, example: "مرحبا" }
        senderPath: { type: string, example: client }
        senderId: { type: string, example: "665f1c2a9b4e1d0012ab34b2" }
        senderName: { type: string, example: "عميل المثال" }
        senderAvatar: { type: string, example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/users/clients/665f1c2a9b4e1d0012ab34b2/avatar.png" }
        date: { type: string, example: "منذ دقيقة" }

    ChatLayout:
      type: object
      required: [name, avatar]
      properties:
        name: { type: string, example: "متجر المثال" }
        avatar: { type: string, example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/users/providers/665f1c2a9b4e1d0012ab34b1/avatar.png" }

    ChatMessagesData:
      type: object
      required: [messages, chat]
      properties:
        messages:
          type: array
          items: { $ref: '#/components/schemas/ChatMessageItem' }
        chat: { $ref: '#/components/schemas/ChatLayout' }

    ChatMessagesResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تفاصيل المحادثة" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ChatMessagesData' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ChatUploadItem:
      type: object
      required: [url, file]
      properties:
        url: { type: string, example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/chat/665f1c2a9b4e1d0012ab34c1/image011753500000000.png" }
        file: { type: string, example: "image011753500000000.png" }

    ChatUploadResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم الرفع بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ChatUploadItem' }

    PackageCardItem:
      type: object
      required: [id, name, price, duration, type, status, isFree, features]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34a1" }
        name: { type: string, example: "باقة شهرية" }
        description: { type: string, example: "وصف الباقة" }
        price: { type: number, example: 100 }
        priceText: { type: string, example: "100 ر.س" }
        duration: { type: number, example: 1 }
        durationText: { type: string, example: "1 شهر" }
        type: { type: string, example: monthly }
        typeText: { type: string, example: "شهرية" }
        status: { type: string, example: active }
        isFree: { type: boolean, example: false }
        flagetext: { type: string, example: "الأكثر طلباً", description: "Optional marketing badge text for the package card." }
        features:
          type: array
          items:
            type: object
            required: [id, name]
            properties:
              id: { type: string, example: "665f1c2a9b4e1d0012ab34f1" }
              name: { type: string, example: "تحليل الأسعار" }
        currentPackage: { type: boolean, example: false }
        subscribeButton: { type: boolean, example: true }

    PackageListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "باقات التسعير AI" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/PackageCardItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    PremiumPackageItem:
      type: object
      required: [id, name, price, duration, type, status]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34b1" }
        name: { type: string, example: "باقة ذهبية" }
        description: { type: string, example: "وصف الباقة المميزة" }
        price: { type: number, example: 200 }
        priceText: { type: string, example: "200 ر.س" }
        duration: { type: number, example: 1 }
        durationText: { type: string, example: "1 شهر" }
        type: { type: string, example: monthly }
        typeText: { type: string, example: "شهرية" }
        status: { type: string, example: active }
        currentPackage: { type: boolean, example: false }
        subscribeButton: { type: boolean, example: true }

    PremiumPackageListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة الباقات المميزة" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/PremiumPackageItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    SubscribeSuccessResponse:
      type: object
      required: [key, message, status]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم الاشتراك بنجاح" }
        status: { type: integer, enum: [200], example: 200 }

    SubscribeRequest:
      type: object
      required: [id, paymentMethod]
      properties:
        id:
          type: string
          description: MongoDB id of the AI pricing package (`Package`).
          example: "665f1c2a9b4e1d0012ab34a1"
        paymentMethod:
          type: string
          enum: [wallet, online]
          example: wallet
        coupon:
          type: string
          description: Optional Coupon Mongo id from `PATCH /apply-coupon` preview (`data.coupon`). When set, `totalAfterCoupon` is required and the wallet/online charge uses the discounted amount. Coupons are rejected on free packages.
          example: "665f1c2a9b4e1d0012ab34c0"
        totalAfterCoupon:
          type: number
          description: Required when `coupon` is sent. Must match server recomputation within ±0.02 or the request fails with `totalAfterCouponMismatch`.
          example: 80

    PremiumSubscribeRequest:
      type: object
      required: [id, paymentMethod]
      properties:
        id:
          type: string
          description: MongoDB id of the premium package (`PremiumPackage`).
          example: "665f1c2a9b4e1d0012ab34b1"
        paymentMethod:
          type: string
          enum: [wallet, online]
          example: wallet
        ids:
          type: string
          description: >
            Optional. Product Mongo id(s) to mark as premium on success (multipart field).
            Accepts a single id (`6a65c8744274743cf7a7b2bb`) or a JSON array
            (`["6a65c8744274743cf7a7b2bb"]`). Missing/empty is allowed. When provided,
            every id must exist and belong to the authenticated provider or the request fails.
          example: '["6a65c8744274743cf7a7b2bb"]'
        coupon:
          type: string
          description: Optional Coupon Mongo id from `PATCH /apply-coupon`. When set, `totalAfterCoupon` is required.
          example: "665f1c2a9b4e1d0012ab34c0"
        totalAfterCoupon:
          type: number
          description: Required when `coupon` is sent. Must match server recomputation within ±0.02.
          example: 80

    ApplyCouponRequest:
      type: object
      required: [code, packageId]
      properties:
        code:
          type: string
          minLength: 6
          description: Coupon code (trimmed, min 6 chars).
          example: "SAVE20"
        packageId:
          type: string
          description: >
            Mongo id of an AI `Package` or a `PremiumPackage`.
            Server resolves which collection it belongs to (no `kind` field).
          example: "665f1c2a9b4e1d0012ab34a1"

    ApplyCouponPreviewData:
      type: object
      required: [coupon, code, packageId, price, total, discount, totalAfterCoupon]
      properties:
        coupon: { type: string, example: "665f1c2a9b4e1d0012ab34c0" }
        code: { type: string, example: "SAVE20" }
        packageId: { type: string, example: "665f1c2a9b4e1d0012ab34a1" }
        price: { type: number, example: 100 }
        total: { type: number, example: 100 }
        discount: { type: number, example: 20 }
        totalAfterCoupon: { type: number, example: 80 }
        discountText: { type: string, example: "قيمة الخصم" }
        afterDiscountText: { type: string, example: "السعر بعد الخصم" }
        beforeDiscountText: { type: string, example: "السعر قبل الخصم" }
        currency: { type: string, example: "﷼" }

    ApplyCouponSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم تطبيق الكوبون بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ApplyCouponPreviewData' }

    CountryListItem:
      type: object
      required: [_id, name]
      properties:
        _id: { type: string, example: "665f1c2a9b4e1d0012ab34c1" }
        name: { type: string, example: "السعودية" }
        image: { type: string, example: "https://example.com/assets/uploads/countries/sa.png" }
        code: { type: string, example: "+966" }
        iso: { type: string, example: "SA" }
        isVisible: { type: boolean, example: true }

    CountryListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة الدول" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/CountryListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    CityListItem:
      type: object
      required: [_id, name]
      properties:
        _id: { type: string, example: "665f1c2a9b4e1d0012ab34c2" }
        name: { type: string, example: "الرياض" }

    CityListResponse:
      type: object
      description: Response list envelope for GET /cities.
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة المدن" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/CityListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    DepartmentListItem:
      type: object
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34e1" }
        name: { type: string, example: "إلكترونيات" }

    DepartmentListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة الأقسام" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/DepartmentListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    SubDepartmentListItem:
      type: object
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34e2" }
        name: { type: string, example: "هواتف" }
        image: { type: string, example: "https://example.com/assets/uploads/subdepartments/phones.png" }

    SubDepartmentListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة الأقسام الفرعية" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/SubDepartmentListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ReasonItem:
      type: object
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d1" }
        name: { type: string, example: "مشكلة تقنية" }

    ReasonListResponse:
      type: object
      description: Response list envelope for GET /reasons.
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الأسباب" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/ReasonItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    PaymentMethodItem:
      type: object
      required: [id, name, slug, image]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34f1" }
        name: { type: string, example: "مدى" }
        slug: { type: string, example: "mada" }
        image: { type: string, example: "https://example.com/assets/uploads/payments/mada.png" }

    PaymentMethodListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "وسيلة الدفع" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/PaymentMethodItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    SettingData:
      type: object
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab3401" }
        linkAndroid: { type: string }
        linkApple: { type: string }
        linkWebSite: { type: string }
        phone: { type: string }
        phoneWhats: { type: string }
        email: { type: string }
        siteNameAr: { type: string, example: "كم تسوي" }
        siteNameEn: { type: string, example: "Kam Teswa" }

    SettingSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "الإعدادات" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/SettingData' }

    ContactMessageCreatedData:
      type: object
      required: [id, status]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d1" }
        status: { type: string, enum: [new, reviewed, closed], example: new }

    ContactMessageCreatedResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم إرسال رسالتك بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ContactMessageCreatedData' }

    ComplaintCreatedData:
      type: object
      required: [id, number, status]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d2" }
        number: { type: string, example: "12315" }
        status: { type: string, enum: [pending, in_progress, replied], example: pending }

    ComplaintCreatedResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم إرسال الشكوى/المقترح بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ComplaintCreatedData' }

    ComplaintListItem:
      type: object
      additionalProperties: false
      required: [id, title]
      properties:
        id: { type: string, example: "6a61f095448284602d21e145" }
        title: { type: string, example: "شكوى رقم #1" }

    ComplaintSummary:
      type: object
      required: [id, number, title, status, statusText, createdAt]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34d2" }
        number: { type: string, pattern: '^#[0-9]+$', example: "#12315" }
        title: { type: string, example: "عنوان الشكوى / المقترح" }
        status: { type: string, enum: [pending, in_progress, replied], example: pending }
        statusText: { type: string, example: "في انتظار الرد" }
        createdAt:
          type: string
          description: Complaint date formatted by Moment using the requested language.
          example: "٢٠٢٦/٠٧/٢٣"

    ComplaintDetails:
      allOf:
        - $ref: '#/components/schemas/ComplaintSummary'
        - type: object
          required: [message, adminReply]
          properties:
            message: { type: string, example: "نص الشكوى / المقترح" }
            adminReply: { type: string, example: "الرد الإداري" }

    ComplaintPagination:
      allOf:
        - $ref: '#/components/schemas/PaginateMeta'

    ComplaintListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة الشكاوى" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/ComplaintListItem' }
        paginate: { $ref: '#/components/schemas/ComplaintPagination' }

    AttributeItem:
      type: object
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34e8" }
        name: { type: string, example: "المقاس" }

    AttributeColorItem:
      type: object
      required: [id, name, colorCode]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34e9" }
        name: { type: string, example: "اخضر" }
        colorCode:
          type: string
          description: HEX colour code.
          example: "#00ff04"

    AttributeSizeItem:
      type: object
      required: [id, name]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34f1" }
        name: { type: string, example: "كبير" }

    AttributePagination:
      allOf:
        - $ref: '#/components/schemas/PaginateMeta'

    AttributeListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة السمات" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/AttributeItem' }
        paginate: { $ref: '#/components/schemas/AttributePagination' }

    AttributeColorListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة ألوان السمة" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/AttributeColorItem' }
        paginate: { $ref: '#/components/schemas/AttributePagination' }

    AttributeSizeListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "قائمة أحجام السمة" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/AttributeSizeItem' }
        paginate: { $ref: '#/components/schemas/AttributePagination' }

    ComplaintDetailsResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تفاصيل الشكوى" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/ComplaintDetails' }

    PricingRequestItem:
      type: object
      description: Create response (`POST /pricing-request`) — base pricing fields only.
      required: [id, name, image, suggestedPrice, suggestedPriceTXT, priceRangeMin, priceRangeMax, currency, createdAt]
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34b1" }
        name: { type: string, example: "آيفون 13" }
        image: { type: string, example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/users/providers/pricing-requests/665f1c2a9b4e1d0012ab34b1/image901785232499932.png" }
        specifications: { type: string, example: "اللون أسود - الحالة جديد - الذاكرة 256" }
        suggestedPrice: { type: number, example: 450 }
        suggestedPriceTXT: { type: string, example: "450 ﷼" }
        priceRangeMin: { type: number, example: 383 }
        priceRangeMax: { type: number, example: 518 }
        currency: { type: string, example: "﷼" }
        createdAt: { type: string, example: "2026/07/28 11:00 صباحا" }

    PricingRequestListItem:
      allOf:
        - $ref: '#/components/schemas/PricingRequestItem'
        - type: object
          description: Library list card (`GET /pricing-library`) — base fields + action buttons.
          required: [detailsButton, deleteButton]
          properties:
            detailsButton: { type: boolean, example: true }
            deleteButton: { type: boolean, example: true }

    PricingRequestDetails:
      allOf:
        - $ref: '#/components/schemas/PricingRequestItem'
        - type: object
          description: Library item details (`GET /pricing-library/details`) — same base fields as create.
    PricingDeleteSuccessResponse:
      type: object
      required: [key, message, status]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم حذف طلب التسعير بنجاح" }
        status: { type: integer, enum: [200], example: 200 }

    PricingRequestSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم التسعير بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/PricingRequestItem' }

    PricingRequestDetailsSuccessResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تفاصيل طلب التسعير" }
        status: { type: integer, enum: [200], example: 200 }
        data: { $ref: '#/components/schemas/PricingRequestDetails' }

    PricingLibraryListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "مكتبة التسعير" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items: { $ref: '#/components/schemas/PricingRequestListItem' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ProviderProductLocalizedText:
      type: object
      additionalProperties: false
      required: [ar, en]
      properties:
        ar: { type: string, minLength: 1, maxLength: 500 }
        en: { type: string, minLength: 1, maxLength: 500 }

    ProviderProductVariantAttributeInput:
      type: object
      additionalProperties: false
      required: [attributeId, valueId]
      properties:
        attributeId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Parent Attribute MongoId (e.g. Size). Never use an AttributeValue `_id` here.
          example: "6a69c8eadef712b95ba86e82"
        valueId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: AttributeValue MongoId that belongs to `attributeId` (e.g. XL under Size).
          example: "6a69c8ecdef712b95ba86e9c"

    ProviderProductVariantInput:
      type: object
      additionalProperties: false
      required: [attributes, quantity, price]
      properties:
        attributes:
          type: array
          minItems: 1
          items: { $ref: '#/components/schemas/ProviderProductVariantAttributeInput' }
          description: Must contain every selected attribute exactly once.
        quantity: { type: integer, minimum: 1 }
        price: { type: number, exclusiveMinimum: true, minimum: 0, multipleOf: 0.01 }
        discountType:
          type: string
          enum: [none, percentage, fixed]
          default: none
        discountValue: { type: number, minimum: 0, multipleOf: 0.01, default: 0 }

    ProviderProductCreateRequest:
      type: object
      additionalProperties: false
      required:
        [type, departmentId, subdepartmentId, name, description, condition, pricingMethod, images]
      properties:
        type:
          type: string
          enum: [simple, variant]
          description: |
            Product shape selector.
            - `simple`: one price and one stock quantity; omit `attributes` and `variants`.
            - `variant`: selectable options; send `attributes` and `variants` as JSON strings.
        departmentId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Must identify an active, non-deleted department.
        subdepartmentId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Must identify an active, non-deleted subdepartment that belongs to departmentId.
        name:
          type: string
          x-kamteswa-widget: json-textarea
          example: '{"ar":"سماعات سوني","en":"Sony headphones"}'
          description: JSON-encoded form-data text field with exactly ar and en (2–200 characters each).
        description:
          type: string
          x-kamteswa-widget: json-textarea
          example: '{"ar":"سماعات بحالة ممتازة","en":"Headphones in excellent condition"}'
          description: JSON-encoded form-data text field with exactly ar and en (1–500 characters each).
        condition: { type: string, enum: [new, used] }
        pricingMethod:
          type: string
          enum: [manual, ai]
          description: |
            Source of the selling price the client sends. Always required.
            - `manual`: client-chosen price; `aiPricingRequestId` is optional.
            - `ai`: price sourced from AI; `aiPricingRequestId` is required and
              must be a completed provider-owned PricingRequest (`POST /pricing-request` → `data.id`).
        aiPricingRequestId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: |
            Required when `pricingMethod=ai`. First call
            [`POST /pricing-request`](#/AI%20Pricing/createPricingRequest),
            then pass the returned `data.id` here. Optional when
            `pricingMethod=manual` (may still be sent to store the AI suggestion
            for display). Suggested price is loaded server-side; client-submitted
            AI prices are rejected. PricingRequest currently has no expiry or
            product-fingerprint fields.
        quantity:
          type: integer
          minimum: 1
          description: |
            Available stock quantity.
            Required for `type=simple`.
            For `type=variant`, omit this field — quantity belongs to each variant.
        price:
          type: number
          exclusiveMinimum: true
          minimum: 0
          multipleOf: 0.01
          description: |
            Product price.
            Required for `type=simple`.
            For `type=variant`, omit this field — price belongs to each variant.
        discountType:
          type: string
          enum: [none, percentage, fixed]
          default: none
          description: |
            Discount strategy for a simple product.
            Allowed values: `none`, `percentage`, `fixed`.
            Use `0` for `discountValue` when `discountType=none`.
            Omit for `type=variant` (each variant has its own discount).
        discountValue:
          type: number
          minimum: 0
          multipleOf: 0.01
          default: 0
          description: |
            Numeric discount value for a simple product.
            Percentage or fixed amount depending on `discountType`.
            Use `0` when `discountType=none`. Omit for `type=variant`.
        attributes:
          type: string
          x-kamteswa-widget: json-textarea
          example: '["6a69c8eadef712b95ba86e82","6a69c8eadef712b95ba86e88"]'
          description: |
            JSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.

            Required when `type=variant`.
            Forbidden when `type=simple`.

            Do not send this field for simple products.
            Do not send an empty value.
            Do not use “Send empty value” in Swagger or Postman.

            These IDs are the selected Product Attributes for the variant product
            (for example الحجم and اللون). Every variant row must use only
            `attributeId` values included in this array.

            Example (Size + Color):
            ```json
            [
              "6a69c8eadef712b95ba86e82",
              "6a69c8eadef712b95ba86e88"
            ]
            ```
        variants:
          type: string
          x-kamteswa-widget: json-textarea
          example: '[{"attributes":[{"attributeId":"6a69c8eadef712b95ba86e82","valueId":"6a69c8ecdef712b95ba86e9c"},{"attributeId":"6a69c8eadef712b95ba86e88","valueId":"6a69c8eddef712b95ba86eb0"}],"quantity":5,"price":120,"discountType":"none","discountValue":0}]'
          description: |
            JSON-encoded array of variant objects submitted as a multipart/form-data **text** field.

            Required when `type=variant`.
            Forbidden when `type=simple`.

            Do not send this field for simple products.
            Do not send an empty value.
            Do not send nested multipart keys such as
            `variants[0][attributes][0][attributeId]`.
            Send the full JSON array as the value of this single text field.

            Each variant is **one** purchasable combination.
            Wrong: Size=S and Size=M inside the same variant.
            Right: S as variant #1, M as variant #2.

            ID ownership:
            - `attributeId` = parent Attribute `_id`
            - `valueId` = AttributeValue `_id` that belongs to that attribute
            Do not use a value `_id` as `attributeId`.

            Each variant object must include exactly:
            - `attributes`: array of `{ attributeId, valueId }`
            - `quantity` (integer ≥ 1)
            - `price` (number > 0)
            - `discountType` (`none` | `percentage` | `fixed`)
            - `discountValue` (number; use `0` when `discountType=none`)

            Backend-enforced rules:
            - every `attributeId` inside a variant must appear in the top-level `attributes` array
            - each variant must include every selected attribute exactly once
            - each `valueId` must belong to its `attributeId`
            - duplicate variant combinations are rejected
            - quantity, price, and discountValue must be numeric

            Example (XL + Red):
            ```json
            [
              {
                "attributes": [
                  {
                    "attributeId": "6a69c8eadef712b95ba86e82",
                    "valueId": "6a69c8ecdef712b95ba86e9c"
                  },
                  {
                    "attributeId": "6a69c8eadef712b95ba86e88",
                    "valueId": "6a69c8eddef712b95ba86eb0"
                  }
                ],
                "quantity": 5,
                "price": 120,
                "discountType": "none",
                "discountValue": 0
              }
            ]
            ```
        images:
          type: array
          minItems: 1
          maxItems: 10
          items: { type: string, format: binary }
          description: |
            Product images submitted as files through multipart/form-data.
            JPEG/PNG/WEBP, maximum 5MB each, 1–10 files. Direct upload; no media registry.
        video:
          type: string
          format: binary
          x-maxBytes: 20971520
          description: Optional product video file (MP4/MOV, maximum 20MB). Clients must validate or compress it before starting multipart upload.

    ProviderProductUpdateRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        type:
          type: string
          enum: [simple, variant]
          description: |
            Product shape selector for the merged result.
            - `simple`: omit `attributes` and `variants`; use product-level price/quantity.
            - `variant`: send `attributes` and `variants` as JSON strings.
        departmentId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Must identify an active, non-deleted department.
        subdepartmentId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Must identify an active, non-deleted subdepartment that belongs to the final departmentId.
        name:
          type: string
          x-kamteswa-widget: json-textarea
          example: '{"ar":"اسم محدث","en":"Updated name"}'
          description: JSON-encoded form-data text field with exactly ar and en (2–200 characters each).
        description:
          type: string
          x-kamteswa-widget: json-textarea
          example: '{"ar":"وصف محدث","en":"Updated description"}'
          description: JSON-encoded form-data text field with exactly ar and en (1–500 characters each).
        condition: { type: string, enum: [new, used] }
        pricingMethod:
          type: string
          enum: [manual, ai]
          description: |
            Updates the source of the selling price. When set to `ai`, also send
            a completed provider-owned `aiPricingRequestId`.
        aiPricingRequestId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: |
            To attach/replace the AI suggestion, first call
            [`POST /pricing-request`](#/AI%20Pricing/createPricingRequest) and
            pass its returned `data.id` here. Required when `pricingMethod=ai`.
            The request must be completed, non-deleted, and owned by the
            authenticated provider.
        quantity:
          type: integer
          minimum: 1
          description: |
            Simple only when the effective product type is `simple`.
            Omit when updating a `variant` product (stock lives in `variants[].quantity`).
        price:
          type: number
          exclusiveMinimum: true
          minimum: 0
          multipleOf: 0.01
          description: |
            Simple only when the effective product type is `simple`.
            Omit for `variant` — use `variants[].price`.
        discountType:
          type: string
          enum: [none, percentage, fixed]
          description: |
            Simple only. Enum: `none` | `percentage` | `fixed`.
            When `none`, send `discountValue` as `0`. Omit for variant products.
        discountValue:
          type: number
          minimum: 0
          multipleOf: 0.01
          description: |
            Simple only. Percentage or fixed amount depending on `discountType`.
            Use `0` with `discountType=none`. Omit for variant products.
        attributes:
          type: string
          x-kamteswa-widget: json-textarea
          example: '["6a69c8eadef712b95ba86e82","6a69c8eadef712b95ba86e88"]'
          description: |
            JSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.

            Required when the effective product `type=variant`.
            Forbidden when the effective product `type=simple`.

            Do not send this field for simple products.
            Do not send an empty value.
            Do not use “Send empty value”.

            These IDs are the selected Product Attributes (for example الحجم and اللون).
            Every variant row must use only `attributeId` values included here.
        variants:
          type: string
          x-kamteswa-widget: json-textarea
          example: '[{"attributes":[{"attributeId":"6a69c8eadef712b95ba86e82","valueId":"6a69c8ecdef712b95ba86e9c"},{"attributeId":"6a69c8eadef712b95ba86e88","valueId":"6a69c8eddef712b95ba86eb0"}],"quantity":5,"price":120,"discountType":"none","discountValue":0},{"attributes":[{"attributeId":"6a69c8eadef712b95ba86e82","valueId":"6a69c8ecdef712b95ba86e9c"},{"attributeId":"6a69c8eadef712b95ba86e88","valueId":"6a69c8eddef712b95ba86ea8"}],"quantity":3,"price":125,"discountType":"none","discountValue":0}]'
          description: |
            JSON-encoded array of variant objects submitted as a multipart/form-data **text** field.

            Required when the effective product `type=variant`.
            Forbidden when the effective product `type=simple`.

            Do not send this field for simple products.
            Do not send an empty value.
            Do not send nested multipart keys — send one JSON string only.

            Each variant is one purchasable combination (`attributeId` + `valueId`),
            plus `quantity`, `price`, `discountType` (`none` | `percentage` | `fixed`),
            and `discountValue`.
            Same `attributeId` must not repeat inside one variant.
            When updating variants, send the full desired array (matrix replace).
        images:
          type: array
          minItems: 1
          maxItems: 10
          items: { type: string, format: binary }
          description: |
            Optional new product images (JPEG/PNG/WEBP, max 5MB each, 1–10 files per request).
            Default `imageMode=append`: keep existing images and append these files.
            With `imageMode=replace`: these files become the full image list.
            When omitted (and no `removedImages`), existing images are preserved.
        removedImages:
          type: string
          x-kamteswa-widget: json-textarea
          example: '["image241785318275776.png"]'
          description: |
            Text field listing existing product image identifiers to remove
            (filename basename, or a product-scoped URL/path from Product Details).

            Preferred (Postman / mobile):
            ```json
            ["image241785318275776.png"]
            ```

            Also accepted (Swagger UI multipart quirk):
            ```
            image241785318275776.png
            ```
            Swagger often coerces a JSON array through `Array#toString()`, so a
            single filename or comma-separated filenames are accepted and
            normalized to an array server-side.

            Submitted as multipart/form-data text — not nested keys.
            Do not check “Send empty value”.
            Path traversal (`..`) and foreign product paths are rejected.
            Can be combined with new `images` uploads under `imageMode=append`.
        imageMode:
          type: string
          enum: [append, replace]
          default: append
          description: |
            How uploaded images interact with the existing gallery.
            - `append` (default): keep remaining old images, apply `removedImages`, then append uploads.
            - `replace`: discard all old images and use uploaded `images` only (uploads required).
        video:
          type: string
          format: binary
          description: When supplied, replaces the previous product video (MP4/MOV, max 20MB).
        removeVideo:
          type: boolean
          default: false
          description: When `true`, clears the product video without requiring a new video file.

    ProviderProductLocation:
      type: object
      required: [title, description, lat, lng]
      properties:
        title: { type: string }
        description: { type: string }
        lat: { type: number }
        lng: { type: number }

    ClientSharedProductActions:
      type: object
      additionalProperties: false
      required: [chatButton, chatId, detailsButton, aiPricingButton, isFav]
      properties:
        chatButton:
          type: boolean
          enum: [true]
          description: The client may open an existing chat or start one with the product provider.
        chatId:
          type: string
          description: Existing direct-chat id, or an empty string when no chat exists yet.
        detailsButton: { type: boolean, enum: [true] }
        aiPricingButton:
          type: boolean
          enum: [true]
          description: Opens the authenticated AI pricing/repricing flow.
        isFav:
          type: boolean
          description: Current client's favorite state for this product.

    ClientSharedProductCard:
      type: object
      additionalProperties: false
      required:
        [id, name, image, total, currency, provider, isFav, isFeatured, actions]
      properties:
        id: { type: string }
        name:
          type: string
          description: Localized product name for the request `lang`.
        image:
          type: string
          description: Resolved first product image URL, or an empty string.
        total: { type: number }
        currency: { type: string }
        provider:
          type: object
          additionalProperties: false
          required: [id, name, location]
          properties:
            id: { type: string }
            name: { type: string }
            location:
              $ref: '#/components/schemas/ProviderProductLocation'
        isFav: { type: boolean }
        isFeatured: { type: boolean }
        actions:
          $ref: '#/components/schemas/ClientSharedProductActions'

    ClientSharedProductDetails:
      type: object
      additionalProperties: false
      required:
        [id, number, images, name, price, priceText, count, countText, isAvailable,
         availabilityText, details, instructions, region, status, statusText, expireAt,
         subDepartment, ratings, rating, ratesCount, provider, addGoldButton, editButton,
         deleteButton, rateButton, rateData, isFav, addToCartButton, addToFavoriteButton,
         isGold, goldStatus, goldStatusText, similarAds, actions]
      properties:
        id: { type: string }
        number:
          oneOf:
            - type: integer
            - type: string
              enum: ['']
          description: Human product number, or an empty string when unavailable.
        images: { type: array, items: { type: string } }
        name: { type: string }
        price: { type: number }
        priceText: { type: string }
        count: { type: integer }
        countText: { type: string }
        isAvailable: { type: boolean }
        availabilityText: { type: string }
        details: { type: string }
        instructions: { type: string }
        region:
          oneOf:
            - type: string
            - type: object
              properties:
                id: { type: string }
                name: { type: string }
        status: { type: string }
        statusText: { type: string }
        expireAt: { type: string, nullable: true }
        subDepartment:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
            image: { type: string }
        ratings:
          type: array
          items: { type: object }
        rating: { type: number }
        ratesCount: { type: integer }
        provider:
          type: object
          nullable: true
          properties:
            id: { type: string }
            name: { type: string }
            fullPhone: { type: string }
            avatar: { type: string }
        addGoldButton: { type: boolean }
        editButton: { type: boolean }
        deleteButton: { type: boolean }
        rateButton: { type: boolean }
        rateData: { type: object, nullable: true }
        isFav: { type: boolean }
        addToCartButton: { type: boolean }
        addToFavoriteButton: { type: boolean }
        isGold: { type: boolean }
        goldStatus: { type: string }
        goldStatusText: { type: string }
        similarAds:
          type: object
          required: [title, items, hasMore]
          properties:
            title: { type: string }
            items:
              type: array
              maxItems: 4
              items: { $ref: '#/components/schemas/ClientSharedProductCard' }
            hasMore: { type: boolean }
        actions:
          $ref: '#/components/schemas/ClientSharedProductActions'

    ProviderProductCard:
      type: object
      required:
        [id, name, type, price, priceText, primaryImage, visibility, pricingMethod]
      properties:
        id: { type: string }
        name:
          type: string
          description: Localized product name for the request `lang` header.
        type: { type: string, enum: [simple, variant] }
        price: { type: number }
        priceText:
          type: string
          example: "100 ﷼"
          description: Display price with currency for the request `lang`.
        primaryImage:
          type: string
          description: First image from `images`, or empty string when none.
        visibility:
          type: boolean
          description: Owner visibility flag — `true` when the product is not hidden (`!isHidden`).
        pricingMethod:
          type: string
          enum: [manual, ai]
          description: Whether the selling price was set manually or from AI.

    ProviderProductDetails:
      type: object
      required:
        [id, type, typeText, name, description, primaryImage, provider, location, price, priceText,
         pricingMethod, pricingMethodText, aiSuggestedPrice, isVisible, visibilityText, status, statusText,
         moderationStatus, moderationStatusText,
         condition, conditionText, isPremium, isPremiumText, date, time, actions, department,
         subdepartment, quantity, quantityText, attributes, variants, images, video, pricing]
      properties:
        id: { type: string }
        type: { type: string, enum: [simple, variant] }
        typeText:
          type: string
          description: Localized product type for the request `lang` header.
        name:
          type: string
          description: Localized product name for the request `lang` header.
        primaryImage:
          type: string
          description: First resolved product image URL, or an empty string when no image exists.
        provider:
          type: object
          properties:
            id: { type: string }
            name: { type: string }
            avatar: { type: string }
            location: { $ref: '#/components/schemas/ProviderProductLocation' }
        location: { $ref: '#/components/schemas/ProviderProductLocation' }
        price: { type: number }
        priceText:
          type: string
          example: "122000 SAR"
          description: Display price with currency code for the request `lang`.
        pricingMethod:
          type: string
          enum: [manual, ai]
        pricingMethodText:
          type: string
          description: Localized pricing method for the request `lang` header.
        aiSuggestedPrice:
          type: number
          nullable: true
          description: AI suggestion when AI pricing exists; otherwise null (never a synthetic zero).
        currency: { type: string, example: "SAR" }
        isVisible: { type: boolean }
        visibilityText:
          type: string
          description: Localized visibility label for the request `lang` header.
        status: { type: string }
        statusText:
          type: string
          description: Localized workflow status for the request `lang` header.
        moderationStatus:
          type: string
          enum: [wait, accept, reject, cancelled]
        moderationStatusText:
          type: string
          description: Localized moderation status for the request `lang` header.
        condition: { type: string, enum: [new, used] }
        conditionText:
          type: string
          description: Localized condition for the request `lang` header.
        isPremium: { type: boolean }
        isPremiumText:
          type: string
          description: Localized premium flag for the request `lang` header.
        date:
          type: string
          example: "2025-01-25"
          description: Created date in `YYYY-MM-DD` format.
        time:
          type: string
          example: "04:53 مساء"
          description: Localized created time with meridiem (`صباحا`/`مساء` or `am`/`pm`).
        actions:
          type: object
          properties:
            edit: { type: boolean }
            visibility: { type: boolean }
            delete: { type: boolean }
        number: { type: integer, nullable: true }
        rejectionReason: { type: string, nullable: true }
        description:
          type: string
          description: Localized product description for the request `lang` header.
        department: { type: object, nullable: true }
        subdepartment: { type: object, nullable: true }
        quantity:
          type: integer
          description: Simple-product stock, or the sum of all variant quantities.
        quantityText:
          type: string
          example: "100 قطعه"
          description: Localized display of total stock for simple and variant products.
        attributes:
          type: array
          description: |
            Product option catalogue for variant products.
            Groups each selected Attribute with only the AttributeValue records
            used by this product's variants (deduplicated).
            Includes localized `name`, attribute-level `kind` when reliable
            (`size` / `color`), and values with `id`, `name`, `kind`, `colorCode`.
            Simple products return an empty array.
            Create/Edit still accept the request JSON-string format; this field
            is the read/display catalogue only.
          items:
            type: object
            required: [id, name, kind, values]
            properties:
              id: { type: string }
              name: { type: string }
              kind:
                type: string
                description: Attribute kind when known (`size` / `color`), else empty string.
              values:
                type: array
                items:
                  type: object
                  required: [id, name, kind, colorCode]
                  properties:
                    id: { type: string }
                    name: { type: string }
                    kind: { type: string, enum: [size, color] }
                    colorCode: { type: string, nullable: true }
        variants:
          type: array
          description: |
            Purchasable combinations for `type=variant` (empty for simple).
            Compact rows — do **not** repeat full attribute/value objects here.
            Resolve names/kinds/colors from top-level `attributes`.

            Each variant includes:
            - `id` — stable combination key (`attributeId:valueId|…`). Embedded
              product variants have no Mongo `_id` (`_id: false` in schema).
            - `label` — human-readable summary in catalogue attribute order
              (e.g. `XS / أسود`, or just `XS` / `أسود` for single-attribute products)
            - `selectedValues` — map `{ [attributeId]: valueId }`
            - stock/price/discount/availability fields

            Example:
            ```json
            {
              "id": "6a69c8eadef712b95ba86e82:6a69c8eadef712b95ba86e8c|6a69c8eadef712b95ba86e88:6a69c8eddef712b95ba86ea8",
              "label": "XS / أسود",
              "selectedValues": {
                "6a69c8eadef712b95ba86e82": "6a69c8eadef712b95ba86e8c",
                "6a69c8eadef712b95ba86e88": "6a69c8eddef712b95ba86ea8"
              },
              "quantity": 5,
              "price": 120,
              "discountType": "none",
              "discountValue": 0,
              "isAvailable": true
            }
            ```
          items:
            type: object
            required:
              [id, label, selectedValues, quantity, quantityText, price, priceText,
               discountType, discountValue, discount, finalPrice, finalPriceText, isAvailable]
            properties:
              id:
                type: string
                description: Stable combination key (not a Mongo ObjectId).
              label:
                type: string
                example: "XS / أسود"
                description: Localized selected value names joined with ` / ` in catalogue order.
              selectedValues:
                type: object
                additionalProperties: { type: string }
                description: |
                  Map of attributeId → selected valueId.
                  Safer than an ordered valueIds array.
                example:
                  "6a69c8eadef712b95ba86e82": "6a69c8eadef712b95ba86e8c"
                  "6a69c8eadef712b95ba86e88": "6a69c8eddef712b95ba86ea8"
              quantity: { type: integer }
              quantityText: { type: string, example: "5 قطعه" }
              price: { type: number }
              priceText: { type: string, example: "120 SAR" }
              discountType:
                type: string
                enum: [none, percentage, fixed]
              discountValue: { type: number }
              discount:
                type: object
                required: [type, typeText, value]
                properties:
                  type: { type: string, enum: [none, percentage, fixed] }
                  typeText: { type: string }
                  value: { type: number }
              finalPrice: { type: number }
              finalPriceText: { type: string, example: "120 SAR" }
              isAvailable:
                type: boolean
                description: "true when quantity > 0."
        images: { type: array, items: { type: string } }
        video: { type: string }
        pricing:
          type: object
          required:
            [method, methodText, manualPrice, manualPriceText, aiSuggestedPrice,
             aiSuggestedPriceText, aiReference, currency]
          properties:
            method: { type: string, enum: [manual, ai] }
            methodText: { type: string }
            manualPrice: { type: number }
            manualPriceText: { type: string, example: "122000 SAR" }
            aiSuggestedPrice: { type: number, nullable: true }
            aiSuggestedPriceText:
              type: string
              nullable: true
              description: Formatted AI suggestion, or null when AI pricing does not exist.
            aiReference:
              type: object
              nullable: true
              description: |
                Populated PricingRequest snapshot when the product has an AI
                pricing reference. `priceRangeMin` / `priceRangeMax` /
                `suggestedPrice` are read from the PricingRequest document
                (not hard-coded zeros). Null when the product has no AI reference.
                PricingRequest currently stores no expiry or product-fingerprint fields.
              properties:
                id: { type: string }
                suggestedPrice: { type: number, example: 797 }
                priceRangeMin: { type: number, example: 677 }
                priceRangeMax: { type: number, example: 917 }
            currency: { type: string, example: "SAR" }
        discount:
          type: object
          nullable: true
          properties:
            type: { type: string, enum: [none, percentage, fixed] }
            typeText: { type: string }
            value: { type: number }

    ProviderProductListResponse:
      type: object
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer, enum: [200] }
        data:
          type: array
          items: { $ref: '#/components/schemas/ProviderProductCard' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    ClientSharedProductListResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer, enum: [200] }
        data:
          type: array
          items: { $ref: '#/components/schemas/ClientSharedProductCard' }
        paginate: { $ref: '#/components/schemas/PaginateMeta' }

    SharedProductListResponse:
      oneOf:
        - $ref: '#/components/schemas/ClientSharedProductListResponse'
        - $ref: '#/components/schemas/ProviderProductListResponse'
      description: Response shape is selected by the authenticated bearer role.

    ProviderProductDetailsResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer }
        data: { $ref: '#/components/schemas/ProviderProductDetails' }

    ClientSharedProductDetailsResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer, enum: [200] }
        data: { $ref: '#/components/schemas/ClientSharedProductDetails' }

    SharedProductDetailsResponse:
      oneOf:
        - $ref: '#/components/schemas/ClientSharedProductDetailsResponse'
        - $ref: '#/components/schemas/ProviderProductDetailsResponse'
      description: Response shape is selected by the authenticated bearer role.

    ProviderProductCreateResponse:
      type: object
      additionalProperties: false
      required: [key, message, status]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer, enum: [200] }

    ProviderProductVisibilityResponse:
      type: object
      additionalProperties: false
      required: [key, message, status]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string, example: "تم تحديث ظهور المنتج بنجاح" }
        status: { type: integer, enum: [200] }

    ProviderProductMarkPremiumResponse:
      type: object
      additionalProperties: false
      required: [key, message, status]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string, example: "تم تمييز المنتج بنجاح" }
        status: { type: integer, enum: [200] }

    ProviderProductDeleteResponse:
      type: object
      required: [key, message, status, data]
      properties:
        key: { type: string, enum: [success] }
        message: { type: string }
        status: { type: integer, enum: [200] }
        data:
          type: object
          required: [productId, deleted]
          properties:
            productId: { type: string }
            deleted: { type: boolean, enum: [true] }

    ErrorResponse:
      type: object
      description: Standard error envelope. No stack trace or sensitive values are returned.
      required: [key, message, status]
      properties:
        key:
          type: string
          enum: [fail, unauthorized, blocked, notFound, exception]
          description: |
            Error category:
            - `fail`: validation, business-rule, or not-found failure.
            - `unauthorized`: reset/authentication proof is invalid or expired.
            - `blocked`: account is blocked.
            - `notFound`: the requested resource does not exist.
            - `exception`: unexpected server error without a stack trace.
          example: fail
        message: { type: string, example: "حدث خطأ" }
        status: { type: integer, example: 400 }

    ValidationErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            key:
              type: string
              enum: [fail]
              description: '`fail`: request validation failed.'
              example: fail
            message: { type: string, example: "البيانات المرسلة غير صحيحة" }
            status:
              type: integer
              enum: [400]
              description: '`400`: validation failure HTTP status.'
              example: 400

    ConflictErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            key:
              type: string
              enum: [fail]
              description: '`fail`: the supplied identity conflicts with an existing account.'
              example: fail
            message: { type: string, example: "رقم الجوال مسجل مسبقا" }
            status:
              type: integer
              enum: [400]
              description: '`400`: identity conflict HTTP status used by this API.'
              example: 400

    UnauthorizedErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            key:
              type: string
              enum: [unauthorized]
              example: unauthorized
            message: { type: string, example: "غير مصرح" }
            status:
              type: integer
              enum: [401]
              example: 401

    NotFoundErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            key:
              type: string
              enum: [notFound]
              example: notFound
            message: { type: string, example: "العنصر المطلوب غير موجود" }
            status:
              type: integer
              enum: [404]
              example: 404

    OrderActionResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
      description: Generic success envelope returned by order mutation endpoints (message only).

    OrderDeliveryActions:
      type: object
      additionalProperties: false
      required: [canClientConfirmReceived, returnRequestButton]
      properties:
        canClientConfirmReceived: { type: boolean, example: true }
        returnRequestButton:
          type: boolean
          example: false
          description: >
            Single flag for POST /return-request.
            `true` = finished paid received order, no return request yet, and
            still inside the dashboard-configured return window;
            `false` = not allowed.

    OrderDetailsActions:
      allOf:
        - $ref: '#/components/schemas/OrderDeliveryActions'
        - type: object
          additionalProperties: false
          required:
            - paymentButton
            - cancelButton
            - receivedButton
            - acceptButton
            - rejectButton
            - deliveredToCustomerButton
            - chatButton
            - rateButton
          properties:
            paymentButton:
              type: boolean
              example: false
              description: "type=order → POST /order/payment (client, accepted & awaiting payment). type=return → always false."
            cancelButton:
              type: boolean
              example: false
              description: "type=order → PATCH /order/cancel (client, awaiting approval). type=return → always false."
            receivedButton:
              type: boolean
              example: false
              description: "type=order → PATCH /order/received (client, paid current & delivered_to_customer). type=return → PATCH /return-request/received (provider, client_delivered)."
            acceptButton:
              type: boolean
              example: false
              description: "type=order → PATCH /order/accept (provider, awaiting approval). type=return → PATCH /return-request/accept (provider, pending_review)."
            rejectButton:
              type: boolean
              example: false
              description: "type=order → PATCH /order/reject (provider, unpaid NEW). type=return → PATCH /return-request/reject (provider, pending_review)."
            deliveredToCustomerButton:
              type: boolean
              example: false
              description: "type=order → PATCH /order/delivered (provider, paid current & deliverable). type=return → PATCH /return-request/delivered (client, accepted)."
            returnRequestButton:
              type: boolean
              example: false
              description: >
                type=order → POST /return-request.
                `true` only when the order is finished, paid, received, and the
                client has not created a return yet and the dashboard-configured
                return window has not expired; otherwise `false`.
                type=return → always false.
            chatButton:
              type: boolean
              example: true
              description: "Open chat with chatId — true when order/return is not cancelled/rejected."
            rateButton:
              type: boolean
              example: false
              description: "type=order → POST /rate (client, finished paid received & not rated). type=return → always false."

    OrderDeliveryActionResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          properties:
            data:
              type: object
              additionalProperties: false
              properties:
                id: { type: string, example: "665f1c2a9b4e1d0012ab34d0" }
                orderNumber: { type: integer, example: 1042 }
                status:
                  type: string
                  enum: [new, current, finished, cancelled]
                  example: current
                statusText: { type: string, example: تم التسليم للعميل }
                paymentStatus:
                  type: string
                  enum: [paid, unpaid]
                  example: paid
                currentStep:
                  type: string
                  nullable: true
                  enum: [processing, delivered_to_shipping, delivered_to_customer, null]
                  example: delivered_to_customer
                isPayment: { type: boolean, example: true }
                isReceived: { type: boolean, example: false }
                deliveredAt:
                  type: string
                  format: date-time
                  nullable: true
                receivedAt:
                  type: string
                  format: date-time
                  nullable: true
                completedAt:
                  type: string
                  format: date-time
                  nullable: true
                actions:
                  $ref: '#/components/schemas/OrderDeliveryActions'

    OrderListActions:
      type: object
      additionalProperties: false
      required: [detailsButton]
      properties:
        detailsButton:
          type: boolean
          enum: [true]
          example: true
          description: Always true — open order details from the list card.

    OrderListItem:
      type: object
      additionalProperties: false
      required:
        - id
        - orderNumber
        - orderNumberText
        - clientName
        - status
        - statusText
        - productName
        - productImage
        - date
        - totalPrice
        - totalPriceText
        - currency
        - actions
      properties:
        id: { type: string, example: "6a6f0ab91af1d34b05615dc5" }
        orderNumber: { type: integer, example: 27 }
        orderNumberText: { type: string, example: "#رقم الطلب: 27" }
        clientName:
          type: string
          description: >
            Provider list returns the client display name.
            Client list always includes the key with an empty string.
          example: ""
        status:
          type: string
          enum: [new, current, finished, cancelled, rejected]
          example: new
        statusText: { type: string, example: جديد }
        productName: { type: string, example: سماعات سوني }
        productImage:
          type: string
          format: uri
          example: "https://dashboard.kam-teswa.4hoste.com/assets/uploads/products/6a69c9117b182fb0ce4bfcdf/image741785317649159.png"
        date:
          type: string
          description: Order date only (`YYYY/MM/DD`), localized digits follow `lang`.
          example: "٢٠٢٦/٠٨/٠٢"
        totalPrice: { type: number, example: 264 }
        totalPriceText: { type: string, example: "264 ﷼" }
        currency: { type: string, example: "﷼" }
        actions:
          $ref: '#/components/schemas/OrderListActions'

    OrderProductLine:
      type: object
      additionalProperties: false
      properties:
        id: { type: string, example: "665f1c2a9b4e1d0012ab34ce" }
        image: { type: string, format: uri, example: "https://example.com/product.jpg" }
        name: { type: string, example: سماعات سوني }
        description: { type: string, example: وصف المنتج }
        condition: { type: string, enum: [new, used, ""], example: used }
        conditionText: { type: string, example: مستعمل }
        count: { type: integer, example: 1 }
        price: { type: number, example: 1400 }
        priceText: { type: string, example: "1400 \uFDFC" }
        attributes:
          type: array
          nullable: true
          description: Selected attribute name/value pairs. Null when none.
          items:
            type: object
            additionalProperties: false
            required: [name, value]
            properties:
              name: { type: string, example: الماركة }
              value: { type: string, example: لايكا }

    OrderDetails:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          example: 665f1c2a9b4e1d0012ab34d0
        warningText:
          type: string
          example: ''
        statusSteps:
          type: array
          description: Lifecycle stepper for orders or return requests. Empty when cancelled/rejected.
          items:
            type: object
            additionalProperties: false
            required:
            - key
            - title
            - description
            - isCompleted
            - isActive
            properties:
              key:
                type: string
                enum:
                - awaiting_approval
                - awaiting_payment
                - delivering
                - finished
                - collecting_from_client
                - delivered_to_merchant
              title:
                type: string
              description:
                type: string
              isCompleted:
                type: boolean
              isActive:
                type: boolean
                description: True for the step the order/return is currently on (exactly one step).
        orderNumber:
          type: integer
          nullable: true
          example: 12315
        orderNumberText:
          type: string
          example: '#رقم الطلب: 12315'
        date:
          type: string
          example: '٢٠٢٥/٠١/٢٥'
          description: Order date only (`YYYY/MM/DD`); digits follow request `lang` (Arabic-Indic for `ar`).
        time:
          type: string
          example: 08:00 مساء
        reasonTitle:
          type: string
          example: ''
        reason:
          type: string
          example: ''
        status:
          type: string
          enum:
          - new
          - current
          - finished
          - cancelled
          - rejected
          example: new
        statusText:
          type: string
          example: قيد المراجعة
        product:
          allOf:
          - $ref: '#/components/schemas/OrderProductLine'
          nullable: true
        aiPrice:
          type: object
          additionalProperties: false
          required: [aiSuggestedPrice, aiSuggestedPriceText, currency, repriceButton]
          properties:
            aiSuggestedPrice:
              type: number
              example: 0
            aiSuggestedPriceText:
              type: string
              example: 0 ﷼
            currency:
              type: string
              example: ﷼
            repriceButton:
              type: boolean
              example: false
              description: >
                إعادة تسعير — `true` when this order's product was previously
                priced with AI (`aiSuggestedPrice` / product AI fields);
                otherwise `false`.
        totalPrice:
          type: object
          additionalProperties: false
          properties:
            totalPrice:
              type: number
              example: 1150
            totalPriceText:
              type: string
              example: 1150 ﷼
            currency:
              type: string
              example: ﷼
        deliveryAddress:
          type: object
          additionalProperties: false
          required:
          - title
          - name
          - phone
          - address
          properties:
            title:
              type: string
              example: عنوان التوصيل
            name:
              type: string
              example: أحمد محمد
            phone:
              type: string
              example: +966 50 123 4567
            address:
              type: string
              example: شارع الملك فهد، حي النخيل
            lat:
              type: number
              example: 24.7136
              description: Latitude from client GeoJSON `location.coordinates[1]`.
            lng:
              type: number
              example: 46.6753
              description: Longitude from client GeoJSON `location.coordinates[0]`.
        provider:
          type: object
          nullable: true
          additionalProperties: false
          properties:
            image:
              type: string
              format: uri
            name:
              type: string
            address:
              type: string
            lat:
              type: number
              example: 24.7136
              description: Latitude from provider GeoJSON `location.coordinates[1]`.
            lng:
              type: number
              example: 46.6753
              description: Longitude from provider GeoJSON `location.coordinates[0]`.
        chatId:
          type: string
          example: 665f1c2a9b4e1d0012ab34d0
        returnReason:
          type: object
          nullable: true
          description: >
            Figma return-details card (سبب الإرجاع). Present for `type=return`,
            null for normal orders.
          additionalProperties: false
          required: [title, reason, attachments]
          properties:
            title:
              type: string
              example: سبب الإرجاع
            reason:
              type: string
              example: المنتج تالف
            attachments:
              type: array
              items:
                type: string
                format: uri
        attachments:
          type: array
          description: Return evidence images. Empty for normal orders.
          items:
            type: string
            format: uri
        actions:
          $ref: '#/components/schemas/OrderDetailsActions'
        rate:
          type: object
          additionalProperties: false
          required: [product, provider]
          description: >
            Submitted ratings for this order.
            `product` / `provider` are Figma rating cards (null until the client rates).
            Still returned when the order later has a return request (`type=return`).
            Use `actions.rateButton` + root `id` for POST /rate (no nested `data` payload).
          properties:
            product:
              nullable: true
              description: Product stars/comment card for this order (null if not rated yet).
              type: object
              additionalProperties: false
              required: [id, name, date, rate, comment]
              properties:
                id: { type: string, example: "665f1c2a9b4e1d0012ab34e2" }
                name: { type: string, example: "أحمد الدوسري", description: Client who left the rating. }
                date: { type: string, example: "٢٠٢٤/٠١/٠٨", description: "`YYYY/MM/DD`; digits follow request `lang`." }
                rate: { type: number, example: 5 }
                comment: { type: string, example: "كاميرا تحفة فنية! تعمل بشكل مثالي والصور التي تنتجها رائعة." }
            provider:
              nullable: true
              description: Store stars/comment card for this order (null if not rated yet).
              type: object
              additionalProperties: false
              required: [id, name, date, rate, comment]
              properties:
                id: { type: string, example: "665f1c2a9b4e1d0012ab34e2" }
                name: { type: string, example: "أحمد الدوسري", description: Client who left the rating. }
                date: { type: string, example: "٢٠٢٤/٠١/٠٨", description: "`YYYY/MM/DD`; digits follow request `lang`." }
                rate: { type: number, example: 5 }
                comment: { type: string, example: "تعامل ممتاز وخدمة سريعة واحترافية." }

    OrderListResponse:
      type: object
      additionalProperties: false
      required: [key, message, status, data, paginate]
      properties:
        key: { type: string, enum: [success], example: success }
        message: { type: string, example: "تم بنجاح" }
        status: { type: integer, enum: [200], example: 200 }
        data:
          type: array
          items:
            $ref: '#/components/schemas/OrderListItem'
        paginate:
          $ref: '#/components/schemas/PaginateMeta'

    OrderDetailsResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required: [data]
          properties:
            data:
              $ref: '#/components/schemas/OrderDetails'

    ConfirmOrderResponse:
      $ref: '#/components/schemas/SuccessEnvelope'

    ProductReportReason:
      type: object
      additionalProperties: false
      required: [id, title, description, icon]
      properties:
        id:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          example: 66a1b2c3d4e5f67890123456
        title:
          type: string
          example: إعلان وهمي
        description:
          type: string
          example: استخدم هذا السبب عندما يبدو المنتج أو العرض غير حقيقي.
        icon:
          type: string
          format: uri
          description: Uploaded local icon URL or the guaranteed bundled fallback URL.

    ProductReportReasonsResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required: [data]
          properties:
            data:
              type: object
              additionalProperties: false
              required: [reasons]
              properties:
                reasons:
                  type: array
                  items:
                    $ref: '#/components/schemas/ProductReportReason'

    ProductReportSubmitRequest:
      type: object
      additionalProperties: false
      required: [productId, reasonId]
      properties:
        productId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          description: Existing, non-deleted Product ObjectId.
          example: 665f1c2a9b4e1d0012ab34d0
        reasonId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
          example: 66a1b2c3d4e5f67890123456
        note:
          type: string
          maxLength: 500
          description: Optional plain-text client context. HTML angle brackets are rejected.
          example: السعر والصور مكررة في إعلان آخر

    ProductReport:
      type: object
      additionalProperties: false
      required: [id, productId, productNumber, reason, note, status, createdAt]
      properties:
        id:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
        productId:
          type: string
          pattern: '^[a-fA-F0-9]{24}$'
        productNumber:
          type: string
          description: Product number snapshot; falls back to the Product ObjectId when no number exists.
        reason:
          $ref: '#/components/schemas/ProductReportReason'
        note:
          type: string
          maxLength: 500
        status:
          type: string
          enum: [pending, reviewed, dismissed, action_taken]
          example: pending
        createdAt:
          type: string
          format: date-time

    ProductReportSubmitResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required: [data]
          properties:
            data:
              type: object
              additionalProperties: false
              required: [report]
              properties:
                report:
                  $ref: '#/components/schemas/ProductReport'

    ReturnRequestActions:
      $ref: '#/components/schemas/OrderDetailsActions'

    ReturnRequestItem:
      $ref: '#/components/schemas/OrderDetails'

    ReturnRequestResponse:
      $ref: '#/components/schemas/OrderDetailsResponse'

    ReturnRequestListResponse:
      $ref: '#/components/schemas/OrderListResponse'
