openapi: 3.0.0
info:
  title: GatesServer API
  description: Person, visitor, and access-control management.
  version: 1.0.0
  contact:
    name: GatesServer Support
    email: support@gatesserver.com

servers:
  - url: http://localhost:3000
    description: Local Development
  - url: https://api.gatesserver.com
    description: Production

security:
  - ApiKeyAuth: []

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

  schemas:
    # ===== PERSON SCHEMAS =====
    Person:
      type: object
      required:
        - personCode
        - personGivenName
        - personFamilyName
        - gender
        - orgIndexCode
        - beginTime
        - endTime
      properties:
        personCode:
          type: string
          minLength: 1
          maxLength: 50
          description: Unique person identifier (employee ID, national ID, etc.)
          example: "EMP20260428001"
        personGivenName:
          type: string
          minLength: 1
          maxLength: 100
          description: Person's first name
          example: "Mostafa"
        personFamilyName:
          type: string
          minLength: 1
          maxLength: 100
          description: Person's last name
          example: "Ahmed"
        gender:
          type: integer
          enum: [0, 1, 2]
          description: "0: Unknown, 1: Male, 2: Female"
          example: 1
        orgIndexCode:
          type: string
          maxLength: 50
          description: Organization index code
          example: "ORG_TATWEER_001"
        phoneNo:
          type: string
          pattern: '^\+?[0-9\s\-()]{7,20}$'
          description: Phone number (E.164 format recommended)
          example: "+971500000000"
        email:
          type: string
          format: email
          description: Email address
          example: "mostafa.ahmed@example.com"
        beginTime:
          type: string
          format: date-time
          description: Access validity start (ISO 8601 with timezone)
          example: "2026-04-28T13:00:00+03:00"
        endTime:
          type: string
          format: date-time
          description: Access validity end (ISO 8601 with timezone). Must be within 10 years of beginTime
          example: "2033-04-28T13:00:00+03:00"
        faces:
          type: array
          description: Facial recognition data
          items:
            type: object
            properties:
              faceData:
                type: string
                format: byte
                description: Base64-encoded face image (JPEG/PNG)
        cards:
          type: array
          description: Physical card assignments
          items:
            type: object
            required:
              - cardNo
            properties:
              cardNo:
                type: string
                maxLength: 50
                description: Card number (RFID, barcode, etc.)
        customFields:
          type: array
          description: Custom field values (NOTE: field name uses 'customFiledName' - typo required by GatesServer API)
          items:
            type: object
            properties:
              id:
                type: string
                description: Custom field ID
              customFiledName:
                type: string
                description: Custom field name (exact spelling 'customFiledName' required)
              customFieldType:
                type: integer
                description: Field type (0: text, 1: number, 2: date, etc.)
              customFieldValue:
                type: string
                maxLength: 50
                description: Custom field value
        accessLevels:
          type: array
          description: Access privilege assignments
          items:
            type: object
            properties:
              privilegeGroupId:
                type: string
              type:
                type: integer
                enum: [1, 2]
                description: "1: Normal Person, 2: Visitor"

    CreatePersonResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            code:
              type: string
              example: "0"
            msg:
              type: string
              example: "Success"
            data:
              type: object
              properties:
                person:
                  type: object
                  properties:
                    personId:
                      type: string
                      example: "119"
                customFields:
                  type: object
                accessLevels:
                  type: array

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        data:
          type: object
          properties:
            code:
              type: string
              description: Error code (99999 for duplicates, 40001 for validation, etc.)
            msg:
              type: string
              description: Human-readable error message
            errorDetails:
              type: object
              description: Detailed error information
              properties:
                field:
                  type: string
                constraint:
                  type: string
                suggestion:
                  type: string

    # ===== CUSTOM FIELDS SCHEMAS =====
    CustomFieldUpdate:
      type: object
      required:
        - personId
        - list
      properties:
        personId:
          type: string
          description: Person ID to update
        list:
          type: array
          description: List of custom fields to update
          items:
            type: object
            properties:
              id:
                type: string
              customFiledName:
                type: string
                maxLength: 50
              customFieldType:
                type: integer
              customFieldValue:
                type: string
                maxLength: 50
                description: Can contain multiple comma-separated values (e.g., A-101,A-102,A-103)

    # ===== VISITOR SCHEMAS =====
    Visitor:
      type: object
      required:
        - receptionistId
        - visitStartTime
        - visitEndTime
        - visitPurposeType
        - visitorInfoList
      properties:
        receptionistId:
          type: integer
          description: ID of receptionist registering visitor
        visitStartTime:
          type: string
          format: date-time
          description: Visitor access start time (ISO 8601 with timezone)
        visitEndTime:
          type: string
          format: date-time
          description: Visitor access end time (must be after visitStartTime)
        visitPurposeType:
          type: integer
          description: Purpose of visit (1-N, check API for enum values)
        visitorInfoList:
          type: array
          items:
            type: object
            properties:
              VisitorInfo:
                type: object
                required:
                  - visitorFamilyName
                  - visitorGivenName
                  - gender
                properties:
                  visitorFamilyName:
                    type: string
                    maxLength: 100
                  visitorGivenName:
                    type: string
                    maxLength: 100
                  gender:
                    type: integer
                    enum: [0, 1, 2]
                  phoneNo:
                    type: string
                  cards:
                    type: array
                    items:
                      type: object
                      properties:
                        cardNo:
                          type: string
                  customField:
                    type: array
                    description: Note: visitor custom fields use 'customFieldName' (no typo)
                    items:
                      type: object
                      properties:
                        customID:
                          type: string
                        customFieldName:
                          type: string
                        customFieldType:
                          type: integer
                        customFieldValue:
                          type: string

    RegisterVisitorResponse:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            code:
              type: string
            msg:
              type: string
            data:
              type: object
              properties:
                visitorId:
                  type: string
                appointRecordId:
                  type: string
                qrCodeImage:
                  type: string
                  format: byte
                  description: Base64-encoded QR code image

paths:
  /api/persons:
    post:
      summary: Create Person
      description: Create a new person with cards, face data, custom fields, and access levels
      tags:
        - Person Management
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Person'
            examples:
              success:
                summary: Valid person creation
                value:
                  personCode: "EMP20260428001"
                  personGivenName: "Mostafa"
                  personFamilyName: "Ahmed"
                  gender: 1
                  orgIndexCode: "ORG_TATWEER_001"
                  phoneNo: "+971500000000"
                  email: "mostafa.ahmed@example.com"
                  beginTime: "2026-04-28T13:00:00+03:00"
                  endTime: "2033-04-28T13:00:00+03:00"
                  cards:
                    - cardNo: "CARD20260428001"
                  customFields:
                    - id: "1"
                      customFiledName: "unit id"
                      customFieldType: 0
                      customFieldValue: "A-101"
                  accessLevels:
                    - privilegeGroupId: "5"
                      type: 1
      responses:
        '201':
          description: Person created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatePersonResponse'
              example:
                success: true
                data:
                  code: "0"
                  msg: "Success"
                  data:
                    person:
                      personId: "119"
                    customFields:
                      "1": "A-101"
                    accessLevels:
                      - privilegeGroupId: "5"
                        type: 1
        '409':
          description: Duplicate personCode
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                data:
                  code: "99999"
                  msg: "Person with personCode 'EMP20260428001' already exists"
                  errorDetails:
                    field: "personCode"
                    constraint: "UNIQUE"
                    suggestion: "Use a unique personCode or call GET /api/persons/code/{code}"
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/persons/code/{code}:
    get:
      summary: Lookup Person By Code
      description: Retrieve person details by personCode (used after duplicate error)
      tags:
        - Person Management
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
          example: "EMP20260428001"
      responses:
        '200':
          description: Person found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      data:
                        type: object
                        properties:
                          personId:
                            type: string
                          personCode:
                            type: string
        '404':
          description: Person not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/persons/{personId}:
    put:
      summary: Update Person
      description: Update existing person information
      tags:
        - Person Management
      parameters:
        - name: personId
          in: path
          required: true
          schema:
            type: string
          example: "119"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Person'
      responses:
        '200':
          description: Person updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      msg:
                        type: string

  /api/persons/custom-fields/update:
    post:
      summary: Update Person Custom Fields
      description: Update custom field values for an existing person (partial update)
      tags:
        - Custom Fields
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomFieldUpdate'
            examples:
              success:
                summary: Valid custom fields update
                value:
                  personId: "119"
                  list:
                    - id: "1"
                      customFiledName: "unit id"
                      customFieldType: 0
                      customFieldValue: "A-101,A-102,A-103"
              error:
                summary: Invalid format (exceeds max length)
                value:
                  personId: "119"
                  list:
                    - id: "1"
                      customFiledName: "unit id"
                      customFieldType: 0
                      customFieldValue: "INVALID_UNIT_FORMAT_EXCEEDS_MAX_LENGTH_012345678901234567890"
      responses:
        '200':
          description: Custom fields updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      msg:
                        type: string
                      data:
                        type: object
                        properties:
                          personId:
                            type: string
                          updatedFields:
                            type: array
        '400':
          description: Validation error (max length, invalid format, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                data:
                  code: "40001"
                  msg: "Validation failed for custom field 'unit id'"
                  errorDetails:
                    field: "customFieldValue"
                    fieldId: "1"
                    fieldName: "unit id"
                    maxLength: 50
                    receivedLength: 58
                    constraint: "MAX_LENGTH"
                    suggestion: "Field value must not exceed 50 characters"

  /api/visitors:
    post:
      summary: Register Visitor
      description: Register a visitor with appointment and generate QR code
      tags:
        - Visitor Management
      parameters:
        - name: version
          in: query
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Visitor'
            examples:
              success:
                summary: Valid visitor registration
                value:
                  receptionistId: 1
                  visitStartTime: "2026-04-28T13:00:00+03:00"
                  visitEndTime: "2026-04-28T23:59:59+03:00"
                  visitPurposeType: 1
                  visitorInfoList:
                    - VisitorInfo:
                        visitorFamilyName: "Guest"
                        visitorGivenName: "Ahmed"
                        gender: 1
                        phoneNo: "+971500000100"
                        cards:
                          - cardNo: "GUESTCARD20260428001"
                        customField:
                          - customID: "4"
                            customFieldName: "unit id"
                            customFieldType: 0
                            customFieldValue: "A-101"
              error:
                summary: Invalid datetime (endTime before startTime)
                value:
                  receptionistId: 1
                  visitStartTime: "2026-04-28T23:59:59+03:00"
                  visitEndTime: "2026-04-28T13:00:00+03:00"
                  visitPurposeType: 1
                  visitorInfoList:
                    - VisitorInfo:
                        visitorFamilyName: "Guest"
                        visitorGivenName: "Ahmed"
                        gender: 1
      responses:
        '201':
          description: Visitor registered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterVisitorResponse'
              example:
                success: true
                data:
                  code: "0"
                  msg: "Success"
                  data:
                    visitorId: "2001"
                    appointRecordId: "3001"
                    qrCodeImage: "iVBORw0KGgoAAAANSUhEUgAAAGQAAABk..."
        '400':
          description: Validation error (invalid datetime range, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                data:
                  code: "40002"
                  msg: "Validation failed: Visit datetime range is invalid"
                  errorDetails:
                    field: "visitEndTime"
                    constraint: "DATETIME_RANGE"
                    issue: "visitEndTime must be after visitStartTime"
                    suggestion: "Ensure visitEndTime is greater than visitStartTime"

  /api/visitors/{visitorId}/qr:
    post:
      summary: Get Visitor QR Code
      description: Fetch QR code for an existing visitor
      tags:
        - Visitor Management
      parameters:
        - name: visitorId
          in: path
          required: true
          schema:
            type: string
          example: "2001"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                visitorId:
                  type: string
      responses:
        '200':
          description: QR code retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      data:
                        type: object
                        properties:
                          qRCodeInfo:
                            type: object
                            properties:
                              qrCodeImage:
                                type: string
                                format: byte

  /api/persons/{personId}/access-level:
    post:
      summary: Assign Person to Access Level
      description: Grant access privileges to person or visitor
      tags:
        - Access Control
      parameters:
        - name: personId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - privilegeGroupId
                - type
              properties:
                privilegeGroupId:
                  type: string
                type:
                  type: integer
                  enum: [1, 2]
                  description: "1: Normal Person, 2: Visitor"
      responses:
        '200':
          description: Access level assigned
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      msg:
                        type: string

  /api/access-levels:
    get:
      summary: Get Access Levels
      description: Fetch all available access levels with pagination
      tags:
        - Access Control
      parameters:
        - name: pageNo
          in: query
          schema:
            type: integer
          example: 1
        - name: pageSize
          in: query
          schema:
            type: integer
          example: 100
        - name: type
          in: query
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: Access levels retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                      data:
                        type: object
                        properties:
                          list:
                            type: array
                            items:
                              type: object
                              properties:
                                privilegeGroupId:
                                  type: string
                                privilegeGroupName:
                                  type: string

tags:
  - name: Person Management
    description: Create, read, update person information
  - name: Custom Fields
    description: Manage custom field values
  - name: Visitor Management
    description: Register and manage visitors
  - name: Access Control
    description: Manage access privileges and levels
