openapi: 3.1.0
info:
  # Do not change the title, if the title changes, the import paths will be broken
  title: Api
  version: 0.1.0
  description: VaultDXB API specification
servers:
  - url: /api
    description: Base API path
tags:
  - name: health
    description: Health operations
  - name: auth
    description: Authentication operations
  - name: deals
    description: Deal operations
  - name: access-requests
    description: Access request operations
  - name: payments
    description: Payment operations
  - name: admin
    description: Admin operations
  - name: users
    description: User operations
  - name: seller-leads
    description: Seller lead operations
  - name: ai
    description: AI analysis operations
  - name: deal-rooms
    description: Weekly Private Deal Room operations

paths:
  /healthz:
    get:
      operationId: healthCheck
      tags: [health]
      summary: Health check
      responses:
        "200":
          description: Healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStatus"

  /auth/register:
    post:
      operationId: register
      tags: [auth]
      summary: Register a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterBody"
      responses:
        "201":
          description: Registered successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/login:
    post:
      operationId: login
      tags: [auth]
      summary: Login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginBody"
      responses:
        "200":
          description: Login successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthResponse"
        "401":
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /auth/me:
    get:
      operationId: getMe
      tags: [auth]
      summary: Get current user
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Current user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /deals:
    get:
      operationId: listDeals
      tags: [deals]
      summary: List deals (investors see approved; brokers see own; admin sees all)
      security:
        - bearerAuth: []
      parameters:
        - name: area
          in: query
          schema:
            type: string
        - name: minDiscount
          in: query
          schema:
            type: number
        - name: minUrgency
          in: query
          schema:
            type: number
        - name: maxPrice
          in: query
          schema:
            type: number
        - name: unitType
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, approved, rejected]
      responses:
        "200":
          description: List of deals
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Deal"

    post:
      operationId: createDeal
      tags: [deals]
      summary: Submit a new deal (broker only)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDealBody"
      responses:
        "201":
          description: Deal created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"

  /deals/{id}:
    get:
      operationId: getDeal
      tags: [deals]
      summary: Get deal details
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Deal details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    put:
      operationId: updateDeal
      tags: [deals]
      summary: Update a deal (admin or deal owner broker)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateDealBody"
      responses:
        "200":
          description: Deal updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"

    delete:
      operationId: deleteDeal
      tags: [deals]
      summary: Delete a deal (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "204":
          description: Deleted

  /deals/{id}/approve:
    post:
      operationId: approveDeal
      tags: [deals]
      summary: Approve a deal (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Deal approved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"

  /deals/{id}/reject:
    post:
      operationId: rejectDeal
      tags: [deals]
      summary: Reject a deal (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Deal rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"

  /deals/{id}/deactivate:
    post:
      operationId: deactivateDeal
      tags: [deals]
      summary: Deactivate a deal — moves it back to pending (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Deal deactivated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deal"

  /access-requests:
    get:
      operationId: listAccessRequests
      tags: [access-requests]
      summary: List access requests (admin sees all; investor sees own)
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Access requests
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AccessRequest"

    post:
      operationId: createAccessRequest
      tags: [access-requests]
      summary: Request access to a deal
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAccessRequestBody"
      responses:
        "201":
          description: Access request created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessRequest"

  /access-requests/{id}:
    put:
      operationId: updateAccessRequest
      tags: [access-requests]
      summary: Update access request status (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAccessRequestBody"
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccessRequest"

  /payments/create-checkout:
    post:
      operationId: createCheckoutSession
      tags: [payments]
      summary: Create a Stripe checkout session
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCheckoutBody"
      responses:
        "200":
          description: Checkout session created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutSessionResponse"

  /payments/webhook:
    post:
      operationId: stripeWebhook
      tags: [payments]
      summary: Stripe webhook endpoint
      responses:
        "200":
          description: Webhook received

  /admin/stats:
    get:
      operationId: getAdminStats
      tags: [admin]
      summary: Get admin dashboard stats
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Admin stats
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminStats"

  /users:
    get:
      operationId: listUsers
      tags: [users]
      summary: List all users (admin only)
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Users list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"

  /users/{id}:
    put:
      operationId: updateUser
      tags: [users]
      summary: Update user membership/role (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateUserBody"
      responses:
        "200":
          description: User updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

  /ai/analyze-deal:
    post:
      operationId: analyzeDeal
      tags: [ai]
      summary: Run AI analysis on a deal (admin only)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnalyzeDealBody"
      responses:
        "200":
          description: AI analysis result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DealAnalysisResult"
        "404":
          description: Deal not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ai/analyze-seller-lead:
    post:
      operationId: analyzeSellerLead
      tags: [ai]
      summary: Run AI qualification on a seller lead (admin only)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnalyzeSellerLeadBody"
      responses:
        "200":
          description: AI qualification result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SellerLeadAnalysisResult"
        "404":
          description: Seller lead not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ai/broker-assist:
    post:
      operationId: brokerAssist
      tags: [ai]
      summary: AI Broker Assistant — improve description, generate pitch, or check attractiveness
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BrokerAssistBody"
      responses:
        "200":
          description: AI broker assist result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BrokerAssistResult"

  /ai/deals/{id}/matched-investors:
    get:
      operationId: getMatchedInvestors
      tags: [ai]
      summary: Get investors matched to a deal (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Matched investors result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MatchedInvestorsResult"
        "404":
          description: Deal not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /deal-rooms:
    get:
      operationId: listDealRooms
      tags: [deal-rooms]
      summary: List deal rooms (investor sees accessible rooms; admin sees all)
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of deal rooms
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/DealRoom"

  /deal-rooms/{id}:
    get:
      operationId: getDealRoom
      tags: [deal-rooms]
      summary: Get deal room with items
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Deal room detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DealRoomDetail"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /deal-rooms/{id}/bids:
    post:
      operationId: submitBid
      tags: [deal-rooms]
      summary: Submit a confidential offer in a deal room (investor only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitBidBody"
      responses:
        "201":
          description: Bid submitted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Bid"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Access level not met
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /deal-rooms/{id}/my-bid:
    get:
      operationId: getMyBid
      tags: [deal-rooms]
      summary: Get the current investor's bid for a deal room item
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: dealId
          in: query
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Existing bid or null
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Bid"
        "404":
          description: No bid found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /admin/deal-rooms:
    get:
      operationId: listAdminDealRooms
      tags: [deal-rooms]
      summary: List all deal rooms with stats (admin only)
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Admin deal room list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/DealRoom"

    post:
      operationId: createDealRoom
      tags: [deal-rooms]
      summary: Create a new deal room (admin only)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDealRoomBody"
      responses:
        "201":
          description: Deal room created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DealRoom"

  /admin/deal-rooms/{id}:
    put:
      operationId: updateDealRoom
      tags: [deal-rooms]
      summary: Update a deal room (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateDealRoomBody"
      responses:
        "200":
          description: Deal room updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DealRoom"

  /admin/deal-rooms/{id}/items:
    post:
      operationId: addDealRoomItem
      tags: [deal-rooms]
      summary: Add a deal to a deal room (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDealRoomItemBody"
      responses:
        "201":
          description: Item added
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DealRoomItem"

  /admin/deal-rooms/{id}/items/{itemId}:
    delete:
      operationId: removeDealRoomItem
      tags: [deal-rooms]
      summary: Remove a deal from a deal room (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: itemId
          in: path
          required: true
          schema:
            type: integer
      responses:
        "204":
          description: Item removed

  /admin/deal-rooms/{id}/bids:
    get:
      operationId: listDealRoomBids
      tags: [deal-rooms]
      summary: List all bids for a deal room (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Bids list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AdminBid"

  /admin/bids/{id}:
    put:
      operationId: updateBid
      tags: [deal-rooms]
      summary: Update bid status (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateBidBody"
      responses:
        "200":
          description: Bid updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminBid"

  /seller-leads/{id}/send-payment-link:
    post:
      operationId: sendSellerPaymentLink
      tags: [seller-leads]
      summary: Send a payment link to a seller (admin only)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendSellerPaymentLinkBody"
      responses:
        "200":
          description: Payment link sent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendSellerPaymentLinkResponse"

  /seller-leads:
    post:
      operationId: createSellerLead
      tags: [seller-leads]
      summary: Submit a seller lead (public, no auth required)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSellerLeadBody"
      responses:
        "201":
          description: Seller lead submitted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SellerLead"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    get:
      operationId: listSellerLeads
      tags: [seller-leads]
      summary: List seller leads (admin sees all; broker sees assigned)
      security:
        - bearerAuth: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [new, contacted, valuation, assigned, active, closed, rejected]
      responses:
        "200":
          description: Seller leads list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SellerLead"

  /seller-leads/{id}:
    get:
      operationId: getSellerLead
      tags: [seller-leads]
      summary: Get a seller lead by ID
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Seller lead details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SellerLead"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    put:
      operationId: updateSellerLead
      tags: [seller-leads]
      summary: Update seller lead status, assignment, or notes (admin or assigned broker)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateSellerLeadBody"
      responses:
        "200":
          description: Updated seller lead
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SellerLead"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

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

  schemas:
    HealthStatus:
      type: object
      properties:
        status:
          type: string
      required:
        - status

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
      required:
        - error

    RegisterBody:
      type: object
      properties:
        name:
          type: string
        email:
          type: string
        password:
          type: string
        phone:
          type: string
        role:
          type: string
          enum: [broker, investor]
      required:
        - name
        - email
        - password
        - role

    LoginBody:
      type: object
      properties:
        email:
          type: string
        password:
          type: string
      required:
        - email
        - password

    AuthResponse:
      type: object
      properties:
        token:
          type: string
        user:
          $ref: "#/components/schemas/User"
      required:
        - token
        - user

    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
        phone:
          type: string
          nullable: true
        role:
          type: string
          enum: [admin, broker, investor]
        membership:
          type: string
          enum: [free, vip, elite]
        status:
          type: string
          enum: [pending, approved, rejected]
        paymentStatus:
          type: string
          enum: [unpaid, pending, paid, failed]
        stripeCustomerId:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
      required:
        - id
        - name
        - email
        - role
        - membership
        - status
        - paymentStatus
        - createdAt

    Deal:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        area:
          type: string
        building:
          type: string
          nullable: true
        unitType:
          type: string
          nullable: true
        bedrooms:
          type: integer
          nullable: true
        bathrooms:
          type: integer
          nullable: true
        sizeSqft:
          type: number
          nullable: true
        askingPrice:
          type: number
        estimatedMarketPrice:
          type: number
        discountPercentage:
          type: number
          nullable: true
        urgencyScore:
          type: integer
          nullable: true
        urgencyReason:
          type: string
          nullable: true
        sellerTimeline:
          type: string
          nullable: true
        propertyStatus:
          type: string
          nullable: true
        paymentPlanDetails:
          type: string
          nullable: true
        brokerContact:
          type: string
          nullable: true
        imageUrl:
          type: string
          nullable: true
        submittedByUserId:
          type: integer
          nullable: true
        roiYield:
          type: number
          nullable: true
        developer:
          type: string
          nullable: true
        handoverStatus:
          type: string
          nullable: true
        sellerType:
          type: string
          enum: [owner, broker, developer, distressed_seller]
          nullable: true
        dealCategory:
          type: string
          enum: [off_market, distress_deal, auction, exclusive, investor_only]
          nullable: true
        description:
          type: string
          nullable: true
        visibility:
          type: string
          enum: [investors_only, brokers_only, both]
          nullable: true
        imageUrls:
          type: string
          nullable: true
        documentUrls:
          type: string
          nullable: true
        status:
          type: string
          enum: [draft, pending, approved, rejected]
        isVipOnly:
          type: boolean
        createdAt:
          type: string
          format: date-time
        submittedBy:
          $ref: "#/components/schemas/User"
          nullable: true
        aiDistressScore:
          type: integer
          nullable: true
        aiDealScore:
          type: integer
          nullable: true
        aiInvestorAppeal:
          type: string
          enum: [High, Medium, Low]
          nullable: true
        aiRiskLevel:
          type: string
          enum: [High, Medium, Low]
          nullable: true
        aiRecommendedAction:
          type: string
          enum: [Prioritize, Verify, Reject]
          nullable: true
        aiInvestorSummary:
          type: string
          nullable: true
        aiRiskFlags:
          type: string
          nullable: true
        aiRecommendedBuyerType:
          type: string
          nullable: true
        aiLiquidityRating:
          type: string
          enum: [Low, Medium, High]
          nullable: true
        aiNegotiationAngle:
          type: string
          nullable: true
        aiMissingInfo:
          type: string
          nullable: true
        aiPricingComment:
          type: string
          nullable: true
        aiAdminDecision:
          type: string
          enum: [Approve, Review, Reject]
          nullable: true
      required:
        - id
        - title
        - area
        - askingPrice
        - estimatedMarketPrice
        - status
        - isVipOnly
        - createdAt

    CreateDealBody:
      type: object
      properties:
        title:
          type: string
        area:
          type: string
        building:
          type: string
        unitType:
          type: string
        bedrooms:
          type: integer
        bathrooms:
          type: integer
        sizeSqft:
          type: number
        askingPrice:
          type: number
        estimatedMarketPrice:
          type: number
        roiYield:
          type: number
        urgencyReason:
          type: string
        sellerTimeline:
          type: string
        propertyStatus:
          type: string
        paymentPlanDetails:
          type: string
        brokerContact:
          type: string
        imageUrl:
          type: string
        imageUrls:
          type: string
        documentUrls:
          type: string
        developer:
          type: string
        handoverStatus:
          type: string
        sellerType:
          type: string
          enum: [owner, broker, developer, distressed_seller]
        dealCategory:
          type: string
          enum: [off_market, distress_deal, auction, exclusive, investor_only]
        description:
          type: string
        visibility:
          type: string
          enum: [investors_only, brokers_only, both]
        isVipOnly:
          type: boolean
        status:
          type: string
          enum: [draft, pending, approved, rejected]
      required:
        - title
        - area
        - askingPrice
        - estimatedMarketPrice

    UpdateDealBody:
      type: object
      properties:
        title:
          type: string
        area:
          type: string
        building:
          type: string
        unitType:
          type: string
        bedrooms:
          type: integer
        bathrooms:
          type: integer
        sizeSqft:
          type: number
        askingPrice:
          type: number
        estimatedMarketPrice:
          type: number
        roiYield:
          type: number
        urgencyReason:
          type: string
        sellerTimeline:
          type: string
        propertyStatus:
          type: string
        paymentPlanDetails:
          type: string
        brokerContact:
          type: string
        imageUrl:
          type: string
        imageUrls:
          type: string
        documentUrls:
          type: string
        developer:
          type: string
        handoverStatus:
          type: string
        sellerType:
          type: string
          enum: [owner, broker, developer, distressed_seller]
        dealCategory:
          type: string
          enum: [off_market, distress_deal, auction, exclusive, investor_only]
        description:
          type: string
        visibility:
          type: string
          enum: [investors_only, brokers_only, both]
        isVipOnly:
          type: boolean
        status:
          type: string
          enum: [draft, pending, approved, rejected]
        aiDistressScore:
          type: integer
          nullable: true
        aiInvestorSummary:
          type: string
          nullable: true
        aiRiskFlags:
          type: string
          nullable: true
        aiRecommendedBuyerType:
          type: string
          nullable: true
        aiLiquidityRating:
          type: string
          enum: [Low, Medium, High]
          nullable: true
        aiNegotiationAngle:
          type: string
          nullable: true

    AccessRequest:
      type: object
      properties:
        id:
          type: integer
        dealId:
          type: integer
        investorUserId:
          type: integer
        message:
          type: string
          nullable: true
        status:
          type: string
          enum: [new, contacted, closed, rejected]
        createdAt:
          type: string
          format: date-time
        deal:
          $ref: "#/components/schemas/Deal"
          nullable: true
        investor:
          $ref: "#/components/schemas/User"
          nullable: true
      required:
        - id
        - dealId
        - investorUserId
        - status
        - createdAt

    CreateAccessRequestBody:
      type: object
      properties:
        dealId:
          type: integer
        message:
          type: string
      required:
        - dealId

    UpdateAccessRequestBody:
      type: object
      properties:
        status:
          type: string
          enum: [new, contacted, closed, rejected]
      required:
        - status

    CreateCheckoutBody:
      type: object
      properties:
        plan:
          type: string
          enum: [vip, elite, verified_broker, urgent_seller, concierge_sale]
        successUrl:
          type: string
        cancelUrl:
          type: string
      required:
        - plan

    CheckoutSessionResponse:
      type: object
      properties:
        url:
          type: string
          nullable: true
        sessionId:
          type: string
      required:
        - sessionId

    AdminStats:
      type: object
      properties:
        totalDeals:
          type: integer
        pendingDeals:
          type: integer
        approvedDeals:
          type: integer
        rejectedDeals:
          type: integer
        totalInvestors:
          type: integer
        pendingInvestors:
          type: integer
        totalBrokers:
          type: integer
        pendingBrokers:
          type: integer
        totalAccessRequests:
          type: integer
        activeDealRooms:
          type: integer
        totalBidsSubmitted:
          type: integer
        shortlistedBids:
          type: integer
        closedDealRooms:
          type: integer
        vipUsers:
          type: integer
        eliteUsers:
          type: integer
        totalSellerLeads:
          type: integer
        newSellerLeads:
          type: integer
      required:
        - totalDeals
        - pendingDeals
        - approvedDeals
        - rejectedDeals
        - totalInvestors
        - pendingInvestors
        - totalBrokers
        - pendingBrokers
        - totalAccessRequests
        - vipUsers
        - eliteUsers
        - totalSellerLeads
        - newSellerLeads

    UpdateUserBody:
      type: object
      properties:
        membership:
          type: string
          enum: [free, vip, elite]
        role:
          type: string
          enum: [admin, broker, investor]
        status:
          type: string
          enum: [pending, approved, rejected]
        paymentStatus:
          type: string
          enum: [unpaid, pending, paid, failed]

    SellerLead:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        phone:
          type: string
        email:
          type: string
        propertyArea:
          type: string
        buildingProject:
          type: string
          nullable: true
        propertyType:
          type: string
        bedrooms:
          type: string
          nullable: true
        askingPrice:
          type: number
        mortgageOutstanding:
          type: number
          nullable: true
        urgencyReason:
          type: string
        timelineToSell:
          type: string
        additionalNotes:
          type: string
          nullable: true
        photoUrls:
          type: string
          nullable: true
        status:
          type: string
          enum: [new, contacted, valuation, assigned, active, closed, rejected]
        paymentStatus:
          type: string
          enum: [unpaid, pending, paid, failed]
          nullable: true
        paymentPlan:
          type: string
          enum: [urgent_seller, concierge_sale]
          nullable: true
        assignedAgentId:
          type: integer
          nullable: true
        adminNotes:
          type: string
          nullable: true
        agentNotes:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
          nullable: true
        assignedAgent:
          $ref: "#/components/schemas/User"
          nullable: true
        aiLeadTemperature:
          type: string
          enum: [Hot, Warm, Cold]
          nullable: true
        aiUrgencyScore:
          type: integer
          nullable: true
        aiLikelyMotivation:
          type: string
          nullable: true
        aiSuggestedAgentScript:
          type: string
          nullable: true
        aiRecommendedNextStep:
          type: string
          nullable: true
      required:
        - id
        - name
        - phone
        - email
        - propertyArea
        - propertyType
        - askingPrice
        - urgencyReason
        - timelineToSell
        - status
        - createdAt

    CreateSellerLeadBody:
      type: object
      properties:
        name:
          type: string
        phone:
          type: string
        email:
          type: string
        propertyArea:
          type: string
        buildingProject:
          type: string
        propertyType:
          type: string
        bedrooms:
          type: string
        askingPrice:
          type: number
        mortgageOutstanding:
          type: number
        urgencyReason:
          type: string
        timelineToSell:
          type: string
        additionalNotes:
          type: string
        photoUrls:
          type: string
      required:
        - name
        - phone
        - email
        - propertyArea
        - propertyType
        - askingPrice
        - urgencyReason
        - timelineToSell

    UpdateSellerLeadBody:
      type: object
      properties:
        status:
          type: string
          enum: [new, contacted, valuation, assigned, active, closed, rejected]
        assignedAgentId:
          type: integer
          nullable: true
        adminNotes:
          type: string
          nullable: true
        agentNotes:
          type: string
          nullable: true
        paymentStatus:
          type: string
          enum: [unpaid, pending, paid, failed]
          nullable: true
        paymentPlan:
          type: string
          enum: [urgent_seller, concierge_sale]
          nullable: true

    AnalyzeDealBody:
      type: object
      properties:
        dealId:
          type: integer
      required:
        - dealId

    DealAnalysisResult:
      type: object
      properties:
        dealId:
          type: integer
        aiDistressScore:
          type: integer
        aiDealScore:
          type: integer
        aiInvestorAppeal:
          type: string
          enum: [High, Medium, Low]
        aiRiskLevel:
          type: string
          enum: [High, Medium, Low]
        aiRecommendedAction:
          type: string
          enum: [Prioritize, Verify, Reject]
        aiInvestorSummary:
          type: string
        aiRiskFlags:
          type: string
        aiRecommendedBuyerType:
          type: string
        aiLiquidityRating:
          type: string
          enum: [Low, Medium, High]
        aiNegotiationAngle:
          type: string
        aiMissingInfo:
          type: string
        aiPricingComment:
          type: string
        aiAdminDecision:
          type: string
          enum: [Approve, Review, Reject]
      required:
        - dealId
        - aiDistressScore
        - aiDealScore
        - aiInvestorAppeal
        - aiRiskLevel
        - aiRecommendedAction
        - aiInvestorSummary
        - aiRiskFlags
        - aiRecommendedBuyerType
        - aiLiquidityRating
        - aiNegotiationAngle
        - aiMissingInfo
        - aiPricingComment
        - aiAdminDecision

    BrokerAssistBody:
      type: object
      properties:
        title:
          type: string
        area:
          type: string
        unitType:
          type: string
        bedrooms:
          type: integer
        bathrooms:
          type: integer
        sizeSqft:
          type: number
        askingPrice:
          type: number
        estimatedMarketPrice:
          type: number
        urgencyReason:
          type: string
        sellerTimeline:
          type: string
        propertyStatus:
          type: string
        mode:
          type: string
          enum: [improve_description, generate_pitch, check_attractiveness]
      required:
        - title
        - area
        - askingPrice
        - estimatedMarketPrice
        - mode

    BrokerAssistResult:
      type: object
      properties:
        mode:
          type: string
        result:
          type: string
        attractivenessScore:
          type: integer
          nullable: true
      required:
        - mode
        - result

    MatchedInvestorsResult:
      type: object
      properties:
        dealId:
          type: integer
        matchedCount:
          type: integer
        investors:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
              email:
                type: string
              membership:
                type: string
              matchScore:
                type: integer
            required:
              - id
              - name
              - email
              - membership
              - matchScore
      required:
        - dealId
        - matchedCount
        - investors

    AnalyzeSellerLeadBody:
      type: object
      properties:
        leadId:
          type: integer
      required:
        - leadId

    SellerLeadAnalysisResult:
      type: object
      properties:
        leadId:
          type: integer
        aiLeadTemperature:
          type: string
          enum: [Hot, Warm, Cold]
        aiUrgencyScore:
          type: integer
        aiLikelyMotivation:
          type: string
        aiSuggestedAgentScript:
          type: string
        aiRecommendedNextStep:
          type: string
      required:
        - leadId
        - aiLeadTemperature
        - aiUrgencyScore
        - aiLikelyMotivation
        - aiSuggestedAgentScript
        - aiRecommendedNextStep

    SendSellerPaymentLinkBody:
      type: object
      properties:
        plan:
          type: string
          enum: [urgent_seller, concierge_sale]
        transferType:
          type: string
          enum: [stripe, bank_transfer]
      required:
        - plan
        - transferType

    DealRoom:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
          nullable: true
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        status:
          type: string
          enum: [upcoming, live, closed]
        accessLevel:
          type: string
          enum: [vip, elite, invite_only]
        createdAt:
          type: string
          format: date-time
        itemCount:
          type: integer
        bidCount:
          type: integer
      required:
        - id
        - title
        - startTime
        - endTime
        - status
        - accessLevel
        - createdAt
        - itemCount
        - bidCount

    DealRoomItem:
      type: object
      properties:
        id:
          type: integer
        dealRoomId:
          type: integer
        dealId:
          type: integer
        reservePrice:
          type: number
          nullable: true
        minimumOffer:
          type: number
          nullable: true
        sellerTerms:
          type: string
          nullable: true
        closingTimeline:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        deal:
          $ref: "#/components/schemas/Deal"
        bidCount:
          type: integer
        hasHighInterest:
          type: boolean
      required:
        - id
        - dealRoomId
        - dealId
        - createdAt
        - deal
        - bidCount
        - hasHighInterest

    DealRoomDetail:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
          nullable: true
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        status:
          type: string
          enum: [upcoming, live, closed]
        accessLevel:
          type: string
          enum: [vip, elite, invite_only]
        createdAt:
          type: string
          format: date-time
        itemCount:
          type: integer
        bidCount:
          type: integer
        items:
          type: array
          items:
            $ref: "#/components/schemas/DealRoomItem"
      required:
        - id
        - title
        - startTime
        - endTime
        - status
        - accessLevel
        - createdAt
        - itemCount
        - bidCount
        - items

    Bid:
      type: object
      properties:
        id:
          type: integer
        dealRoomId:
          type: integer
        dealId:
          type: integer
        investorUserId:
          type: integer
        offerAmount:
          type: number
        message:
          type: string
          nullable: true
        proofOfFundsUrl:
          type: string
          nullable: true
        status:
          type: string
          enum: [submitted, shortlisted, rejected, accepted]
        createdAt:
          type: string
          format: date-time
      required:
        - id
        - dealRoomId
        - dealId
        - investorUserId
        - offerAmount
        - status
        - createdAt

    AdminBid:
      type: object
      properties:
        id:
          type: integer
        dealRoomId:
          type: integer
        dealId:
          type: integer
        investorUserId:
          type: integer
        offerAmount:
          type: number
        message:
          type: string
          nullable: true
        proofOfFundsUrl:
          type: string
          nullable: true
        status:
          type: string
          enum: [submitted, shortlisted, rejected, accepted]
        createdAt:
          type: string
          format: date-time
        investor:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string
            email:
              type: string
            membership:
              type: string
          required:
            - id
            - name
            - email
            - membership
        dealTitle:
          type: string
      required:
        - id
        - dealRoomId
        - dealId
        - investorUserId
        - offerAmount
        - status
        - createdAt
        - investor
        - dealTitle

    CreateDealRoomBody:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        accessLevel:
          type: string
          enum: [vip, elite, invite_only]
      required:
        - title
        - startTime
        - endTime
        - accessLevel

    UpdateDealRoomBody:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        startTime:
          type: string
          format: date-time
        endTime:
          type: string
          format: date-time
        status:
          type: string
          enum: [upcoming, live, closed]
        accessLevel:
          type: string
          enum: [vip, elite, invite_only]

    CreateDealRoomItemBody:
      type: object
      properties:
        dealId:
          type: integer
        reservePrice:
          type: number
        minimumOffer:
          type: number
        sellerTerms:
          type: string
        closingTimeline:
          type: string
      required:
        - dealId

    SubmitBidBody:
      type: object
      properties:
        dealId:
          type: integer
        offerAmount:
          type: number
        message:
          type: string
        proofOfFundsUrl:
          type: string
      required:
        - dealId
        - offerAmount

    UpdateBidBody:
      type: object
      properties:
        status:
          type: string
          enum: [submitted, shortlisted, rejected, accepted]
      required:
        - status

    SendSellerPaymentLinkResponse:
      type: object
      properties:
        url:
          type: string
          nullable: true
        bankDetails:
          type: string
          nullable: true
        plan:
          type: string
        amount:
          type: number
      required:
        - plan
        - amount
