# app/services/notificacao/cliente_whatsapp.rb
#
# Cliente da ponte Baileys (container `whatsapp`). Substitui o Twilio como canal
# padrão de WhatsApp.
#
# ⚠️ Canal NÃO OFICIAL: a ponte fala o protocolo do WhatsApp Web por engenharia
#    reversa. É gratuito e ilimitado, mas está fora dos Termos do WhatsApp e a
#    Meta pode banir o número. Por isso: chip dedicado, e o intervalo entre
#    mensagens é respeitado por quem chama (Notificacao::Despachante), não aqui.
#
# TIMEOUT em tudo: sem ele, uma ponte pendurada trava o worker Puma até o proxy
# cortar — o mesmo motivo que levou o ClienteTwilio a configurar timeout.
module Notificacao
  class ClienteWhatsapp
    TIMEOUT_ABERTURA = 5
    TIMEOUT_LEITURA  = 20

    Resposta = Struct.new(:ok, :dados, :erro, keyword_init: true) do
      def ok? = ok
    end

    def self.padrao
      config = ConfiguracaoNotificacao.instancia
      new(url: config.baileys_url_efetiva, token: config.baileys_token_efetivo)
    end

    def initialize(url:, token:)
      @url   = url.to_s.strip.chomp('/')
      @token = token.to_s
    end

    def configurado?
      @url.present? && @token.present?
    end

    # { conectado:, numero:, qr:, desde:, ultimo_erro: }
    def status
      get('/status')
    end

    def enviar(para:, texto:)
      post('/enviar', para: para, texto: texto)
    end

    # Grupos de que o número conectado participa. O id do grupo é opaco, então a
    # tela precisa desta lista para o ADM escolher pelo nome.
    def grupos
      get('/grupos')
    end

    def desconectar
      post('/logout')
    end

    private

    def get(caminho)
      requisitar(Net::HTTP::Get, caminho)
    end

    def post(caminho, **corpo)
      requisitar(Net::HTTP::Post, caminho, corpo)
    end

    def requisitar(classe, caminho, corpo = nil)
      return Resposta.new(ok: false, erro: 'Ponte do WhatsApp não configurada.') unless configurado?

      uri = URI.join("#{@url}/", caminho.delete_prefix('/'))
      req = classe.new(uri)
      req['Authorization'] = "Bearer #{@token}"
      req['Content-Type']  = 'application/json'
      req.body = corpo.to_json if corpo

      resposta = Net::HTTP.start(uri.hostname, uri.port,
                                 use_ssl: uri.scheme == 'https',
                                 open_timeout: TIMEOUT_ABERTURA,
                                 read_timeout: TIMEOUT_LEITURA) { |http| http.request(req) }

      dados = parse(resposta.body)
      if resposta.is_a?(Net::HTTPSuccess)
        Resposta.new(ok: true, dados: dados)
      else
        Resposta.new(ok: false, dados: dados, erro: mensagem_de_erro(resposta, dados))
      end
    rescue StandardError => e
      # A ponte fora do ar não pode virar 500 na tela nem quebrar um fechamento.
      Rails.logger.error("[ClienteWhatsapp] #{e.class}: #{e.message}")
      Resposta.new(ok: false, erro: "#{e.class}: #{e.message}")
    end

    def parse(corpo)
      JSON.parse(corpo.to_s)
    rescue JSON::ParserError
      {}
    end

    def mensagem_de_erro(resposta, dados)
      return 'Token da ponte do WhatsApp inválido.' if resposta.code == '401'
      return 'WhatsApp desconectado — leia o QR code na tela.' if resposta.code == '503'
      # 404 = a ponte respondeu, mas não conhece a rota: a imagem do container
      # `whatsapp` é anterior a ela. "rota desconhecida" cru na tela não diz o
      # que fazer; o conserto é rebuildar a ponte, não mexer no Rails.
      if resposta.code == '404'
        return 'Ponte do WhatsApp desatualizada (não conhece esta rota) — refaça o build do container `whatsapp`.'
      end

      dados['erro'].presence || "HTTP #{resposta.code}"
    end
  end
end
