openapi: 3.0.3
info:
  title: ImpAcc User Profile API
  description: |
    Authenticate users against the ImpAcc user database and read their
    profile. Uses Laravel Sanctum bearer tokens.
  version: "1.0.0"
  contact:
    name: ImpAcc API Team
servers:
  - url: https://usr.impacc.work/api
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Auth
    description: Login / logout (token lifecycle)
  - name: Profile
    description: Read the authenticated user's profile

paths:
  /login:
    post:
      tags: [Auth]
      summary: Exchange credentials for a bearer token
      security: []   # public endpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
            examples:
              default:
                value:
                  citizen_id: "1234567890123"
                  password: "user-secret"
                  device_name: "partner-app-web"
      responses:
        '200':
          description: Login succeeded; token returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '422':
          description: Validation error or wrong credentials.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '429':
          description: Too many requests.

  /sso/exchange:
    post:
      tags: [Auth]
      summary: Exchange a one-time portal mToken for a bearer token (SSO)
      description: |
        Alternative to /login for apps launched from the imPACC portal. The
        portal mints a single-use mToken on the user record and redirects the
        browser with it; post it here to obtain a bearer token and profile
        without handling credentials. The mToken is cleared on success
        (single-use).
      security: []   # public endpoint; the mToken is the credential
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SsoExchangeRequest'
            examples:
              default:
                value:
                  mToken: "0f3a...64-char-token...d9"
                  device_name: "sso-web"
      responses:
        '200':
          description: Exchange succeeded; token returned (same shape as /login).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '422':
          description: Unknown, already-used, or expired mToken, or inactive account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '429':
          description: Too many requests.

  /logout:
    post:
      tags: [Auth]
      summary: Revoke the current bearer token
      responses:
        '200':
          description: Token revoked.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "Logged out"
        '401':
          $ref: '#/components/responses/Unauthenticated'

  /profile:
    get:
      tags: [Profile]
      summary: Get the authenticated user's profile
      responses:
        '200':
          description: Profile payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    $ref: '#/components/schemas/UserProfile'
        '401':
          $ref: '#/components/responses/Unauthenticated'

  /permissions:
    get:
      tags: [Profile]
      summary: Get the authenticated user's effective applications/menus/sections
      description: |
        Effective access resolved as
        (group grants ∪ direct user grants) ∖ user blocks, across the
        application → menu → section tiers. Groups are the union of
        workgroup-derived (workgroup_groups via users.workgroup_id) and
        direct (user_groups) memberships. The same object is also embedded
        as `user.permissions` in the login and profile responses.
      responses:
        '200':
          description: Effective permission tree.
          content:
            application/json:
              schema:
                type: object
                properties:
                  permissions:
                    $ref: '#/components/schemas/Permissions'
        '401':
          $ref: '#/components/responses/Unauthenticated'

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

  responses:
    Unauthenticated:
      description: Missing, invalid, or revoked token.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: "Unauthenticated."

  schemas:
    LoginRequest:
      type: object
      required: [citizen_id, password]
      properties:
        citizen_id:
          type: string
          description: 13-digit citizen ID.
          example: "1234567890123"
        password:
          type: string
          format: password
          example: "user-secret"
        device_name:
          type: string
          maxLength: 100
          description: Label stored with the issued token.
          example: "partner-app-web"

    SsoExchangeRequest:
      type: object
      required: [mToken]
      properties:
        mToken:
          type: string
          description: One-time token minted by the imPACC portal. Single-use.
          example: "0f3a...64-char-token...d9"
        device_name:
          type: string
          maxLength: 100
          description: Label stored with the issued token.
          example: "sso-web"

    LoginResponse:
      type: object
      properties:
        token:
          type: string
          description: "Bearer token. Send as `Authorization: Bearer <token>`."
          example: "12|H7c0gQ9b3l...redacted...XyZ"
        token_type:
          type: string
          example: "Bearer"
        user:
          $ref: '#/components/schemas/UserProfile'

    UserProfile:
      type: object
      properties:
        id:                { type: integer, example: 1234 }
        citizen_id:        { type: string, example: "1234567890123" }
        title:             { type: string, nullable: true, example: "นาย" }
        firstname:         { type: string, nullable: true, example: "สมชาย" }
        lastname:          { type: string, nullable: true, example: "ใจดี" }
        title_english:     { type: string, nullable: true, example: "Mr." }
        firstname_english: { type: string, nullable: true, example: "Somchai" }
        lastname_english:  { type: string, nullable: true, example: "Jaidee" }
        email:             { type: string, nullable: true, format: email, example: "somchai@example.com" }
        mobile:            { type: string, nullable: true, example: "0812345678" }
        born_date:         { type: string, nullable: true, format: date, example: "1990-05-12" }
        workgroup:         { type: string, nullable: true, example: "IT" }
        workgroup_id:      { type: integer, nullable: true, example: 39, description: "FK to workgroups; drives permission resolution." }
        division_id:       { type: integer, nullable: true, example: 2, description: "FK to divisions." }
        organization:      { type: string, nullable: true, example: "Impacc" }
        role_type1:        { type: string, nullable: true, example: "officer" }
        role_type2:        { type: string, nullable: true }
        role_type3:        { type: string, nullable: true }
        status:            { type: string, nullable: true, example: "1", description: "\"1\" = active." }
        roles:
          type: array
          items: { type: string }
          example: ["user"]
        last_login:
          type: string
          nullable: true
          format: date-time
          example: "2026-05-19T08:42:11.000000Z"
        current_profile:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/Profile'
          description: "The user's currently active OU profile (falls back to the default). Null if the user has no rows in user_profiles."
        profiles:
          type: array
          description: "All OU / special-mission assignments. Ordered by is_active DESC, is_default DESC, id."
          items:
            $ref: '#/components/schemas/Profile'
        permissions:
          $ref: '#/components/schemas/Permissions'

    Profile:
      type: object
      description: "One organizational-unit assignment for the user (home, secondary, or special mission)."
      properties:
        id:                   { type: integer, example: 1 }
        label:                { type: string, nullable: true, example: "ต้นสังกัด" }
        kind:
          type: string
          enum: [home, secondary, mission]
          description: "Convenience classifier derived from is_default / is_temporary."
          example: "home"
        dept1:                { type: string, nullable: true, example: "สำนักงาน ป.ป.ท." }
        dept2:                { type: string, nullable: true, example: "สำนักงาน ป.ป.ท." }
        dept3:                { type: string, nullable: true }
        officer_type_id:      { type: integer, nullable: true, example: 4 }
        officer_type_name:    { type: string, nullable: true, example: "ทั่วไป" }
        position_type_id:     { type: integer, nullable: true, example: 3 }
        position_type_name:   { type: string, nullable: true, example: "วิชาการ" }
        position:             { type: string, nullable: true, example: "นักวิชาการ" }
        description:          { type: string, nullable: true }
        level:                { type: string, nullable: true, example: "ปฏิบัติการ" }
        management_position:  { type: string, nullable: true }
        is_default:           { type: boolean, example: true }
        is_active:            { type: boolean, example: true }
        is_temporary:         { type: boolean, example: false }
        mission_title:        { type: string, nullable: true, example: "สืบสวน" }
        mission_note:         { type: string, nullable: true }
        mission_start_date:   { type: string, nullable: true, format: date, example: "2026-01-01" }
        mission_end_date:     { type: string, nullable: true, format: date, example: "2026-06-30" }
        mission_order_no:     { type: string, nullable: true, example: "123/2569" }
        mission_order_file:   { type: string, nullable: true, example: "orders/2569-123.pdf" }
        status:               { type: string, nullable: true, example: "approved" }
        approved_at:          { type: string, nullable: true, format: date-time }

    Permissions:
      type: object
      description: "The user's effective applications, menus and sections."
      properties:
        group_ids:
          type: array
          description: "Groups the user belongs to (workgroup-derived ∪ direct)."
          items: { type: integer }
          example: [1, 2, 3, 22, 23]
        applications:
          type: array
          description: "Effective applications, ordered by sequence then id."
          items:
            $ref: '#/components/schemas/PermissionApp'

    PermissionApp:
      type: object
      properties:
        id:     { type: integer, example: 22 }
        app_id: { type: string, nullable: true, example: "11-22" }
        name:   { type: string, nullable: true, example: "ระบบDIDC" }
        link:   { type: string, nullable: true, example: "/miniapp/didc" }
        menus:
          type: array
          description: "Effective menus under this application (empty if none)."
          items:
            $ref: '#/components/schemas/PermissionMenu'

    PermissionMenu:
      type: object
      properties:
        id:     { type: integer, example: 81 }
        name:   { type: string, nullable: true, example: "จัดการ Dashboard" }
        path:   { type: string, nullable: true, example: "/dashboard-management" }
        level:  { type: integer, nullable: true, example: 2 }
        parent: { type: integer, nullable: true, example: 75 }
        sections:
          type: array
          description: "Effective sections under this menu (empty until section data exists)."
          items:
            $ref: '#/components/schemas/PermissionSection'

    PermissionSection:
      type: object
      properties:
        id:   { type: integer, example: 5 }
        name: { type: string, nullable: true, example: "ภาพรวม" }

    ValidationError:
      type: object
      properties:
        message:
          type: string
          example: "The provided credentials are incorrect."
        errors:
          type: object
          additionalProperties:
            type: array
            items: { type: string }
          example:
            citizen_id: ["The provided credentials are incorrect."]
