openapi: 3.0.3
info:
  title: IZUBA API
  version: 2.0.0
  description: Complete API specification for IZUBA loan management system with multi-level reviews, savings management, and reporting.
  contact:
    name: IZUBA Team
  license:
    name: MIT

servers:
  - url: http://localhost:{port}
    variables:
      port:
        default: "3000"

paths:
  /api/auth/login:
    post:
      summary: Authenticate user and receive JWT token
      tags: [Auth]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          description: Successfully authenticated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoginResponse"
        "400":
          description: Invalid request format
        "401":
          description: Invalid credentials

  /api/auth/me:
    get:
      summary: Get currently authenticated user
      tags: [Auth]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Current user details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized or invalid token

  /api/users:
    post:
      summary: Create new user (admin only)
      tags: [Users]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUserRequest"
      responses:
        "201":
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin role required

    get:
      summary: List all users (admin and accountant only)
      tags: [Users]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: List of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin/accountant role required

  /api/users/{userId}:
    patch:
      summary: Update user details (admin only)
      tags: [Users]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: userId
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                fullName:
                  type: string
                  minLength: 1
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 6
                netsallary:
                  type: number
                  format: decimal
                  minimum: 0
                savingAmount:
                  type: number
                  format: decimal
                  minimum: 0
                role:
                  type: string
                  enum: [ADMIN, MEMBER, ACCOUNTANT, LOAN_COMMITTEE, AUDITOR]
      responses:
        "200":
          description: User updated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin role required
        "404":
          description: User not found
        "409":
          description: Email already in use

  /api/users/{userId}/activate:
    patch:
      summary: Activate a member (admin only)
      tags: [Users]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: userId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Member activated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin role required
        "404":
          description: User not found

  /api/users/{userId}/deactivate:
    patch:
      summary: Deactivate a member (admin only)
      tags: [Users]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: userId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Member deactivated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin role required
        "404":
          description: User not found

  /api/members/{memberId}/exit-balance:
    get:
      summary: Calculate exit balance for a member
      tags: [Members]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: memberId
          required: true
          schema:
            type: string
            format: uuid
          description: Member ID
      responses:
        "200":
          description: Exit balance calculation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExitBalanceResponse"
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/savings/balance:
    get:
      summary: Get current savings balance
      tags: [Savings]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
          description: Member ID (if admin querying another user)
      responses:
        "200":
          description: Current balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  memberId:
                    type: string
                  balance:
                    type: number
                    format: decimal
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/savings/transactions:
    get:
      summary: List savings transactions
      tags: [Savings]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
            maximum: 1000
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of transactions with pagination
          content:
            application/json:
              schema:
                type: object
                properties:
                  transactions:
                    type: array
                    items:
                      $ref: "#/components/schemas/SavingsTransaction"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized

  /api/savings/deposit:
    post:
      summary: Deposit to savings
      tags: [Savings]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                memberId:
                  type: string
                  format: uuid
                  description: Member ID (admin can deposit for others)
                amount:
                  type: number
                  format: decimal
                  minimum: 0.01
              required: [memberId, amount]
      responses:
        "201":
          description: Deposit recorded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SavingsTransaction"
        "400":
          description: Invalid amount
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/savings/withdraw:
    post:
      summary: Withdraw from savings
      tags: [Savings]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                memberId:
                  type: string
                  format: uuid
                amount:
                  type: number
                  format: decimal
                  minimum: 0.01
              required: [memberId, amount]
      responses:
        "201":
          description: Withdrawal recorded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SavingsTransaction"
        "400":
          description: Invalid amount or insufficient balance
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/savings/admin/set-balance:
    post:
      summary: Admin set user's savings balance directly
      tags: [Savings]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                memberId:
                  type: string
                  format: uuid
                amount:
                  type: number
                  format: decimal
                  minimum: 0
              required: [memberId, amount]
      responses:
        "200":
          description: Balance updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  memberId:
                    type: string
                  newBalance:
                    type: number
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only
        "404":
          description: Member not found

  /api/savings/upload-csv:
    post:
      summary: Upload savings transactions via CSV (admin only)
      tags: [Savings]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: CSV file with columns - memberId, amount, type (DEPOSIT/WITHDRAW)
              required: [file]
      responses:
        "200":
          description: CSV processed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  processed:
                    type: integer
                  skipped:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        "400":
          description: Invalid CSV format
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only

  /api/savings/export:
    get:
      summary: Export savings transactions as CSV
      tags: [Savings]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: CSV file
          content:
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          description: Unauthorized

  /api/interest-rates:
    post:
      summary: Create interest rate range (admin only)
      tags: [InterestRates]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InterestRangeCreate"
      responses:
        "201":
          description: Interest range created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InterestRange"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only

    get:
      summary: List all interest rate ranges (admin only)
      tags: [InterestRates]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of interest ranges
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/InterestRange"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only

    put:
      summary: Update interest rate range (admin only)
      tags: [InterestRates]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InterestRangeUpdate"
      responses:
        "200":
          description: Interest range updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InterestRange"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only
        "404":
          description: Interest range not found

  /api/interest-rates/{id}:
    delete:
      summary: Delete interest rate range (admin only)
      tags: [InterestRates]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Interest range deleted
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only
        "404":
          description: Interest range not found

  /api/interest-rates/match:
    get:
      summary: Find matching interest rate for term months
      tags: [InterestRates]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: termMonths
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Matching interest rate
          content:
            application/json:
              schema:
                type: object
                properties:
                  interestRate:
                    type: number
                    format: decimal
        "400":
          description: No matching rate found
        "401":
          description: Unauthorized

  /api/loans/calc:
    post:
      summary: Calculate loan repayment preview (simple interest)
      tags: [Loans]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoanCalcRequest"
      responses:
        "200":
          description: Loan calculation result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoanCalcResponse"
        "400":
          description: Invalid parameters

  /api/loans/limit:
    get:
      summary: Get computed loan limit for member
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Computed loan limit
          content:
            application/json:
              schema:
                type: object
                properties:
                  loanLimit:
                    type: number
                    format: decimal
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/loans/applications:
    post:
      summary: Submit new loan application
      tags: [Loans]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                amount:
                  type: number
                  format: decimal
                  minimum: 0.01
                interestRate:
                  type: number
                  format: decimal
                  minimum: 0
                termMonths:
                  type: integer
                  minimum: 1
                purpose:
                  type: string
                guarantorId:
                  type: string
                  format: uuid
                  description: Required if amount exceeds member's limit
                memberId:
                  type: string
                  format: uuid
                  description: Member to apply loan for (admin only). If not provided, applies for current user
                documents:
                  type: array
                  items:
                    type: string
                    format: binary
                  maxItems: 5
              required: [amount, interestRate, termMonths]
      responses:
        "201":
          description: Application submitted
          content:
            application/json:
              schema:
                type: object
                properties:
                  applicationId:
                    type: string
                    format: uuid
                  calculation:
                    $ref: "#/components/schemas/LoanCalcResponse"
        "400":
          description: Invalid request or amount exceeds limit without guarantor
        "401":
          description: Unauthorized
        "404":
          description: Member or guarantor not found

    get:
      summary: List loan applications
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
        - in: query
          name: format
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        "200":
          description: List of applications
          content:
            application/json:
              schema:
                type: object
                properties:
                  applications:
                    type: array
                    items:
                      $ref: "#/components/schemas/LoanApplicationDetail"
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          description: Unauthorized

  /api/loans/applications/{id}:
    get:
      summary: Get loan application details
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Application details with reviews
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoanApplicationDetail"
        "401":
          description: Unauthorized
        "404":
          description: Application not found

  /api/loans/applications/{applicationId}/review:
    post:
      summary: Submit loan application review (level 1-3, accountant/admin only)
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: applicationId
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoanReviewRequest"
      responses:
        "201":
          description: Review submitted
          content:
            application/json:
              schema:
                type: object
                properties:
                  review:
                    $ref: "#/components/schemas/LoanReview"
                  application:
                    $ref: "#/components/schemas/LoanApplicationDetail"
        "400":
          description: Cannot review at current level or already rejected
        "401":
          description: Unauthorized
        "404":
          description: Application not found

  /api/loans/{applicationId}/approve:
    post:
      summary: Approve loan application (creates loan record with repayment schedule)
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: applicationId
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Loan created with repayment schedule
          content:
            application/json:
              schema:
                type: object
                properties:
                  loanId:
                    type: string
                    format: uuid
                  schedule:
                    type: array
                    items:
                      $ref: "#/components/schemas/RepaymentScheduleItem"
        "400":
          description: Application must pass all 3 review levels or has rejections
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin/loan committee only
        "404":
          description: Application not found

  /api/loans/{applicationId}/reject:
    post:
      summary: Reject loan application
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: applicationId
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
              required: [reason]
      responses:
        "200":
          description: Application rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoanApplication"
        "400":
          description: Cannot reject - loan already created
        "401":
          description: Unauthorized
        "404":
          description: Application not found

  /api/loans:
    get:
      summary: List loans with optional filters
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: status
          schema:
            type: string
            enum: [ACTIVE, COMPLETED, DEFAULTED]
        - in: query
          name: outstandingOnly
          schema:
            type: string
            enum: ["true", "false", "1", "0"]
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of loans
          content:
            application/json:
              schema:
                type: object
                properties:
                  loans:
                    type: array
                    items:
                      $ref: "#/components/schemas/LoanDetail"
        "401":
          description: Unauthorized

  /api/loans/{loanId}:
    get:
      summary: Get loan details with repayment history
      tags: [Loans]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: loanId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Loan details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoanDetail"
        "401":
          description: Unauthorized
        "404":
          description: Loan not found

  /api/repayments:
    post:
      summary: Record loan repayment (admin/accountant only)
      tags: [Repayments]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RepaymentRequest"
      responses:
        "201":
          description: Repayment recorded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Repayment"
        "400":
          description: Invalid loan or insufficient amount
        "401":
          description: Unauthorized
        "404":
          description: Loan not found

    get:
      summary: List repayments with optional filters
      tags: [Repayments]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: loanId
          schema:
            type: string
            format: uuid
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of repayments
          content:
            application/json:
              schema:
                type: object
                properties:
                  repayments:
                    type: array
                    items:
                      $ref: "#/components/schemas/Repayment"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized

  /api/disbursements:
    post:
      summary: Create social disbursement (admin/accountant only)
      tags: [Disbursements]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DisbursementRequest"
      responses:
        "201":
          description: Disbursement created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Disbursement"
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Member not found

    get:
      summary: List disbursements (admin/accountant only)
      tags: [Disbursements]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of disbursements
          content:
            application/json:
              schema:
                type: object
                properties:
                  disbursements:
                    type: array
                    items:
                      $ref: "#/components/schemas/Disbursement"
                  total:
                    type: integer
                  totalAmount:
                    type: number
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin/accountant only

  /api/disbursements/exit/{memberId}:
    post:
      summary: Process member exit - payout all non-SOCIAL savings
      tags: [Disbursements]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: memberId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Exit processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  memberId:
                    type: string
                  payoutAmount:
                    type: number
                  remainingSocial:
                    type: number
        "400":
          description: Member has outstanding loans
        "401":
          description: Unauthorized
        "404":
          description: Member not found

  /api/guarantor-locks:
    get:
      summary: List all guarantor locks (admin only)
      tags: [GuarantorLocks]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of locks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/GuarantorLock"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only

  /api/guarantor-locks/{id}/release:
    post:
      summary: Release guarantor lock (admin only)
      tags: [GuarantorLocks]
      security:
        - bearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Lock released
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GuarantorLock"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin only
        "404":
          description: Lock not found

  /api/reports/summary:
    get:
      summary: Get summary report (aggregated totals)
      tags: [Reports]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: from
          schema:
            type: string
            format: date
          description: Start date (defaults to 3 days ago)
        - in: query
          name: to
          schema:
            type: string
            format: date
          description: End date
        - in: query
          name: format
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        "200":
          description: Summary report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReportSummary"
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          description: Unauthorized

  /api/reports/repayments:
    get:
      summary: Get repayments report with detailed list
      tags: [Reports]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: from
          schema:
            type: string
            format: date
          description: Start date (defaults to 3 days ago)
        - in: query
          name: to
          schema:
            type: string
            format: date
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
        - in: query
          name: format
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        "200":
          description: Repayments report
          content:
            application/json:
              schema:
                type: object
                properties:
                  repayments:
                    type: array
                    items:
                      $ref: "#/components/schemas/Repayment"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          description: Unauthorized

  /api/reports/disbursements:
    get:
      summary: Get disbursements report (admin/accountant only)
      tags: [Reports]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: from
          schema:
            type: string
            format: date
          description: Start date (defaults to 3 days ago)
        - in: query
          name: to
          schema:
            type: string
            format: date
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
        - in: query
          name: format
          schema:
            type: string
            enum: [json, csv]
            default: json
      responses:
        "200":
          description: Disbursements report
          content:
            application/json:
              schema:
                type: object
                properties:
                  disbursements:
                    type: array
                    items:
                      $ref: "#/components/schemas/Disbursement"
                  total:
                    type: integer
                  totalAmount:
                    type: number
                  limit:
                    type: integer
                  offset:
                    type: integer
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - admin/accountant only

  /api/reports/loans:
    get:
      summary: Get loans report with aggregates and filters
      tags: [Reports]
      security:
        - bearerAuth: []
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [ACTIVE, COMPLETED, DEFAULTED]
        - in: query
          name: memberId
          schema:
            type: string
            format: uuid
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Loans report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoansReport"
        "401":
          description: Unauthorized

  /api/reports/users:
    get:
      summary: Get users report with statistics
      tags: [Reports]
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Users report
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalUsers:
                    type: integer
                  usersByRole:
                    type: object
                    properties:
                      ADMIN:
                        type: integer
                      MEMBER:
                        type: integer
                      ACCOUNTANT:
                        type: integer
                      LOAN_COMMITTEE:
                        type: integer
                      AUDITOR:
                        type: integer
                  users:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
        "401":
          description: Unauthorized

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          format: uuid
        fullName:
          type: string
        email:
          type: string
          format: email
        netsallary:
          type: number
          format: decimal
          nullable: true
        savingAmount:
          type: number
          format: decimal
          default: 0
        role:
          type: string
          enum: [ADMIN, MEMBER, ACCOUNTANT, LOAN_COMMITTEE, AUDITOR]
        isActive:
          type: boolean
          default: true
        createdAt:
          type: string
          format: date-time
      required: [id, fullName, email, role, isActive, createdAt]

    CreateUserRequest:
      type: object
      properties:
        fullName:
          type: string
          minLength: 1
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 6
        netsallary:
          type: number
          format: decimal
          minimum: 0
        savingAmount:
          type: number
          format: decimal
          minimum: 0
        role:
          type: string
          enum: [ADMIN, MEMBER, ACCOUNTANT, LOAN_COMMITTEE, AUDITOR]
      required: [fullName, email, password]

    LoginRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 6
      required: [email, password]

    LoginResponse:
      type: object
      properties:
        token:
          type: string
          description: JWT token for authentication
        user:
          $ref: "#/components/schemas/User"
      required: [token, user]

    SavingsTransaction:
      type: object
      properties:
        id:
          type: string
          format: uuid
        memberId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
        type:
          type: string
          enum: [DEPOSIT, WITHDRAW, SOCIAL, ADMIN]
        reason:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
      required: [id, memberId, amount, type, createdAt]

    InterestRange:
      type: object
      properties:
        id:
          type: string
          format: uuid
        minMonths:
          type: integer
          minimum: 1
        maxMonths:
          type: integer
          minimum: 1
        interestRate:
          type: number
          format: decimal
          description: Annual interest rate as percentage
        createdBy:
          type: string
          format: uuid
          nullable: true
        createdAt:
          type: string
          format: date-time
      required: [id, minMonths, maxMonths, interestRate, createdAt]

    InterestRangeCreate:
      type: object
      properties:
        minMonths:
          type: integer
          minimum: 1
        maxMonths:
          type: integer
          minimum: 1
        interestRate:
          type: number
          format: decimal
          minimum: 0
      required: [minMonths, maxMonths, interestRate]

    InterestRangeUpdate:
      type: object
      properties:
        id:
          type: string
          format: uuid
        minMonths:
          type: integer
          minimum: 1
        maxMonths:
          type: integer
          minimum: 1
        interestRate:
          type: number
          format: decimal
          minimum: 0
      required: [id]

    LoanCalcRequest:
      type: object
      properties:
        amount:
          type: number
          format: decimal
          minimum: 0.01
        interestRate:
          type: number
          format: decimal
          minimum: 0
        termMonths:
          type: integer
          minimum: 1
      required: [amount, interestRate, termMonths]

    LoanCalcResponse:
      type: object
      properties:
        monthlyPayment:
          type: number
          format: decimal
        totalInterest:
          type: number
          format: decimal
        totalRepayment:
          type: number
          format: decimal
      required: [monthlyPayment, totalInterest, totalRepayment]

    LoanApplication:
      type: object
      properties:
        id:
          type: string
          format: uuid
        memberId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
        interestRate:
          type: number
          format: decimal
        termMonths:
          type: integer
        purpose:
          type: string
          nullable: true
        guarantorId:
          type: string
          format: uuid
          nullable: true
        reviewLevel:
          type: integer
          minimum: 0
          maximum: 3
        decisionReason:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
      required:
        [id, memberId, amount, interestRate, termMonths, reviewLevel, createdAt]

    LoanApplicationDetail:
      allOf:
        - $ref: "#/components/schemas/LoanApplication"
        - type: object
          properties:
            member:
              $ref: "#/components/schemas/User"
            guarantor:
              $ref: "#/components/schemas/User"
              nullable: true
            reviews:
              type: array
              items:
                $ref: "#/components/schemas/LoanReview"
            loan:
              $ref: "#/components/schemas/Loan"
              nullable: true
            documents:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  filename:
                    type: string
                  path:
                    type: string

    LoanReview:
      type: object
      properties:
        id:
          type: string
          format: uuid
        loanApplicationId:
          type: string
          format: uuid
        reviewerId:
          type: string
          format: uuid
        reviewer:
          $ref: "#/components/schemas/User"
          nullable: true
        level:
          type: integer
          minimum: 1
          maximum: 3
          description: Review level (1, 2, or 3)
        decision:
          type: string
          enum: [APPROVED, REJECTED]
        comment:
          type: string
          nullable: true
        reviewedAt:
          type: string
          format: date-time
      required: [id, loanApplicationId, reviewerId, level, decision]

    LoanReviewRequest:
      type: object
      properties:
        applicationId:
          type: string
          format: uuid
        decision:
          type: string
          enum: [APPROVED, REJECTED]
        comment:
          type: string
          nullable: true
      required: [applicationId, decision]

    Loan:
      type: object
      properties:
        id:
          type: string
          format: uuid
        applicationId:
          type: string
          format: uuid
        principal:
          type: number
          format: decimal
        interestRate:
          type: number
          format: decimal
        termMonths:
          type: integer
        status:
          type: string
          enum: [ACTIVE, COMPLETED, DEFAULTED]
        outstandingPrincipal:
          type: number
          format: decimal
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time
      required:
        [
          id,
          applicationId,
          principal,
          interestRate,
          termMonths,
          status,
          outstandingPrincipal,
        ]

    LoanDetail:
      allOf:
        - $ref: "#/components/schemas/Loan"
        - type: object
          properties:
            application:
              $ref: "#/components/schemas/LoanApplication"
            repayments:
              type: array
              items:
                $ref: "#/components/schemas/Repayment"

    RepaymentScheduleItem:
      type: object
      properties:
        installment:
          type: integer
        dueDate:
          type: string
          format: date-time
        principalPayment:
          type: number
          format: decimal
        interest:
          type: number
          format: decimal
        total:
          type: number
          format: decimal
        remaining:
          type: number
          format: decimal
      required:
        [installment, dueDate, principalPayment, interest, total, remaining]

    Repayment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        loanId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
        date:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
      required: [id, loanId, amount, date]

    RepaymentRequest:
      type: object
      properties:
        loanId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
          minimum: 0.01
        date:
          type: string
          format: date
          nullable: true
      required: [loanId, amount]

    Disbursement:
      type: object
      properties:
        id:
          type: string
          format: uuid
        memberId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
        date:
          type: string
          format: date-time
        reason:
          type: string
          nullable: true
        member:
          $ref: "#/components/schemas/User"
          nullable: true
        createdAt:
          type: string
          format: date-time
      required: [id, memberId, amount, date]

    DisbursementRequest:
      type: object
      properties:
        memberId:
          type: string
          format: uuid
        amount:
          type: number
          format: decimal
          minimum: 0.01
        date:
          type: string
          format: date
          nullable: true
        reason:
          type: string
          nullable: true
      required: [memberId, amount]

    GuarantorLock:
      type: object
      properties:
        id:
          type: string
          format: uuid
        guarantorId:
          type: string
          format: uuid
        loanApplicationId:
          type: string
          format: uuid
        amountLocked:
          type: number
          format: decimal
        amountReleased:
          type: number
          format: decimal
          default: 0
        createdAt:
          type: string
          format: date-time
      required: [id, guarantorId, loanApplicationId, amountLocked, createdAt]

    ExitBalanceResponse:
      type: object
      properties:
        memberId:
          type: string
          format: uuid
        totalSavings:
          type: number
          format: decimal
        outstandingPrincipal:
          type: number
          format: decimal
        outstandingInterest:
          type: number
          format: decimal
        socialContributions:
          type: number
          format: decimal
        exitBalance:
          type: number
          format: decimal
          description: Total amount member receives on exit
      required:
        [
          memberId,
          totalSavings,
          outstandingPrincipal,
          outstandingInterest,
          socialContributions,
          exitBalance,
        ]

    ReportSummary:
      type: object
      properties:
        totalSavings:
          type: number
          format: decimal
        totalOutstandingPrincipal:
          type: number
          format: decimal
        socialContributions:
          type: number
          format: decimal
      required: [totalSavings, totalOutstandingPrincipal, socialContributions]

    LoansReport:
      type: object
      properties:
        loans:
          type: array
          items:
            $ref: "#/components/schemas/LoanDetail"
        total:
          type: integer
        totalOutstanding:
          type: number
          format: decimal
        totalPrincipal:
          type: number
          format: decimal
        statusCounts:
          type: object
          additionalProperties:
            type: integer
        limit:
          type: integer
        offset:
          type: integer
      required: [loans, total, totalOutstanding, totalPrincipal]

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token obtained from /api/auth/login

security:
  - bearerAuth: []

tags:
  - name: Auth
    description: Authentication endpoints
  - name: Users
    description: User management
  - name: Members
    description: Member-specific operations
  - name: Savings
    description: Savings accounts and transactions
  - name: InterestRates
    description: Loan interest rate ranges
  - name: Loans
    description: Loan applications and management
  - name: Repayments
    description: Loan repayment tracking
  - name: Disbursements
    description: Social contributions and payouts
  - name: GuarantorLocks
    description: Guarantor savings locks
  - name: Reports
    description: Business intelligence and reporting
