openapi: 3.0.3
info:
  title: Nomli — API de Integração Externa
  description: |
    API REST para integração de sistemas terceiros (ERPs, PDVs) com a plataforma Nomli.

    ## Autenticação
    Todas as requisições (exceto `/docs` e `/openapi`) exigem o header:
    ```
    Authorization: Bearer <seu_token>
    ```
    O token é gerado no painel do restaurante em **Integrações → Tokens** ou pelo gestor da plataforma.

    ## Rate Limit
    - Intervalo mínimo configurável por token (padrão: **30 segundos** entre requisições)
    - **5 violações** dentro de 10 minutos → token bloqueado automaticamente
    - Bloqueio revertido somente pelo gestor da plataforma
    - Respostas com status `429` incluem `retry_after` (segundos para aguardar)

    ## Fluxo recomendado de polling
    1. Chamar `GET /api/ext/events/polling` a cada N segundos (respeitando o intervalo mínimo)
    2. Para cada evento recebido, chamar `GET /api/ext/orders/{id}` para obter os detalhes
    3. Processar o pedido no sistema local
    4. Chamar `POST /api/ext/events/acknowledgment` com os IDs confirmados
    5. Eventos não confirmados continuam aparecendo no próximo polling

    ## Códigos de evento
    | Código | Significado |
    |--------|-------------|
    | `PLC`  | Novo pedido recebido |
    | `CFM`  | Pedido aceito pelo restaurante |
    | `DSP`  | Pedido despachado para entrega |
    | `RTP`  | Pedido pronto para retirada no balcão |
    | `CON`  | Pedido concluído/entregue |
    | `CAN`  | Pedido cancelado |

  version: "1.0.0"
  contact:
    name: Nomli — Suporte Técnico

servers:
  - url: /api/ext
    description: Produção

security:
  - bearerAuth: []

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

  schemas:
    Evento:
      type: object
      properties:
        id:
          type: integer
          example: 42
        code:
          type: string
          enum: [PLC, CFM, DSP, RTP, CON, CAN]
          example: PLC
        nr_orcamen:
          type: integer
          example: 88
        created_at:
          type: string
          example: "2026-06-18 14:32:00"

    Pedido:
      type: object
      properties:
        nr_orcamen:
          type: integer
        nm_statuss:
          type: string
          example: pendente
        tp_orcamen:
          type: string
          example: VEN
        fl_entbalc:
          type: string
          enum: [D, B]
          description: "D = Delivery, B = Balcão"
        nm_cliente:
          type: string
        ds_observa:
          type: string
        tp_esperar:
          type: integer
          description: Tempo estimado em minutos
        dt_inclusa:
          type: string
          format: date
        hr_inclusa:
          type: string
        totais:
          type: object
          properties:
            vr_produto: { type: number }
            vr_fretess: { type: number }
            vr_descont: { type: number }
            vr_liquido: { type: number }
        endereco:
          nullable: true
          type: object
          properties:
            logradouro: { type: string }
            numero:     { type: string }
            complemento:{ type: string }
            referencia: { type: string }
            bairro:     { type: string }
            cidade:     { type: string }
        pagamento:
          type: object
          properties:
            ds_formpag: { type: string }
            tp_formpag: { type: string }
            vr_parcela: { type: number }
        itens:
          type: array
          items:
            $ref: '#/components/schemas/ItemPedido'

    ItemPedido:
      type: object
      properties:
        nr_sequenc:
          type: integer
        cd_produto:
          type: integer
        cd_pdvext:
          type: string
          nullable: true
          description: Código do produto no sistema externo (PDV/ERP)
          example: "4521"
        ds_produto:
          type: string
        qt_produto:
          type: integer
        vr_produto:
          type: number
        vr_totprod:
          type: number
        vr_liquido:
          type: number
        ds_tamanho:
          type: string
          nullable: true
        ds_massass:
          type: string
          nullable: true
          description: Tipo de massa (pizza)
        vr_massass:
          type: number
          nullable: true
        ds_bordass:
          type: string
          nullable: true
          description: Tipo de borda (pizza)
        vr_bordass:
          type: number
          nullable: true
        ds_opcoess:
          type: string
          nullable: true
          description: Opções/personalizações selecionadas (blocos de personalização)
        ds_observa:
          type: string
          nullable: true

    MerchantStatus:
      type: object
      properties:
        ds_fantasi:
          type: string
        fl_entrega:
          type: boolean
        fl_balcoes:
          type: boolean

    HorarioDia:
      type: object
      properties:
        dia_semana:
          type: integer
          description: "0=Domingo, 1=Segunda, ..., 6=Sábado"
        ds_dia:
          type: string
          example: segunda
        aberto:
          type: boolean
        janelas:
          type: array
          items:
            type: object
            properties:
              abertura:
                type: string
                example: "11:00"
              fechamento:
                type: string
                example: "22:00"

    ErroRateLimit:
      type: object
      properties:
        message:
          type: string
          example: "Aguarde pelo menos 30 segundos entre requisições."
        retry_after:
          type: integer
          description: Segundos para aguardar antes de tentar novamente
          example: 22
        violation:
          type: integer
          description: Número da violação atual (bloqueio ocorre na 5ª)
          example: 2

paths:
  /events/polling:
    get:
      summary: Buscar eventos pendentes
      description: |
        Retorna até 20 eventos não confirmados para o consumidor autenticado.
        Eventos são específicos por empresa (token) e por consumidor.
        Chamar respeitando o intervalo mínimo configurado no token (padrão 30s).
      tags: [Eventos]
      responses:
        '200':
          description: Lista de eventos pendentes (pode ser vazia)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Evento'
              example:
                - id: 42
                  code: PLC
                  nr_orcamen: 88
                  created_at: "2026-06-18 14:32:00"
                - id: 43
                  code: CFM
                  nr_orcamen: 88
                  created_at: "2026-06-18 14:35:10"
        '401':
          description: Token inválido ou não informado
        '429':
          description: Rate limit excedido
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroRateLimit'

  /events/acknowledgment:
    post:
      summary: Confirmar eventos processados
      description: |
        Marca eventos como confirmados. Eventos confirmados não aparecem mais no próximo polling.
        Confirmar somente após processar com sucesso no sistema local.
      tags: [Eventos]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items:
                    type: integer
                  example: [42, 43]
      responses:
        '200':
          description: Quantidade de eventos confirmados
          content:
            application/json:
              schema:
                type: object
                properties:
                  acknowledged:
                    type: integer
                    example: 2
        '401':
          description: Token inválido
        '422':
          description: Campo ids ausente ou inválido
        '429':
          description: Rate limit excedido

  /orders/{id}:
    get:
      summary: Detalhes do pedido
      description: |
        Retorna o pedido completo com itens, personalizações, endereço e pagamento.
        O campo `cd_pdvext` em cada item contém o código do produto no sistema externo,
        configurado pelo restaurante no cadastro do produto (campo "Código PDV").
      tags: [Pedidos]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
          description: Número do pedido (nr_orcamen)
      responses:
        '200':
          description: Pedido completo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pedido'
        '401':
          description: Token inválido
        '404':
          description: Pedido não encontrado
        '429':
          description: Rate limit excedido

  /merchant/status:
    get:
      summary: Status de abertura do restaurante
      description: Retorna se o restaurante está aceitando pedidos de delivery e/ou balcão.
      tags: [Restaurante]
      responses:
        '200':
          description: Status atual
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MerchantStatus'
              example:
                ds_fantasi: "Pizzaria Boa Pedida"
                fl_entrega: true
                fl_balcoes: false

  /merchant/opening-hours:
    get:
      summary: Horários de funcionamento semanais
      description: |
        Retorna os horários configurados para cada dia da semana.
        Um dia pode ter múltiplas janelas (ex: almoço e jantar).
        Dias sem janelas = restaurante fechado naquele dia.
      tags: [Restaurante]
      responses:
        '200':
          description: Horários por dia da semana
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HorarioDia'
              example:
                - dia_semana: 0
                  ds_dia: domingo
                  aberto: false
                  janelas: []
                - dia_semana: 1
                  ds_dia: segunda
                  aberto: true
                  janelas:
                    - abertura: "11:00"
                      fechamento: "14:00"
                    - abertura: "18:00"
                      fechamento: "22:00"

  /merchant/open:
    post:
      summary: Forçar abertura do restaurante
      description: Abre o restaurante imediatamente para delivery, balcão ou ambos.
      tags: [Restaurante]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                tipo:
                  type: string
                  enum: [entrega, balcao, ambos]
                  default: ambos
      responses:
        '200':
          description: Loja aberta
          content:
            application/json:
              example:
                message: "Loja aberta com sucesso."
                tipo: ambos

  /merchant/close:
    post:
      summary: Forçar fechamento do restaurante
      description: Fecha o restaurante imediatamente para delivery, balcão ou ambos.
      tags: [Restaurante]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                tipo:
                  type: string
                  enum: [entrega, balcao, ambos]
                  default: ambos
      responses:
        '200':
          description: Loja fechada
          content:
            application/json:
              example:
                message: "Loja fechada com sucesso."
                tipo: ambos

tags:
  - name: Eventos
    description: Polling de eventos e confirmação (acknowledgment)
  - name: Pedidos
    description: Detalhes completos do pedido
  - name: Restaurante
    description: Status e horários de funcionamento
