openapi: 3.0.3
info:
  title: Customers (Clientes)
  version: v1.0.0
  description: "Endpoints de clientes (cadastro, busca, perfil, saúde e suporte). RPC over /adm/api/Customers/{acao}."
servers:
  - url: /adm/api

tags:
  - name: Customers
    description: "Clientes (geral)"
  - name: Customers - Admin
    description: "Ações administrativas"
  - name: Customers - Profile
    description: "Perfil e dados cadastrais"
  - name: Customers - Health
    description: "Perfil de saúde e medicamentos"
  - name: Customers - Support
    description: "Suporte (tickets)"
  - name: Customers - Media
    description: "Uploads (avatar/anexos)"

paths:
  /Customers/resolveByEmail:
    post:
      tags:
        - Customers - Profile
      summary: "Resolver/criar usuário por e-mail"
      description: "Cria o usuário se não existir e retorna user_id."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                nome:
                  type: string
                  description: "Nome opcional para display_name"
      responses:
        "200":
          description: "OK"
        "422":
          description: "email inválido"

  /Customers/search:
    post:
      tags:
        - Customers - Profile
      summary: "Busca rápida de usuários"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - q
              properties:
                q:
                  type: string
                  description: "Trecho de nome/email/login"
      responses:
        "200":
          description: "OK"

  /Customers/find:
    post:
      tags:
        - Customers - Profile
      summary: "Encontrar cliente por CPF ou e-mail (rígido)"
      description: "Prioriza CPF (11 dígitos). Se não vier CPF válido, tenta e-mail."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                cpf:
                  type: string
                  description: "CPF (com ou sem máscara)"
                email:
                  type: string
                  format: email
      responses:
        "200":
          description: "OK"

  /Customers/lookup:
    post:
      tags:
        - Customers - Profile
      summary: "Lookup flexível (cpf/email/login/q)"
      description: "Tenta cpf, e-mail, login e q parcial, retornando o registro mais provável."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                cpf:
                  type: string
                email:
                  type: string
                  format: email
                login:
                  type: string
                q:
                  type: string
                  description: "Busca parcial"
      responses:
        "200":
          description: "OK"

  /Customers/get:
    post:
      tags:
        - Customers - Profile
      summary: "Obter cliente por ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: integer
      responses:
        "200":
          description: "OK"
        "404":
          description: "Não encontrado"
        "422":
          description: "ID inválido"

  /Customers/infoSafe:
    post:
      tags:
        - Customers - Profile
      summary: "Obter cliente por ID (safe)"
      description: "Sempre retorna 200. Se não existir, retorna {exists:0}."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: integer
      responses:
        "200":
          description: "OK"

  /Customers/save_user_dados:
    post:
      tags:
        - Customers - Profile
      summary: "Salvar/atualizar user_dados (upsert)"
      description: "Atualiza dados cadastrais. Se logado usa o usuário logado; se não, aceita user_id explícito."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: integer
                  description: "Obrigatório quando não autenticado"

                nome:
                  type: string
                email:
                  type: string
                  format: email
                cpf:
                  type: string
                celular:
                  type: string
                telefone:
                  type: string

                data_nascimento:
                  type: string
                  format: date
                  example: "1990-05-20"
                sexo:
                  type: string
                  description: "M|F|O"
                  example: "M"
                nacionalidade:
                  type: string
                pais:
                  type: string
                nome_mae:
                  type: string
                profissao:
                  type: string
                facebook:
                  type: string
                instagram:
                  type: string

                cep:
                  type: string
                logradouro:
                  type: string
                numero:
                  type: string
                complemento:
                  type: string
                bairro:
                  type: string
                cidade:
                  type: string
                uf:
                  type: string
                  example: "SP"
                estado:
                  type: string
                  description: "Alias para uf"

                contato_emerg_nome:
                  type: string
                contato_emerg_parentesco:
                  type: string
                contato_emerg_telefone:
                  type: string
                contato_extra_nome:
                  type: string
                contato_extra_telefone:
                  type: string
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado (sem user_id)"
        "500":
          description: "Falha ao salvar"

  /Customers/admin_list:
    post:
      tags:
        - Customers - Admin
      summary: "Listagem administrativa de clientes (paginada)"
      description: "Lista com agregados (pedidos/receita) e assinatura (quando existir fin_orders/fin_subscriptions)."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                q:
                  type: string
                  description: "Busca por nome/email/cpf/cidade/telefone"
                order:
                  type: string
                  enum:
                    - recent
                    - name_asc
                    - orders_desc
                    - paid_desc
                  default: recent
                limit:
                  type: integer
                  default: 50
                  maximum: 200
                offset:
                  type: integer
                  default: 0
      responses:
        "200":
          description: "OK"

  /Customers/profile_full:
    post:
      tags:
        - Customers - Admin
      summary: "Perfil completo (WP + user_dados + saúde + medicamentos)"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - user_id
              properties:
                user_id:
                  type: integer
      responses:
        "200":
          description: "OK"
        "404":
          description: "Usuário não encontrado"
        "422":
          description: "user_id inválido"

  /Customers/upload_avatar:
    post:
      tags:
        - Customers - Media
      summary: "Upload de avatar"
      description: "Upload multipart (campo: arquivo). Salva em storage local e retorna URL."
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - arquivo
              properties:
                arquivo:
                  type: string
                  format: binary
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"
        "422":
          description: "Arquivo inválido"

  /Customers/health_get:
    post:
      tags:
        - Customers - Health
      summary: "Obter perfil de saúde (cliente logado)"
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"

  /Customers/health_save:
    post:
      tags:
        - Customers - Health
      summary: "Salvar perfil de saúde (parcial)"
      description: "Aceita campos parciais. Mantém valores já salvos para campos não enviados."
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                condicoes:
                  type: string
                alergias:
                  type: string
                cirurgias_internacoes:
                  type: string
                historico_familiar:
                  type: string
                habitos_vida:
                  type: string
                queixa_principal:
                  type: string
                historia_doenca_atual:
                  type: string
                objetivos_saude:
                  type: string
                observacoes:
                  type: string
                tipo_sanguineo:
                  type: string
                  description: "A+, A-, B+, B-, AB+, AB-, O+, O-, NI"
                plano_saude_plano_id:
                  type: string
                plano_saude_convenio_nome:
                  type: string
                plano_saude_plano_nome:
                  type: string
                plano_saude_numero:
                  type: string
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"

  /Customers/meds_list:
    post:
      tags:
        - Customers - Health
      summary: "Listar medicamentos (cliente logado)"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                em_uso:
                  type: integer
                  enum:
                    - 0
                    - 1
                  description: "Filtra em uso"
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"

  /Customers/meds_save:
    post:
      tags:
        - Customers - Health
      summary: "Criar/editar medicamento"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - nome
              properties:
                id:
                  type: integer
                  description: "Se enviar, faz update"
                nome:
                  type: string
                dosagem:
                  type: string
                frequencia:
                  type: string
                via:
                  type: string
                motivo:
                  type: string
                prescrito_por:
                  type: string
                desde:
                  type: string
                  format: date
                ate:
                  type: string
                  format: date
                em_uso:
                  type: integer
                  enum:
                    - 0
                    - 1
                observacoes:
                  type: string
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"
        "422":
          description: "Validação (nome obrigatório)"

  /Customers/meds_delete:
    post:
      tags:
        - Customers - Health
      summary: "Excluir medicamento"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: integer
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"
        "403":
          description: "Não pertence ao usuário"

  /Customers/support_list:
    post:
      tags:
        - Customers - Support
      summary: "Listar tickets do cliente logado"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                q:
                  type: string
                status:
                  type: string
                meio:
                  type: string
                tipo:
                  type: string
                date_from:
                  type: string
                  format: date
                date_to:
                  type: string
                  format: date
                top_only:
                  type: boolean
                  default: true
                limit:
                  type: integer
                  default: 20
                  maximum: 200
                offset:
                  type: integer
                  default: 0
                order:
                  type: string
                  enum:
                    - ASC
                    - DESC
                  default: DESC
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"

  /Customers/support_thread:
    post:
      tags:
        - Customers - Support
      summary: "Thread de ticket (cabeça + respostas)"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: integer
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"
        "403":
          description: "Acesso negado"

  /Customers/support_save:
    post:
      tags:
        - Customers - Support
      summary: "Criar/responder/fechar ticket"
      description: "Novo ticket (assunto+texto) OU responder (atendimento_pai+texto) OU alterar status (id+status)."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: integer
                  description: "Para alterar status do ticket (cabeça)"
                status:
                  type: string
                atendimento_pai:
                  type: integer
                  description: "Para responder em um ticket existente"
                assunto:
                  type: string
                texto:
                  type: string
                anexo:
                  type: string
                  description: "URL do arquivo/anexo"
                tipo:
                  type: string
                meio:
                  type: string
      responses:
        "200":
          description: "OK"
        "401":
          description: "Não autenticado"
        "422":
          description: "Validação (assunto/texto)"