openapi: 3.0.3
info:
  title: IPS Worklist API
  version: 1.0.0
  description: |
    API REST de integración HIS — worklist RIS/PACS.

    **Manual:** `/integracion` · **Swagger UI:** `/integracion/swagger`

    - Paciente → **DNI**
    - Estudio → **Accession Number**
    - Autenticación → Bearer token (POST /auth/login)

servers:
  - url: https://api.ipsalud.com.ar
    description: Servidor de pruebas (reemplazado automáticamente por APP_URL)

tags:
  - name: Auth
  - name: Catalog
  - name: Patients
  - name: Worklist

paths:
  /auth/login:
    post:
      tags: [Auth]
      summary: Login usuario integración
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [password]
              properties:
                email: { type: string, format: email }
                username: { type: string }
                password: { type: string, format: password }
      responses:
        '200':
          description: Token Bearer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '422':
          description: Credenciales inválidas
        '429':
          description: Demasiados intentos

  /auth/logout:
    post:
      tags: [Auth]
      security: [{ bearerAuth: [] }]
      responses:
        '200': { description: OK }

  /auth/me:
    get:
      tags: [Auth]
      security: [{ bearerAuth: [] }]
      responses:
        '200': { description: Perfil API }

  /catalog/sites:
    get:
      tags: [Catalog]
      security: [{ bearerAuth: [] }]
      summary: Sedes activas
      responses:
        '200': { description: Lista de sedes }

  /catalog/procedures:
    get:
      tags: [Catalog]
      security: [{ bearerAuth: [] }]
      summary: Catálogo de estudios
      parameters:
        - name: modality
          in: query
          schema: { type: string }
        - name: code
          in: query
          schema: { type: string }
      responses:
        '200': { description: Procedimientos }

  /catalog/devices:
    get:
      tags: [Catalog]
      security: [{ bearerAuth: [] }]
      summary: Equipos DICOM (AE Titles)
      parameters:
        - name: modality
          in: query
          schema: { type: string }
        - name: site_code
          in: query
          schema: { type: string }
        - name: site_id
          in: query
          schema: { type: integer }
      responses:
        '200': { description: Dispositivos }

  /patients:
    get:
      tags: [Patients]
      security: [{ bearerAuth: [] }]
      summary: Buscar por DNI
      parameters:
        - name: dni
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Pacientes }

  /patients/{dni}:
    get:
      tags: [Patients]
      security: [{ bearerAuth: [] }]
      summary: Paciente por DNI
      parameters:
        - name: dni
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Paciente }
        '404': { description: No encontrado }

  /worklist:
    get:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Listar órdenes
      parameters:
        - { name: dni, in: query, schema: { type: string } }
        - { name: accession_number, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string } }
        - { name: modality, in: query, schema: { type: string } }
        - { name: study_date, in: query, schema: { type: string, format: date } }
        - { name: per_page, in: query, schema: { type: integer } }
      responses:
        '200': { description: Listado paginado }
    post:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Alta orden + MWL
      parameters:
        - name: Idempotency-Key
          in: header
          schema: { type: string }
          description: Evita duplicados en reintentos (24h)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorklistCreate'
      responses:
        '201': { description: Creada }
        '422': { description: Validación / catálogo }

  /worklist/{accession_number}:
    get:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Detalle por accession
      parameters:
        - $ref: '#/components/parameters/AccessionNumber'
      responses:
        '200': { description: Orden }
        '404': { description: No encontrada }
    patch:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Modificar orden
      parameters:
        - $ref: '#/components/parameters/AccessionNumber'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorklistUpdate'
      responses:
        '200': { description: Actualizada }
        '409': { description: Estado no editable }
    delete:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Cancelar orden
      parameters:
        - $ref: '#/components/parameters/AccessionNumber'
      responses:
        '200': { description: Cancelada }
        '409': { description: No cancelable }

  /worklist/{accession_number}/mwl/resend:
    post:
      tags: [Worklist]
      security: [{ bearerAuth: [] }]
      summary: Reenviar MWL
      parameters:
        - $ref: '#/components/parameters/AccessionNumber'
      responses:
        '200': { description: MWL reenviada }

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

  parameters:
    AccessionNumber:
      name: accession_number
      in: path
      required: true
      schema: { type: string }

  schemas:
    LoginResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            token_type: { type: string, example: Bearer }
            access_token: { type: string }
            expires_in: { type: integer, example: 86400 }
            user:
              type: object
              properties:
                id: { type: integer }
                email: { type: string }
                client_id: { type: integer }
        meta:
          type: object
          properties:
            timestamp: { type: string, format: date-time }

    ApiError:
      type: object
      properties:
        message: { type: string }
        errors: { type: object, additionalProperties: true }

    WorklistCreate:
      type: object
      required: [patient, order, studies]
      properties:
        site_code: { type: string, example: RES }
        site_id: { type: integer }
        patient:
          type: object
          required: [dni, last_name, first_name]
          properties:
            dni: { type: string }
            last_name: { type: string }
            first_name: { type: string }
            birth_date: { type: string, format: date }
            sex: { type: string, enum: [M, F, O] }
        order:
          type: object
          required: [scheduled_at]
          properties:
            scheduled_at: { type: string, format: date-time }
            priority: { type: string }
            requesting_physician: { type: string }
        studies:
          type: array
          minItems: 1
          items:
            type: object
            required: [scheduled_start_at]
            properties:
              procedure_code: { type: string }
              procedure_catalog_id: { type: integer }
              station_aet: { type: string }
              dicom_device_id: { type: integer }
              scheduled_start_at: { type: string, format: date-time }

    WorklistUpdate:
      type: object
      properties:
        patient: { type: object }
        order: { type: object }
        studies: { type: array, items: { type: object } }
        resend_mwl: { type: boolean }
